Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo descreve como acelerar as inicializações quentes para aplicativos Windows de instância única. Saiba como registrar uma instância de processo em execução para ser notificado diretamente quando ocorrer uma inicialização, evitando sobrecarga associada ao fluxo de inicialização completo. Essa abordagem reduz a latência geral, eliminando o processo intermediário que unifica com instâncias já em execução.
Fluxo típico hoje:
Com um lançamento acelerado:
Esta funcionalidade de lançamento acelerado não abrange todos os tipos de lançamento, de todas as aplicações de origem, em todas as versões do Windows. Como resultado, seu aplicativo ainda deve manter sua própria implementação de instância única (consulte Instanciação de aplicativos com a API de ciclo de vida do aplicativo para obter uma abordagem).
O restante deste artigo descreve a criação de um aplicativo WinUI 3 C++ simples que oferece suporte ao Menu Iniciar quente acelerado e a inicializações de protocolo.
Importante
As APIs de lançamento acelerado fazem parte de uma funcionalidade de acesso limitado (ver
Crie um novo project
No Visual Studio, crie um novo projeto. Neste exemplo, no diálogo Criar um novo project, defina o filtro de linguagem para C++ e o tipo de project para WinUI, depois selecione WinUI Blank App (Packaged). Se não encontrar este modelo, certifica-te de que tens a carga de trabalho desenvolvimento de aplicações WinUI instalada no Visual Studio Installer.
Implementação de instância única
Você pode usar qualquer técnica de sua escolha para implementar a instância única em seu aplicativo. Neste exemplo, utilize as APIs de Ciclo de Vida de Aplicação do 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");
}
Adicionar uma associação de protocolo para o aplicativo
Para receber iniciações de protocolo aceleradas, seu aplicativo deve ser o destino padrão de um registro de protocolo. Neste exemplo, use um registo em pacote. Para obter mais informações sobre como se registrar como o manipulador padrão para um esquema de URI, consulte Manipular ativação de URI.
No Package.appxmanifest, adicione uma entrada para uma associação de protocolo.
<Applications>
<Application Id="App" … />
…
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="acceleratedlaunchsample"/>
</uap:Extension>
</Extensions>
</Application>
</Applications>
Agora, seu aplicativo pode ser iniciado por meio de seu bloco no Menu Iniciar ou digitando acceleratedlaunchsample: na caixa de diálogo Executar. Em ambos os casos, um processo intermediário é iniciado e redireciona para a instância já em execução.
Registe-se para lançamentos acelerados
Agora, o aplicativo está pronto para aceitar as otimizações.
As mensagens de inicialização acelerada são entregues como mensagens de janela para um HWND dentro do seu processo. Você diz ao sistema qual janela deve receber as mensagens e quais mensagens deseja usar, com a interface IExperimentalAPIInvoker que é implementada pela CLSID_ExperimentalAPIInvoker.
IExperimentalAPIInvoker e CLSID_ExperimentalAPIInvoker definem-se do seguinte modo:
[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 Permite que você chame métodos pelo nome com argumentos empacotados em um repositório de propriedades nomeado. Os métodos para o lançamento acelerado são:
Método: "RegisterAcceleratedUriLaunch"
Registre-se para lançamentos de protocolo entregues como uma cadeia de caracteres UTF-16 terminada nula (PCWSTR) via WM_COPYDATA para o HWND de destino.
Você pode chamar esse método apenas uma vez por IExperimentalAPIInvoker instância de objeto. Depois de se registrar, não libere o objeto até depois de cancelar o registro.
| Nome do parâmetro | Tipo | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
O HWND, convertido para um LONGLONG assinado, que recebe uma mensagem de janela quando o aplicativo é iniciado para o seu tile principal no Menu Iniciar ou noutras localizações. |
"copyDataFormatId" |
VT_UI4 |
Valor que é passado como COPYDATASTRUCT.dwData ao fornecer a string URI. |
"schemes" |
VT_LPWSTR \| VT_VECTOR |
Lista de esquemas de URI para os quais se registrar. Somente aqueles para os quais o processo de chamada é o manipulador padrão são acelerados. |
Método: "UnregisterAcceleratedUriLaunch"
Cancele o registro de iniciações de URI. Sem argumentos. Deve ser chamado sobre o mesmo objeto com o qual foi previamente registado.
Método: "RegisterAcceleratedTileLaunch"
Registe-se para lançamentos de blocos entregues por uma mensagem de janela selecionada. Você pode chamar esse método apenas uma vez para cada IExperimentalAPIInvoker instância de objeto. Depois de se registrar, não libere o objeto até cancelar o registro.
| Nome do parâmetro | Tipo | Description |
|---|---|---|
"targetWindow" |
VT_I8 |
O HWND, convertido para um LONGLONG assinado, que recebe uma mensagem de janela quando o aplicativo é iniciado para o seu tile principal no Menu Iniciar ou noutras localizações. |
"messageId" |
VT_UI4 |
O ID da mensagem da janela que vai para a janela de destino especificada. |
Método: "UnregisterAcceleratedTileLaunch"
Cancele o registro de lançamentos de blocos. Este método não aceita argumentos. Você deve chamá-lo no mesmo objeto com o qual você se registrou anteriormente.
Juntando tudo
Para simplificar os passos anteriores, especialmente para aplicações que não têm acesso fácil a um procedimento de janela existente, este exemplo fornece um auxiliar independente que gere uma janela de trabalho e a regista para lançamentos acelerados. Você deve criar esse objeto em um thread com uma bomba de mensagem ativa (como o thread da interface do usuário de um aplicativo WinUI).
Primeiro, copie o seguinte código para um ficheiro de cabeçalho no seu 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;
};
Agora você pode integrar esse código ao seu aplicativo:
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);
}
Com esse código, seu aplicativo se beneficia de inicializações aceleradas em versões do sistema operacional que suportam esse recurso.
Conteúdo relacionado
Instanciação de aplicativos com a API de ciclo de vida do aplicativo
APIs do ciclo de vida da aplicação Windows App SDK
Windows developer