Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln beskriver hur du påskyndar snabba uppstarter för Windows-applikationer med en enda instans. Lär dig hur du registrerar en processinstans som körs för att meddelas direkt när en lansering sker, vilket undviker omkostnader som är associerade med det fullständiga startflödet. Den här metoden minskar den totala svarstiden genom att eliminera den mellanliggande process som förenar med instanser som redan körs.
Typiskt flöde idag:
Med en accelererad start:
Den här accelererade startfunktionen omfattar inte alla typer av lanseringar, från varje källprogram, på varje version av Windows. Därför måste ditt program fortfarande upprätthålla sin egen implementering av enkelinstansiering (se Appinstansiering med appens livscykel-API för ett tillvägagångssätt).
Resten av den här artikeln går igenom hur du skapar en enkel C++ WinUI 3-app som stöder accelererade varma startmenyer och protokolllanseringar.
Viktigt!
API:erna för accelererad start är en del av en funktion för begränsad Access (se klassen LimitedAccessFeatures). Om du vill ha mer information eller begära en upplåsningstoken använder du LAF Access formulär för tokenbegäran.
Skapa ett nytt projekt
Skapa en ny project i Visual Studio. I det här exemplet i dialogrutan Skapa en ny project anger du språkfiltret till C++ och project typ till WinUI och väljer sedan WinUI Blank App (Packaged). Om du inte ser den här mallen kontrollerar du att arbetsbelastningen WinUI-programutveckling är installerad i Visual Studio Installer.
Implementera enkel instansiering
Du kan använda valfri teknik för att implementera singelinstansiering i din app. I det här exemplet använder du Windows App SDK:s App Lifecycle API:er.
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");
}
Lägga till en protokollassociation för appen
För att få accelererade protokollstarter måste din app vara standardmålet för en protokollregistrering. I det här exemplet använder du en paketerad registrering. Mer information om hur du registrerar dig som standardhanterare för ett URI-schema finns i Hantera URI-aktivering.
Lägg till en post för en protokollassociation i din Package.appxmanifest.
<Applications>
<Application Id="App" … />
…
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="acceleratedlaunchsample"/>
</uap:Extension>
</Extensions>
</Application>
</Applications>
Din app kan nu startas via dess panel på Start-menyn eller genom att acceleratedlaunchsample: skriva in i dialogrutan Kör. I båda fallen är det en mellanhandsprocess som startar och omdirigerar till den instans som redan körs.
Registrera dig för accelererade lanseringar
Nu är appen redo att välja optimeringar.
De accelererade startmeddelandena levereras som fönstermeddelanden till en HWND i din process. Du talar om för systemet vilket fönster som ska ta emot meddelandena och vilka meddelanden du vill använda, med gränssnittet IExperimentalAPIInvoker som implementeras av CLSID_ExperimentalAPIInvoker.
IExperimentalAPIInvoker och CLSID_ExperimentalAPIInvoker definieras på följande sätt:
[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 gör det möjligt för dig att anropa metoder efter namn med argument paketerade i en namngiven egenskapslagring. Metoderna för accelererad start är:
Metod: "RegisterAcceleratedUriLaunch"
Registrera dig för protokolluppskjutningar som levereras som en null-avslutad UTF-16-sträng (PCWSTR) via WM_COPYDATA till mål-HWND.
Du kan bara anropa den här metoden en gång per IExperimentalAPIInvoker objektinstans. När du har registrerat dig släpper du inte objektet förrän du har avregistrerat dig.
| Parameternamn | Typ | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
HWND, som skickas till en signerad LONGLONG, som tar emot ett fönstermeddelande när appen startas för sin primära panel på Start-menyn eller på andra platser. |
"copyDataFormatId" |
VT_UI4 |
Värde som skickas som COPYDATASTRUCT.dwData när du levererar URI-strängen. |
"schemes" |
VT_LPWSTR \| VT_VECTOR |
Lista över URI-scheman att registrera sig för. Endast de processer som har anropsprocessen som standardhanterare accelereras. |
Metod: "UnregisterAcceleratedUriLaunch"
Avregistrera från URI-lanseringar. Inga argument. Måste anropas på samma objekt som tidigare registrerats med.
Metod: "RegisterAcceleratedTileLaunch"
Registrera dig för panellanseringar som levereras av ett valt fönstermeddelande. Du kan bara anropa den här metoden en gång för varje IExperimentalAPIInvoker objektinstans. När du har registrerat dig släpper du inte objektet förrän du avregistrerar det.
| Parameternamn | Typ | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
HWND, som skickas till en signerad LONGLONG, som tar emot ett fönstermeddelande när appen startas för sin primära panel på Start-menyn eller på andra platser. |
"messageId" |
VT_UI4 |
Meddelande-ID:t för fönstret som skickas till det specificerade målfönstret. |
Metod: "UnregisterAcceleratedTileLaunch"
Avregistrera från panelstarter. Den här metoden tar inga argument. Du måste anropa det på samma objekt som du tidigare har registrerat dig med.
Sammanfoga
För att förenkla föregående steg, särskilt för appar som inte har lätt access till en befintlig fönsterprocedur, tillhandahåller det här exemplet en fristående hjälp som hanterar ett arbetsfönster och registrerar det för accelererade uppskjutningar. Du måste skapa det här objektet i en tråd med en aktiv meddelandepump (till exempel användargränssnittstråden i en WinUI-app).
Kopiera först följande kod till en rubrikfil i din 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;
};
Nu kan du integrera den här koden i din app:
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);
}
Med den här koden drar din app nytta av accelererade lanseringar på os-versioner som stöder den här funktionen.
Relaterat innehåll
Användning av instanser i appar tillsammans med applikationens livscykel-API
Windows developer