使用應用程式生命週期 API 進行豐富啟用

在 Windows 應用程式 SDK 中,應用程式生命週期 API 會將 UWP 樣式的豐富啟用行為支援帶到所有應用程式、封裝和解除封裝。 第一個版本著重於將最常用的啟用類型帶入解壓縮的應用程式,而未來的版本旨在支援更多 UWP 的 44 種啟用類型

支援豐富啟用需要兩個步驟:

  • 告知系統您的應用程式支援一或多個豐富的啟用類型。
  • 接收並處理應用程式啟動時所收到的豐富啟用承載。

必要條件

若要在 Windows 應用程式 SDK 中使用應用程式生命週期 API:

  1. 下載並安裝最新版的 Windows 應用程式 SDK。 如需詳細資訊,請參閱安裝 Windows 應用程式 SDK 的工具。
  2. 請依照指示建立您的第一個 WinUI 3 專案,或使用現有專案中的 Windows 應用程式 SDK。

未封裝應用程式的啟用詳細數據

目前版本的 Windows 應用程式 SDK 支援四種最常見的啟動類型,以解除封裝的應用程式。 這些啟用類型是由 ExtendedActivationKind 列舉所定義。

啟用種類 描述
Launch 當使用者按兩下應用程式的圖示,或透過 ShellExecuteCreateProcess 以程式設計方式按下應用程式時,從命令行啟動應用程式。
File 透過 ShellExecute、Launcher.LaunchFileAsync 或命令行開啟類型檔案時,啟動已註冊檔類型的應用程式。
Protocol 當該通訊協定的字串透過 ShellExecuteLauncher.LaunchUriAsync或命令行執行時,啟動已註冊通訊協議的應用程式。
StartupTask 當使用者登入 Windows 時啟動應用程式,可能是因為登錄機碼,或是因為已知啟動資料夾中的快捷方式。

每種類型的未封裝應用程式都會以不同的方式擷取其命令行自變數。 例如,C++ Win32 應用程式預期會收到以字串形式傳入 WinMain 的啟用自變數(雖然它們也有呼叫 GetCommandLineW 的選項)。 不過, Windows Forms 應用程式必須 呼叫 Environment.GetCommandLineArgs,因為自變數不會自動傳遞至它們。

已封裝應用程式的啟用詳細數據

使用 Windows 應用程式 SDK 的已封裝應用程式支援所有 44 種 UWP 的啟用類型。 每個啟用類型都有自己的 IActivatedEventArgs 實作,其中包含與該特定啟用類型相關的屬性。

封裝的應用程式一律會在其AppInstance.Activated事件處理程式中接收啟用事件自變數,而且也可以選擇呼叫AppInstance.GetActivatedEventArgs

啟用註冊

所有應用程式預設都支援 Launch 啟用種類。 不同於 UWP,Windows 應用程式 SDK Launch 啟用種類包含命令行啟動。 應用程式可以透過數種方式註冊其他啟用類型。

  • 使用 Windows 應用程式 SDK 的未封裝應用程式可以透過 Windows 應用程式 SDK 中的應用程式生命週期 API,註冊或取消註冊其他啟用類型。
  • 未封裝的應用程式可以使用撰寫登錄機碼的傳統方法,繼續註冊其他啟用類型。
  • 已封裝的應用程式可以透過應用程式指令清單中的專案註冊其他啟用類型。

啟用註冊是每位使用者。 如果您的應用程式已針對多個使用者安裝,您必須為每個使用者重新註冊啟用。

範例

註冊豐富啟用

雖然應用程式可以隨時呼叫註冊 API,但最常見的案例是在應用程式啟動時檢查註冊。

此範例示範解除封裝的應用程式如何使用 ActivationRegistrationManager 類別的下列靜態方法,在啟動應用程式時註冊數種啟用類型:

這個範例也會示範如何使用 MddBootstrapInitializeMddBootstrapShutdown 函式來初始化和清除 Windows 應用程式 SDK 架構套件的參考。 所有未封裝的應用程式都必須執行此動作,才能使用 Windows 應用程式 SDK 所提供的 API。 如需詳細資訊,請參閱針對封裝外部位置或未封裝的應用程式使用 Windows 應用程式 SDK 運行時間。

注意

此範例會一次註冊三個影像檔類型的關聯。 這很方便,但結果與個別註冊每個檔類型相同:註冊新的映像類型不會覆寫先前的註冊。 不過,如果應用程式以一組不同的動詞重新註冊已註冊的檔類型,則會覆寫該檔類型的先前一組動詞。

const UINT32 majorMinorVersion{ WINDOWSAPPSDK_RELEASE_MAJORMINOR };
PCWSTR versionTag{ WINDOWSAPPSDK_RELEASE_VERSION_TAG_W };
const PACKAGE_VERSION minVersion{ WINDOWSAPPSDK_RUNTIME_VERSION_UINT64 };
WCHAR szExePath[MAX_PATH]{};
WCHAR szExePathAndIconIndex[MAX_PATH + 8]{};

