Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk azt ismerteti, hogyan gyorsíthatja fel az egypéldányos Windows-alkalmazások melegindításait. Megtudhatja, hogyan regisztrálhat egy futó folyamatpéldányt, amely közvetlenül értesítést kap az indításkor, elkerülve a teljes indítási folyamathoz kapcsolódó többletterhelést. Ez a megközelítés csökkenti az általános késést azáltal, hogy megszünteti a már futó példányokkal egyesített köztes folyamatot.
Tipikus folyamat ma:
Gyorsított indítással:
Ez a gyorsított indítási funkció nem fedi le a Windows minden verziójának minden forrásalkalmazásból származó indítási funkcióját. Ennek eredményeképpen az alkalmazásnak továbbra is fenn kell tartania az egyedi példányosítás saját implementációját (lásd alkalmazáspéldányosítás az alkalmazás életciklus API-val a megközelítések egyikeként).
A cikk további része egy egyszerű C++ WinUI 3-alkalmazás létrehozását ismerteti, amely támogatja a gyorsított, meleg Start menüt és protokollindításokat.
Fontos
A gyorsított indítási API-k egy korlátozott Access funkció részét képezik (lásd: LimitedAccessFeatures osztály). További információkért vagy feloldási jogkivonat kéréséhez használja a LAF Access Token Request Form.
Új project létrehozása
Hozzon létre egy új projektet a Visual Studio-ban. Ebben a példában a A project párbeszédpanelen állítsa a nyelvi szűrőt C++ értékre, a project típust WinUI értékre, majd válassza a WinUI Blank App (Csomagolt) lehetőséget. Ha nem látja ezt a sablont, győződjön meg arról, hogy telepítve van a WinUI alkalmazásfejlesztés számítási feladat a Visual Studio Telepítőben.
Egyszeri megvalósításának implementálása
Tetszőleges technikával implementálhatja az egyszeri instancingot az alkalmazásban. Ebben a példában használja a Windows App SDK alkalmazás-életciklus API-jait.
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");
}
Protokolltársítás hozzáadása az alkalmazáshoz
A gyorsított protokollindítások fogadásához az alkalmazásnak kell lennie a protokollregisztráció alapértelmezett céljának. Ebben a mintában használjon csomagolt regisztrációt. Az URI-sémák alapértelmezett kezelőjeként való regisztrációról további információt az URI aktiválásának kezelése című témakörben talál.
Package.appxmanifestAdjon hozzá egy bejegyzést egy protokolltársításhoz:
<Applications>
<Application Id="App" … />
…
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="acceleratedlaunchsample"/>
</uap:Extension>
</Extensions>
</Application>
</Applications>
Az alkalmazás most már elindítható a Start menü csempéjén keresztül vagy úgy, hogy beírja acceleratedlaunchsample: a Futtatás párbeszédpanelen. Mindkét esetben elindul egy közvetítő folyamat, amely átirányítja a már futó példányhoz.
Gyorsított indítások regisztrálása
Az alkalmazás most már készen áll az optimalizálásra.
A gyorsított indítási üzenetek ablaküzenetként érkeznek a HWND-hez a folyamaton belül. Meg kell adnia a rendszernek, hogy melyik ablak fogadja az üzeneteket, és mely üzeneteket szeretné használni, a felülettel IExperimentalAPIInvoker , amelyet a rendszer implementál CLSID_ExperimentalAPIInvoker.
IExperimentalAPIInvoker és CLSID_ExperimentalAPIInvoker a következőképpen vannak definiálva:
[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 lehetővé teszi a metódusok név szerinti meghívását egy névvel ellátott tulajdonságtárolóba csomagolt argumentumokkal. A gyorsított indítás módszerei a következők:
Módszer: "RegisterAcceleratedUriLaunch"
Regisztráljon protokollindításokra, amelyeket null-végű UTF-16 sztringként (PCWSTR) kézbesítenek a cél HWND-hez, WM_COPYDATA segítségével.
Ezt a metódust csak egyszer hívhatja meg az IExperimentalAPIInvoker objektumpéldányonként. A regisztráció után ne engedje fel az objektumot, amíg meg nem szünteti a regisztrációt.
| Paraméter neve | Típus | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
A HWND, egy aláírt LONGLONG típusra alakítva, ablaküzenetet kap, amikor az alkalmazás elindul a kezdőlapra helyezett elsődleges csempéjén vagy más helyeken. |
"copyDataFormatId" |
VT_UI4 |
Az URI-sztring kézbesítésekor a COPYDATASTRUCT.dwData értékként átadott érték. |
"schemes" |
VT_LPWSTR \| VT_VECTOR |
A regisztrálandó URI-sémák listája. Csak azok kerülnek felgyorsításra, amelyekhez a hívási folyamat az alapértelmezett kezelő. |
Metódus: "UnregisterAcceleratedUriLaunch"
Elindul az URI regisztrációjának törlése. Nincsenek argumentumok. Azt az objektumot kell meghívni, amelyre korábban regisztrált.
Metódus: "RegisterAcceleratedTileLaunch"
Regisztráljon csempeindításokra, amelyeket egy kiválasztott ablaküzenet közvetít. Ezt a metódust minden objektumpéldány IExperimentalAPIInvoker esetében csak egyszer hívhatja meg. A regisztráció után ne engedje fel az objektumot, amíg meg nem szünteti a regisztrációt.
| Paraméter neve | Típus | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
A HWND, egy aláírt LONGLONG típusra alakítva, ablaküzenetet kap, amikor az alkalmazás elindul a kezdőlapra helyezett elsődleges csempéjén vagy más helyeken. |
"messageId" |
VT_UI4 |
Az ablak üzenet ID-je, amely a megadott célablakba megy. |
Módszer: "UnregisterAcceleratedTileLaunch"
A csempeindítások regisztrációjának törlése. Ez a metódus nem vesz fel argumentumokat. Ugyanazt az objektumot kell meghívnia, amellyel korábban regisztrált.
Összeállítás
Az előző lépések egyszerűsítése érdekében, különösen az olyan alkalmazások esetében, amelyek nem rendelkeznek egyszerűen elérhetőséggel egy meglévő ablakeljáráshoz, ez a minta egy önálló segédprogramot biztosít, amely kezeli a munkafelületet, és regisztrálja a gyorsított indításhoz. Ezt az objektumot egy aktív üzenetszivattyúval rendelkező szálon kell létrehoznia (például egy WinUI-alkalmazás felhasználói felületi szálával).
Elsőként másolja a következő kódot egy fejlécfájlba a projektedben.
#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;
};
Most már integrálhatja ezt a kódot az alkalmazásba:
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);
}
Ezzel a kóddal az alkalmazás kihasználhatja a funkciót támogató operációsrendszer-verziók gyorsított elindítását.
Kapcsolódó tartalom
Alkalmazás példányosítás az alkalmazás életciklusának API-jával
Windows developer