Registrujte svou aplikaci pro urychlené spuštění

Tento článek popisuje, jak zrychlit teplé spouštění pro jedno instance aplikací pro Windows. Zjistěte, jak zaregistrovat spuštěnou instanci procesu, která má být přímo upozorněna při spuštění, a vyhnout se režii přidružené k úplnému průběhu spuštění. Tento přístup snižuje celkovou latenci tím, že eliminuje zprostředkující proces, který je sjednocený s již spuštěnými instancemi.

Typický tok dnes:

Snímek obrazovky vývojového diagramu znázorňujícího cestu normálního spuštění s meziprocesem.

S akcelerovaným spuštěním:

Snímek obrazovky diagramu toku znázorňující zrychlenou cestu pro spuštění systému bez zprostředkujícího procesu.

Tato akcelerovaná funkce spouštění nepokrývá každý druh spuštění, od každé zdrojové aplikace, ve všech verzích Windows. V důsledku toho musí vaše aplikace stále udržovat vlastní implementaci jednoho instancengu (viz Instanceng aplikací s rozhraním API životního cyklu aplikace pro jeden přístup).

Zbytek tohoto článku vás provede vytvořením jednoduché aplikace C++ WinUI 3, která podporuje akcelerované rychlé spuštění nabídky Start a spuštění protokolu.

Důležité

Rozhraní API pro zrychlené spuštění jsou součástí jedné funkce s omezeným přístupem (viz třída LimitedAccessFeatures). Další informace nebo vyžádání tokenu odemknutí najdete v formuláři žádosti o token Access LAF.

Vytvoření nového project

V Visual Studio vytvořte nový project. V tomto příkladu v dialogovém okně Vytvoření nového dialogového okna project nastavte filtr jazyka na C++ a typ project na WinUI a pak vyberte WinUI Blank App (Packaged). Pokud tuto šablonu nevidíte, ujistěte se, že máte v instalačním programu Visual Studio nainstalovaný vývoj aplikacíWinUI.

Implementace jedné instance

K implementaci jediné instance v aplikaci můžete použít libovolnou techniku. V tomto příkladu použijte rozhraní API životního cyklu aplikace Windows App SDK.

void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
    AppInstance mainInstance = AppInstance::FindOrRegisterForKey(L"SingleInstance");
    if (mainInstance.IsCurrent())
    {
        // We're the main instance. Register to receive redirected launches.
        EventHandler<AppActivationArguments> handler(this, &App::OnRedirectedActivation);
        g_activatedRevoker = mainInstance.Activated(winrt::auto_revoke, handler);
        // Proceed with creating the main window.
        window = make<MainWindow>();
        window.Activate();

        window.SetMessage(L"Launched normally");
    }
    else
    {
        // We are a secondary instance. Redirect to the main instance and terminate when we're done.
        wil::unique_event redirectionComplete;
        redirectionComplete.create();

        auto redirectionOperation = mainInstance.RedirectActivationToAsync(mainInstance.GetActivatedEventArgs());
        redirectionOperation.Completed([&redirectionComplete](auto&&, auto&&)
        {
            redirectionComplete.SetEvent();
        });

        DWORD indexIgnored{};
        CoWaitForMultipleObjects(CWMO_DEFAULT, INFINITE, 1, redirectionComplete.addressof(), &indexIgnored);
        App::Exit();
    }
}

winrt::fire_and_forget App::OnRedirectedActivation(const IInspectable&, const AppActivationArguments& args)
{
    co_await wil::resume_foreground(window.DispatcherQueue());
    ::SwitchToThisWindow(GetWindowFromWindowId(window.AppWindow().Id()), TRUE);
    
    window.SetMessage(L"Launched via redirection");
}

Přidejte přidružení protokolu pro aplikaci

Pokud chcete přijímat akcelerované spuštění protokolu, musí být vaše aplikace výchozím cílem registrace protokolu. V této ukázce použijte předem připravenou registraci. Další informace o tom, jak se zaregistrovat jako výchozí zpracovatel pro URI schéma, naleznete v tématu Zpracování aktivace identifikátoru URI.

Do pole Package.appxmanifestpřidejte položku pro přidružení protokolu:

<Applications>
  <Application Id="App" … />
    …
    <Extensions>
      <uap:Extension Category="windows.protocol">
        <uap:Protocol Name="acceleratedlaunchsample"/>
      </uap:Extension>
    </Extensions>
  </Application>
