アプリ ライフサイクル API によるリッチ アクティベーション

Windows App SDK でアプリ ライフサイクル API を使用すると、すべてのアプリで (パッケージ アプリとパッケージ化されていないアプリで同様に)、UWP スタイルのリッチ アクティベーション動作がサポートされるようになります。 この最初のリリースでは、最も一般的に使用されるアクティブ化の種類をパッケージ化されていないアプリに導入することに重点を置いており、将来のリリースでは、UWP の44 のアクティブ化の種類の中からさらに多くの種類をサポートすることを目指しています。

リッチ アクティベーションをサポートするには、次の 2 つの手順が必要です。

  • アプリで 1 つ以上のリッチ アクティベーションの種類がサポートされることをシステムに伝える。
  • アクティベーション時にアプリで受け取られるリッチ アクティベーション ペイロードを受信して処理する。

前提条件

Windows App SDK でアプリ ライフサイクル API を使用するには、次のようにします。

  1. Windows App SDK の最新リリースをダウンロードしてインストールします。 詳細については、「Windows App SDK 用ツールをインストールする」を参照してください。
  2. 手順に従って、最初の WinUI 3 プロジェクトを作成するか、既存のプロジェクトで Windows App SDK を使用します

パッケージ化されていないアプリのアクティブ化の詳細

Windows App SDK の現行バージョンでは、パッケージ化されていないアプリに対して、4 つの最も一般的なアクティブ化の種類がサポートされています。 これらのアクティブ化の種類は、ExtendedActivationKind 列挙型で定義されます。

アクティブ化の種類 説明
Launch ユーザーがアプリのアイコンをダブルクリックしたときにコマンドラインから、または ShellExecute または CreateProcess を使用してプログラムで、アプリをアクティブ化します。
File ShellExecuteLauncher.LaunchFileAsync、またはコマンド ラインを介して種類のファイルが開かれたときに、そのファイルの種類に登録されているアプリをアクティブ化します。
Protocol ShellExecuteLauncher.LaunchUriAsync、またはコマンド ラインを介してプロトコルの文字列が実行されたときに、そのプロトコルに登録されているアプリをアクティブ化します。
StartupTask レジストリ キー、または既知のスタートアップ フォルダー内のショートカットにより、ユーザーが Windows にログインしたときに、アプリをアクティブ化します。

コマンド ライン引数の取得方法は、パッケージ化されていないアプリの種類ごとに異なります。 たとえば、C++ Win32 アプリでは、受け取られるアクティブ引数が文字列の形式で WinMain に渡される必要があります (ただし、GetCommandLineWを呼び出すオプションもあります)。 一方、Windows フォーム アプリでは、引数が Environment.GetCommandLineArgs に自動的に渡されないため、これを呼び出す "必要" があります。

パッケージ アプリのアクティブ化の詳細

Windows App SDK を使用するパッケージ アプリでは、UWP の 44 のアクティブ化の種類すべてがサポートされます。 各アクティブ化の種類には、それに対応する固有の IActivatedEventArgs の実装があります。これには、特定の種類のアクティブ化に関連するプロパティが含まれています。

パッケージ アプリは常に、AppInstance.Activated イベント ハンドラーでアクティブ化イベント引数を受け取ります。また、AppInstance.GetActivatedEventArgs を呼び出すオプションもあります。

アクティブ化の登録

すべてのアプリでは、既定で、アクティブ化の種類 Launch がサポートされます。 UWP とは異なり、Windows App SDK のアクティブ化の種類 Launch には、コマンド ラインからの起動が含まれます。 アプリで追加のアクティブ化の種類を登録するには、次の方法があります。

  • Windows App SDK を使用するパッケージ化されていないアプリでは、Windows App SDK のアプリ ライフサイクル API を使用して、追加のアクティブ化の種類を登録 (および登録解除) できます。
  • パッケージ化されていないアプリでは、引き続きレジストリ キーを作成するという従来の方法を使用して、追加のアクティブ化の種類を登録することができます。
  • パッケージ アプリでは、アプリケーション マニフェストのエントリを使用して追加のアクティブ化の種類を登録できます。

アクティブ化の登録は、ユーザーごとに行います。 アプリが複数のユーザーにインストールされている場合は、ユーザーごとにアクティブ化を再登録する必要があります。

リッチ アクティベーションを登録する

アプリではいつでも登録 API を呼び出すことができますが、アプリの起動時に登録を確認するのが最も一般的なシナリオです。

この例は、パッケージ化されていないアプリで、以下に示す ActivationRegistrationManager クラスの静的メソッドを使用して、アプリの起動時に次のアクティブ化の種類を登録する方法を示しています。

この例では、MddBootstrapInitialize および MddBootstrapShutdown 関数を使用して、Windows App SDK フレームワーク パッケージへの参照を初期化およびクリーンアップする方法も示しています。 すべてのパッケージ化されていないアプリでは、Windows App SDK で提供される API を使用するために、これを行う必要があります。 詳細については、「外部の場所でパッケージ化されたアプリまたはパッケージ化されていないアプリの Windows App SDK ランタイムを使用する」を参照してください。

注意

この例では、一度に 3 種類のイメージ ファイルとの関連付けを登録します。 これは便利ですが、結果は、各ファイルの種類を個別に登録する場合と同じです。新しい登録によって、以前の登録が上書きされることはありません。 ただし、アプリで、既に登録されているファイルの種類を別の動詞セットで再登録すると、そのファイルの種類の以前の動詞セットは上書きされます。

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");
}