int APIENTRY wWinMain(
    _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Initialize Windows App SDK framework package for unpackaged apps.
    HRESULT hr{ MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion) };
    if (FAILED(hr))
    {
        wprintf(L"Error 0x%X in MddBootstrapInitialize(0x%08X, %s, %hu.%hu.%hu.%hu)\n",
            hr, majorMinorVersion, versionTag, minVersion.Major, 
            minVersion.Minor, minVersion.Build, minVersion.Revision);
        return hr;
    }

    // Get the current executable filesystem path, so we can
    // use it later in registering for activation kinds.
    GetModuleFileName(NULL, szExePath, MAX_PATH);
    wcscpy_s(szExePathAndIconIndex, szExePath);
    wcscat_s(szExePathAndIconIndex, L",1");

    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_CLASSNAME, szWindowClass, MAX_LOADSTRING);
    RegisterWindowClass(hInstance);
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Uninitialize Windows App SDK.
    MddBootstrapShutdown();
    return (int)msg.wParam;
}

void RegisterForActivation()
{
    OutputMessage(L"Registering for rich activation");

    // Register one or more supported filetypes, specifying 
    // an icon (specified by binary file path plus resource index),
    // a display name to use in Shell and Settings,
    // zero or more verbs for the File Explorer context menu,
    // and the path to the EXE to register for activation.
    hstring myFileTypes[3] = { L".foo", L".foo2", L".foo3" };
    hstring verbs[2] = { L"view", L"edit" };
    ActivationRegistrationManager::RegisterForFileTypeActivation(
        myFileTypes,
        szExePathAndIconIndex,
        L"Contoso File Types",
        verbs,
        szExePath
    );

    // Register a URI scheme for protocol activation,
    // specifying the scheme name, icon, display name and EXE path.
    ActivationRegistrationManager::RegisterForProtocolActivation(
        L"foo",
        szExePathAndIconIndex,
        L"Contoso Foo Protocol",
        szExePath
    );

    // Register for startup activation.
    // As we're registering for startup activation multiple times,
    // and this is a multi-instance app, we'll get multiple instances
    // activated at startup.
    ActivationRegistrationManager::RegisterForStartupActivation(
        L"ContosoStartupId",
        szExePath
    );

    // If we don't specify the EXE, it will default to this EXE.
    ActivationRegistrationManager::RegisterForStartupActivation(
        L"ContosoStartupId2",
        L""
    );
}

取得豐富的啟用事件自變數

啟動之後,應用程式必須擷取其啟用事件自變數。 在此範例中,未封裝的應用程式會呼叫 AppInstance.GetActivatedEventArgs 方法來取得啟用事件的事件自變數,然後使用 AppActivationArguments.Kind 屬性擷取不同類型的啟用事件自變數。

注意

Win32 應用程式通常會非常早地取得其 WinMain 方法的命令行自變數。 同樣地,這些應用程式應該在先前使用提供的 lpCmdLine 參數或呼叫 GetCommandLineW的相同位置呼叫 AppInstance.GetActivatedEventArgs

void GetActivationInfo()
{
    AppActivationArguments args = AppInstance::GetCurrent().GetActivatedEventArgs();
    ExtendedActivationKind kind = args.Kind();
    if (kind == ExtendedActivationKind::Launch)
    {
        ILaunchActivatedEventArgs launchArgs = 
            args.Data().as<ILaunchActivatedEventArgs>();
        if (launchArgs != NULL)
        {
            winrt::hstring argString = launchArgs.Arguments().c_str();
            std::vector<std::wstring> argStrings = split_strings(argString);
            OutputMessage(L"Launch activation");
            for (std::wstring s : argStrings)
            {
                OutputMessage(s.c_str());
            }
        }
    }
    else if (kind == ExtendedActivationKind::File)
    {
        IFileActivatedEventArgs fileArgs = 
            args.Data().as<IFileActivatedEventArgs>();
        if (fileArgs != NULL)
        {
            IStorageItem file = fileArgs.Files().GetAt(0);
            OutputFormattedMessage(
                L"File activation: %s", file.Name().c_str());
        }
    }
    else if (kind == ExtendedActivationKind::Protocol)
    {
        IProtocolActivatedEventArgs protocolArgs = 
            args.Data().as<IProtocolActivatedEventArgs>();
        if (protocolArgs != NULL)
        {
            Uri uri = protocolArgs.Uri();
            OutputFormattedMessage(
                L"Protocol activation: %s", uri.RawUri().c_str());
        }
    }
    else if (kind == ExtendedActivationKind::StartupTask)
    {
        IStartupTaskActivatedEventArgs startupArgs = 
            args.Data().as<IStartupTaskActivatedEventArgs>();
        if (startupArgs != NULL)
        {
            OutputFormattedMessage(
                L"Startup activation: %s", startupArgs.TaskId().c_str());
        }
    }
}

Unregister

此範例示範如何使用 ActivationRegistrationManager 類別的下列靜態方法,動態取消註冊特定啟用類型的未封裝應用程式:

注意

取消註冊啟動啟用時,應用程式必須使用原本註冊時所使用的相同 taskId。

void UnregisterForActivation()
{
    OutputMessage(L"Unregistering for rich activation");
    
    // Unregister one or more registered filetypes.
    try
    {
        hstring myFileTypes[3] = { L".foo", L".foo2", L".foo3" };
        ActivationRegistrationManager::UnregisterForFileTypeActivation(
            myFileTypes,
            szExePath
        );
    }
    catch (...)
    {
        OutputMessage(L"Error unregistering file types");
    }

    // Unregister a protocol scheme.
    ActivationRegistrationManager::UnregisterForProtocolActivation(
        L"foo",
        L"");

    // Unregister for startup activation.
    ActivationRegistrationManager::UnregisterForStartupActivation(
        L"ContosoStartupId");
    ActivationRegistrationManager::UnregisterForStartupActivation(
        L"ContosoStartupId2");
}