</Applications>

Aplikaci teď můžete spustit prostřednictvím její dlaždice v nabídce Start nebo zadáním acceleratedlaunchsample: do dialogového okna Spustit. V obou případech se spustí zprostředkující proces a přesměruje na již běžící instanci.

Registrace pro akcelerované spuštění

Teď je aplikace připravená k vyjádření souhlasu s optimalizacemi.

Zprávy o urychleném spuštění jsou doručovány jako oknové zprávy do HWND v rámci vašeho procesu. Systému řeknete, které okno má přijímat zprávy a které zprávy chcete použít, s rozhraním IExperimentalAPIInvoker , které je implementováno CLSID_ExperimentalAPIInvoker.

IExperimentalAPIInvoker a CLSID_ExperimentalAPIInvoker jsou definovány takto:

[uuid("6fb3e48c-ba77-4f80-b9c9-77635a08a7f8")]
interface DECLSPEC_NOVTABLE IExperimentalAPIInvoker : public IUnknown
{
    IFACEMETHOD(IsSupported)(PCWSTR methodName, BOOL* isSupported) = 0;
    IFACEMETHOD(Invoke)(PCWSTR methodName, INamedPropertyStore* args, BOOL* isSupported) = 0;
};

DEFINE_GUID(CLSID_ExperimentalAPIInvoker, 0x81AF2611, 0xE262, 0x4090, 0xA1, 0x5B, 0xC0, 0x88, 0x95, 0xB1, 0xA0, 0xF0);

IExperimentalAPIInvoker umožňuje volat metody podle názvu s argumenty zabalenými v pojmenovaném úložišti vlastností. Metody zrychleného spuštění jsou:

Metoda: RegisterAcceleratedUriLaunch

Zaregistrujte se pro spuštění protokolů doručovaných jako řetězec UTF-16 ukončený znakem null (PCWSTR) prostřednictvím WM_COPYDATA na cílové okno HWND.

Tuto metodu lze volat pouze jednou na IExperimentalAPIInvoker instanci objektu. Jakmile se zaregistrujete, neuvoľňujte objekt, dokud nezrušíte registraci.

Název parametru Typ Description
"targetWindow" VT_I8 Přetypovaný HWND na podepsaný LONGLONG, který přijímá zprávu okna, když je aplikace spuštěna pro svou primární dlaždici v nabídce Start nebo na jiných místech.
"copyDataFormatId" VT_UI4 Hodnota, která se předává jako COPYDATASTRUCT.dwData při předávání řetězce identifikátoru URI.
"schemes" VT_LPWSTR \| VT_VECTOR Seznam schémat identifikátorů URI, pro která se mají zaregistrovat. Urychleny jsou pouze ty, pro které je volající proces výchozí obslužnou rutinou.

Metoda: UnregisterAcceleratedUriLaunch

Zrušení registrace pro spuštění prostřednictvím URI Žádné argumenty. Musí být volána na stejném objektu, na kterém byla dříve zaregistrována.

Metoda: "RegisterAcceleratedTileLaunch"

Zaregistrujte se ke spuštění dlaždic doručované zprávou vybraného okna. Tuto metodu můžete volat pouze jednou pro každou IExperimentalAPIInvoker instanci objektu. Po registraci objekt neuvolňujte, dokud jej nezrušíte.

Název parametru Typ Description
"targetWindow" VT_I8 Přetypovaný HWND na podepsaný LONGLONG, který přijímá zprávu okna, když je aplikace spuštěna pro svou primární dlaždici v nabídce Start nebo na jiných místech.
"messageId" VT_UI4 ID zprávy okna, které přejde do zadaného cílového okna.

Metoda: „UnregisterAcceleratedTileLaunch“

Odregistrujte se od spouštění dlaždic. Tato metoda nepřijímá žádné argumenty. Musíte použít stejný objekt, který jste dříve registrovali.

Dávání dohromady

Pro zjednodušení předchozích kroků, zejména pro aplikace, které nemají jednoduchý přístup k existujícímu postupu okna, tato ukázka poskytuje nezávislého pomocníka, který spravuje pracovní okno a zaregistruje je pro urychlené spuštění. Tento objekt musíte vytvořit ve vlákně s aktivním pumpou zpráv (například vlákno uživatelského rozhraní aplikace WinUI).

