Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule opisano sposób przyspieszenia uruchamiania jednowystąpieniowych aplikacji Windows. Dowiedz się, jak zarejestrować działające wystąpienie procesu, aby otrzymywać bezpośrednie powiadomienia o uruchomieniu, unikając obciążeń związanych z pełnym przepływem uruchamiania. Takie podejście zmniejsza ogólne opóźnienie, eliminując proces pośredniczący, który łączy się z już uruchomionymi wystąpieniami.
Typowy przepływ dzisiaj:
Z przyspieszonym startem:
Ta przyspieszona funkcja uruchamiania nie obejmuje każdego rodzaju uruchamiania z każdej aplikacji źródłowej w każdej wersji systemu Windows. W związku z tym aplikacja musi nadal utrzymywać własną implementację pojedynczego instancjonowania (zobacz Instancjonowanie aplikacji przy użyciu interfejsu API cyklu życia aplikacji dla jednego podejścia).
W pozostałej części tego artykułu opisano tworzenie prostej aplikacji WinUI 3 w języku C++, która obsługuje przyspieszone uruchomienia menu Start oraz protokołu.
Ważne
Przyspieszone interfejsy API uruchamiania są częścią funkcji Ograniczonego Dostępu (zobacz LimitedAccessFeatures). Aby uzyskać więcej informacji lub zażądać tokenu odblokowania, użyj formularza żądania tokenu LAF Access.
Utwórz nowy projekt
W Visual Studio utwórz nowy project. W tym przykładzie w oknie dialogowym Utwórz nowy projekt, ustaw filtr języka na C++ i typ projektu na WinUI, a następnie wybierz pozycję WinUI Pusta aplikacja (spakowana). Jeśli nie widzisz tego szablonu, upewnij się, że masz obciążenie WinUI application development zainstalowane w Instalatorze Visual Studio.
Uruchamianie jako pojedyncza instancja
Możesz użyć dowolnej techniki, aby zaimplementować pojedyncze instancjonowanie w swojej aplikacji. W tym przykładzie użyj interfejsów API zarządzania cyklem życia aplikacji 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");
}
Dodaj skojarzenie protokołu dla aplikacji
Aby otrzymywać przyspieszone uruchamianie protokołu, aplikacja musi być domyślnym celem rejestracji tego protokołu. W tym przykładzie użyj rejestracji spakowanej. Aby uzyskać więcej informacji na temat rejestrowania jako domyślnego obsługującego schemat URI, zobacz Obsługa aktywacji identyfikatora URI.
W pliku Package.appxmanifestdodaj wpis dla skojarzenia protokołu:
<Applications>
<Application Id="App" … />
…
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="acceleratedlaunchsample"/>
</uap:Extension>
</Extensions>
</Application>
</Applications>
Aplikację można teraz uruchomić za pomocą kafelka w menu Start lub wpisując acceleratedlaunchsample: w oknie dialogowym Uruchamianie. W obu przypadkach uruchamiany jest proces pośredniczący, który przekierowuje do już działającego wystąpienia.
Zarejestruj się na przyspieszone uruchomienia
Teraz aplikacja jest gotowa do wyrażenia zgody na optymalizacje.
Przyspieszone komunikaty uruchamiania są dostarczane jako wiadomości okienkowe do HWND wewnątrz procesu. Informujesz system, które okno powinno odbierać komunikaty oraz komunikaty, których chcesz użyć, za pomocą interfejsu IExperimentalAPIInvoker zaimplementowanego przez CLSID_ExperimentalAPIInvokerprogram .
IExperimentalAPIInvoker i CLSID_ExperimentalAPIInvoker są zdefiniowane w następujący sposób:
[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żliwia wywoływanie metod według nazwy z argumentami spakowanymi w nazwanym składzie właściwości. Metody przyspieszonego uruchamiania to:
Metoda: "RegisterAcceleratedUriLaunch"
Zarejestruj uruchomienie protokołu przekazywane jako ciąg UTF-16 zakończony znakiem null (PCWSTR) przez WM_COPYDATA do docelowego HWND.
Tę metodę można wywołać tylko raz na IExperimentalAPIInvoker wystąpienie obiektu. Po zarejestrowaniu nie zwalniaj obiektu, dopóki go nie wyrejestrujesz.
| Nazwa parametru | Typ | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
HWND, rzutowany na podpisany LONGLONG, który otrzymuje komunikat okna po uruchomieniu aplikacji dla jego podstawowego kafelka w menu Start lub innych lokalizacjach. |
"copyDataFormatId" |
VT_UI4 |
Wartość przekazana jako COPYDATASTRUCT.dwData podczas dostarczania ciągu identyfikatora URI. |
"schemes" |
VT_LPWSTR \| VT_VECTOR |
Lista schematów URI do zarejestrowania. Tylko te, których domyślnym procesem obsługi jest proces wywołujący, są przyspieszane. |
Metoda: „UnregisterAcceleratedUriLaunch”
Wyrejestrowywanie z uruchomień identyfikatora URI. Brak argumentów. Musi być wywoływany na tym samym obiekcie, który został wcześniej zarejestrowany.
Metoda: "RegisterAcceleratedTileLaunch"
Zarejestruj się, aby otrzymywać informacje o uruchomieniach kafelków za pomocą wybranego komunikatu okna. Tę metodę można wywołać tylko raz dla każdego IExperimentalAPIInvoker wystąpienia obiektu. Po zarejestrowaniu nie zwalniaj obiektu, dopóki go nie wyrejestrujesz.
| Nazwa parametru | Typ | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
HWND, rzutowany na podpisany LONGLONG, który otrzymuje komunikat okna po uruchomieniu aplikacji dla jego podstawowego kafelka w menu Start lub innych lokalizacjach. |
"messageId" |
VT_UI4 |
Identyfikator komunikatu wiadomości okna, który jest wysyłany do określonego okna docelowego. |
Metoda: "UnregisterAcceleratedTileLaunch"
Wyrejestrowanie funkcji uruchamiania kafelka. Ta metoda nie przyjmuje żadnych argumentów. Należy wywołać go na tym samym obiekcie, na którym wcześniej zarejestrowano.
Składanie całości
Aby uprościć powyższe kroki, zwłaszcza w przypadku aplikacji, które nie mają łatwego dostępu do istniejącej procedury okna, ten przykład udostępnia pomocniczą funkcję, która zarządza oknem roboczym i rejestruje je w celu przyspieszonego uruchamiania. Ten obiekt należy utworzyć w wątku z aktywną pompą komunikatów (taką jak wątek interfejsu użytkownika aplikacji WinUI).
Najpierw skopiuj następujący kod do pliku nagłówka w Twoim projekcie:
#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;
};
Teraz możesz zintegrować ten kod z aplikacją:
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);
}
Dzięki temu kodowi aplikacja korzysta z przyspieszonych uruchomień w wersjach systemu operacyjnego, które obsługują tę funkcję.
Treści powiązane
Instancjonowanie aplikacji za pomocą interfejsu API cyklu życia aplikacji
interfejsy API cyklu życia aplikacji Windows App SDK