Nejprve zkopírujte následující kód do hlavičkového souboru v project:

#pragma once
#include <propsys.h>
#include <propvarutil.h>
#include <initguid.h>
#include <vector>
#include <functional>
#include <wil/result.h>
#include <wil/resource.h>

[uuid("6fb3e48c-ba77-4f80-b9c9-77635a08a7f8")]
interface DECLSPEC_NOVTABLE IExperimentalAPIInvoker : public IUnknown
{
    IFACEMETHOD(IsSupported)(PCWSTR methodName, BOOL* isSupported) = 0;
    IFACEMETHOD(Invoke)(PCWSTR methodName, INamedPropertyStore* args, BOOL* isSupported) = 0;
};

DEFINE_GUID(CLSID_ExperimentalAPIInvoker, 0x81AF2611, 0xE262, 0x4090, 0xA1, 0x5B, 0xC0, 0x88, 0x95, 0xB1, 0xA0, 0xF0);

class AcceleratedLaunchHelper
{
public:
    ~AcceleratedLaunchHelper()
    {
        if (m_hwnd != nullptr)
        {
            FAIL_FAST_IF(::GetWindowThreadProcessId(m_hwnd, nullptr) != ::GetCurrentThreadId());

            if (m_tileLaunchCallback)
            {
                BOOL isSupported;
                m_invoker->Invoke(L"UnregisterAcceleratedTileLaunch", nullptr, &isSupported);
            }

            if (m_uriLaunchCallback)
            {
                BOOL isSupported;
                m_invoker->Invoke(L"UnregisterAcceleratedUriLaunch", nullptr, &isSupported);
            }

            ::DestroyWindow(m_hwnd);
        }
    }

    inline bool TryRegisterForUris(
        const std::vector<PCWSTR>& uris, std::function<void(PCWSTR)> callback)
    {
        EnsureWorkerWindow();

        if (!m_invoker)
        {
            return false;
        }

        winrt::com_ptr<INamedPropertyStore> args;
        THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&args)));

        wil::unique_prop_variant targetWindowParam;
        THROW_IF_FAILED(InitPropVariantFromInt64(reinterpret_cast<LONGLONG>(m_hwnd), targetWindowParam.reset_and_addressof()));
        args->SetNamedValue(L"targetWindow", targetWindowParam);

        wil::unique_prop_variant copyDataFormatIdParam;
        THROW_IF_FAILED(InitPropVariantFromUInt32(0,
            copyDataFormatIdParam.reset_and_addressof()));
        args->SetNamedValue(L"copyDataFormatId", copyDataFormatIdParam);

        wil::unique_prop_variant schemesParam;
        THROW_IF_FAILED(InitPropVariantFromStringVector(
            const_cast<PCWSTR*>(uris.data()), uris.size(), schemesParam.reset_and_addressof()));
        THROW_IF_FAILED(args->SetNamedValue(L"schemes", schemesParam));

        BOOL isSupported{ FALSE };
        THROW_IF_FAILED(m_invoker->Invoke(L"RegisterAcceleratedUriLaunch", args.get(),
            &isSupported));

        if (isSupported)
        {
            m_uriLaunchCallback = callback;
            m_uriSchemes = uris;
        }

        return isSupported;
    }

    inline bool TryRegisterForTileLaunch(std::function<void()> callback)
    {
        EnsureWorkerWindow();

        if (!m_invoker)
        {
            return false;
        }

        winrt::com_ptr<INamedPropertyStore> args;
        THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&args)));

        wil::unique_prop_variant targetWindowParam;
        THROW_IF_FAILED(InitPropVariantFromInt64(reinterpret_cast<LONGLONG>(m_hwnd), targetWindowParam.reset_and_addressof()));
        args->SetNamedValue(L"targetWindow", targetWindowParam);

        wil::unique_prop_variant tileLaunchMessageIdParam;
        THROW_IF_FAILED(InitPropVariantFromUInt32(WM_USER,
            tileLaunchMessageIdParam.reset_and_addressof()));
        THROW_IF_FAILED(args->SetNamedValue(L"messageId", tileLaunchMessageIdParam));

        BOOL isSupported{ FALSE };
        THROW_IF_FAILED(m_invoker->Invoke(L"RegisterAcceleratedTileLaunch", args.get(),
            &isSupported));
    
        if (isSupported)
        {
            m_tileLaunchCallback = callback;
        }

        return isSupported;
    }

private:
    static inline LRESULT s_WorkerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_NCCREATE:
        {
            auto pThis = reinterpret_cast<AcceleratedLaunchHelper*>(
                (reinterpret_cast<CREATESTRUCT*>(lParam))->lpCreateParams);
            if (pThis)
            {
                ::SetWindowLongPtr(hwnd, 0, reinterpret_cast<LONG_PTR>(pThis));
            }
            break;
        }
        }

        auto pThis = reinterpret_cast<AcceleratedLaunchHelper*>(GetWindowLongPtr(hwnd, 0));
        if (pThis)
        {
            return pThis->WorkerWndProc(hwnd, msg, wParam, lParam);
        }

        return 0;
    }

    inline LRESULT WorkerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_COPYDATA:
        {
            const auto copyData = reinterpret_cast<COPYDATASTRUCT*>(lParam);
            if (copyData->dwData == 0)
            {
                const auto uri = reinterpret_cast<PCWSTR>(copyData->lpData);
                if (m_uriLaunchCallback)
                {
                    m_uriLaunchCallback(uri);
                }
            }
        }
        break;

        case WM_USER:
            if (m_tileLaunchCallback)
            {
                m_tileLaunchCallback();
            }
            break;
        }
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    inline void EnsureWorkerWindow()
    {
        if (m_hwnd != nullptr)
        {
            return;
        }

        // Best-effort: Try to acquire the IExperimentalAPIInvoker.
        // If we can't, the OS does not currently support the feature.
        if (FAILED(CoCreateInstance(CLSID_ExperimentalAPIInvoker, nullptr, CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(&m_invoker)))
        {
            return;
        }

        WNDCLASSEX wcex{ sizeof(wcex) };
        wcex.lpszClassName = L"AcceleratedLaunchWorkerClass";
        wcex.cbWndExtra = sizeof(this);
        wcex.lpfnWndProc = s_WorkerWndProc;
        wcex.hInstance = GetModuleHandle(nullptr);
        ::RegisterClassEx(&wcex);

        m_hwnd = ::CreateWindowEx(0, wcex.lpszClassName, nullptr, 0, 0, 0, 0, 0,
            HWND_MESSAGE, 0, wcex.hInstance, this);

        FAIL_FAST_LAST_ERROR_IF_NULL(m_hwnd);
    }

    // Worker window for receiving notifications
    HWND m_hwnd{ nullptr };

    // Entry point for registration & unregistration
    winrt::com_ptr<IExperimentalAPIInvoker> m_invoker;

    // Callbacks
    std::function<void()> m_tileLaunchCallback;
    std::function<void(PCWSTR)> m_uriLaunchCallback;

    // Registered URI schemes
    std::vector<PCWSTR> m_uriSchemes;
};

Teď můžete tento kód integrovat do své aplikace:

void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
    …
    if (mainInstance.IsCurrent())
    {
        …

        m_acceleratedLaunchHelper = std::make_unique<AcceleratedLaunchHelper>();
        m_acceleratedLaunchHelper->TryRegisterForTileLaunch(
            [this]() { OnAcceleratedTileLaunch(); });

        std::vector<PCWSTR> schemes = { L"acceleratedlaunchsample" };
        m_acceleratedLaunchHelper->TryRegisterForUris(schemes,
            [this](PCWSTR uri) { OnAcceleratedUriLaunch(uri); });
    }
    …
}

void App::OnAcceleratedTileLaunch()
{
    ::SwitchToThisWindow(GetWindowFromWindowId(window.AppWindow().Id()), TRUE);

    window.SetMessage(L"Launched via accelerated tile launch.");
}

void App::OnAcceleratedUriLaunch(PCWSTR uri)
{
    ::SwitchToThisWindow(GetWindowFromWindowId(window.AppWindow().Id()), TRUE);

    std::wstring message = L"Launched via accelerated URI launch: ";
    message += uri;
    window.SetMessage(message);
}

S tímto kódem vaše aplikace přináší výhody akcelerovaných spuštění ve verzích operačního systému, které tuto funkci podporují.

Zpracování aktivace identifikátoru URI

Vytváření instancí aplikací pomocí rozhraní API životního cyklu aplikace

rozhraní API životního cyklu aplikací Windows App SDK