最新の UWP コンポーネントでデスクトップ アプリを拡張する

一部の Windows エクスペリエンス (タッチ対応 UI ページなど) は、AppContainer 内で実行する必要があります。 こうしたエクスペリエンスを追加するには、UWP プロジェクトと Windows ランタイム コンポーネントを使ってデスクトップ アプリを拡張します。

多くの場合、デスクトップ アプリケーションから Windows ランタイム API を直接呼び出すことができます。そのため、このガイドを確認する前に、Windows のための強化に関する記事をご覧ください。

Note

このトピックで説明する機能では、アプリをパッケージ化する必要があります (実行時にパッケージ ID を持ちます)。 それには、パッケージ化されたアプリ (パッケージ化された WinUI 3 デスクトップ アプリの新しいプロジェクトの作成に関する記事を参照) と、外部の場所でパッケージ化されたアプリが含まれます (「外部の場所でパッケージ化してパッケージ ID を付与する」を参照)。 また、「パッケージ ID が必要な機能」も参照してください。

まず、ソリューションをセットアップします。

UWP プロジェクトとランタイム コンポーネントを 1 つ以上ソリューションに追加します。

Windows アプリケーション パッケージ プロジェクトとデスクトップ アプリケーションへの参照が含まれるソリューションから始めます。

次の画像は、ソリューションの例を示しています。

Extend start project

ソリューションにパッケージ プロジェクトが含まれていない場合は、Visual Studio を使ったデスクトップ アプリケーションのパッケージ化に関する記事を参照してください。

デスクトップ アプリケーションを構成する

デスクトップ アプリケーションに Windows ランタイム API を呼び出すために必要なファイルへの参照があることを確認します。

これを行うには、「デスクトップ アプリで Windows ランタイム API を呼び出す」を参照してください。

UWP プロジェクトを追加する

ソリューションに [空白のアプリ (ユニバーサル Windows)] を追加します。

ここでは、最新の XAML UI をビルドするか、UWP プロセス内でのみ実行される API を使います。

Add new project

パッケージ プロジェクトで、 [アプリケーション] ノードを右クリックして [参照の追加] をクリックします。

Add reference

次に、UWP プロジェクトに参照を追加します。

Select UWP project

ソリューションは次のようになります。

Solution with UWP project

(省略可能) Windows ランタイム コンポーネントを追加する

いくつかのシナリオを実現するには、Windows ランタイム コンポーネントにコードを追加する必要があります。

runtime component app service

次に、UWP プロジェクトからランタイム コンポーネントに参照を追加します。 ソリューションは次のようになります。

Runtime Component Reference

ソリューションをビルドする

ソリューションをビルドして、エラーが発生していないことを確認します。 エラーが発生した場合は、構成マネージャーを開き、プロジェクトが確実に同じプラットフォームを対象とするようにします。

Config manager

UWP プロジェクトとランタイム コンポーネントで行うことができる操作をいくつか見てみましょう。

最新の XAML UI を表示する

アプリケーション フローの一環として、最新の XAML ベースのユーザー インターフェイスをデスクトップ アプリケーションに組み込むことができます。 これらのユーザー インターフェイスは、さまざまな画面サイズと解像度に適応し、タッチや手描きなどの最新の対話モデルをサポートする性質を備えています。

たとえば、少量の XAML マークアップを使用して、地図関連の強力な視覚化機能をユーザーに提供できます。

次の画像に、マップ コントロールを含む XAML ベースの最新の UI を表示している Windows フォーム アプリケーションを示しています。

adaptive-design

注意

この例では、UWP プロジェクトをソリューションに追加して、XAML UI を表示しています。 これは、デスクトップ アプリケーションで XAML UI を表示するためにサポートされている安定した方法です。 この方法の代わりに、XAML Island を使用して UWP XAML コントロールをデスクトップ アプリケーションに直接追加することもできます。 XAML Islands は現在、開発者プレビューとして使用できます。 ご自身のプロトタイプ コードでこれらを試すことはお勧めしますが、現時点では運用コードで使用することはお勧めしません。 これらの API とコントロールは、引き続き今後の Windows リリースで完成度が高められ、安定したものになる予定です。 XAML Islands の詳細については、デスクトップ アプリケーションでの UWP コントロール に関する記事を参照してください

設計パターン

XAML ベースの UI を表示するには、以下の手順を実行します。

1️⃣ ソリューションを設定する

2️⃣ XAML UI を作成する

3️⃣ プロトコル拡張機能を UWP プロジェクトに追加する

4️⃣ デスクトップ アプリから UWP アプリを起動する

5️⃣ UWP プロジェクトで目的のページを表示する

ソリューションを設定する

ソリューションのセットアップ方法に関する一般的なガイダンスについては、このガイドの冒頭の「まず、ソリューションをセットアップする」セクションを参照してください。

ソリューションは次のようになります。

XAML UI Solution

この例では、Windows フォーム プロジェクトは Landmarks という名前で、XAML UI を含む UWP プロジェクトは MapUI という名前です。

XAML UI の作成

XAML UI を UWP プロジェクトに追加します。 基本的なマップの XAML を次に示します。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="12,20,12,14">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <maps:MapControl x:Name="myMap" Grid.Column="0" Width="500" Height="500"
                     ZoomLevel="{Binding ElementName=zoomSlider,Path=Value, Mode=TwoWay}"
                     Heading="{Binding ElementName=headingSlider,Path=Value, Mode=TwoWay}"
                     DesiredPitch="{Binding ElementName=desiredPitchSlider,Path=Value, Mode=TwoWay}"
                     HorizontalAlignment="Left"
                     MapServiceToken="<Your Key Goes Here" />
    <Grid Grid.Column="1" Margin="12">
        <StackPanel>
            <Slider Minimum="1" Maximum="20" Header="ZoomLevel" Name="zoomSlider" Value="17.5"/>
            <Slider Minimum="0" Maximum="360" Header="Heading" Name="headingSlider" Value="0"/>
            <Slider Minimum="0" Maximum="64" Header=" DesiredPitch" Name="desiredPitchSlider" Value="32"/>
        </StackPanel>
    </Grid>
</Grid>

プロトコル拡張機能を追加する

ソリューション エクスプローラーで、ソリューション内にパッケージ プロジェクトの package.appxmanifest ファイルを開き、この拡張機能を追加します。

<Extensions>
  <uap:Extension Category="windows.protocol" Executable="MapUI.exe" EntryPoint="MapUI.App">
    <uap:Protocol Name="xamluidemo" />
  </uap:Extension>
</Extensions>

プロトコルに名前を付けて、UWP プロジェクトによって生成された実行可能ファイルの名前と、エントリ ポイント クラスの名前を指定します。

デザイナーで package.appxmanifest 開き、 [宣言] タブを選んで、そこで拡張機能を追加することもできます。

declarations-tab

注意

マップ コントロールはインターネットからデータをダウンロードします。そのため、マップ コントロールを使用する場合は、"インターネット クライアント" 機能もマニフェストに追加する必要があります。

UWP アプリを起動する

まず、デスクトップ アプリケーションから、プロトコル名と UWP アプリに渡すパラメーターが含まれた URI を作成します。 次に、LaunchUriAsync メソッドを呼び出します。


private void Statue_Of_Liberty_Click(object sender, EventArgs e)
{
    ShowMap(40.689247, -74.044502);
}

private async void ShowMap(double lat, double lon)
{
    string str = "xamluidemo://";

    Uri uri = new Uri(str + "location?lat=" +
        lat.ToString() + "&?lon=" + lon.ToString());

    var success = await Windows.System.Launcher.LaunchUriAsync(uri);

}

パラメーターを解析してページを表示する

UWP プロジェクトの App クラスで、OnActivated イベント ハンドラーをオーバーライドします。 アプリがプロトコルによってアクティブ化されている場合は、パラメーターを解析して目的のページを開きます。

protected override void OnActivated(Windows.ApplicationModel.Activation.IActivatedEventArgs e)
{
    if (e.Kind == ActivationKind.Protocol)
    {
        ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)e;
        Uri uri = protocolArgs.Uri;
        if (uri.Scheme == "xamluidemo")
        {
            Frame rootFrame = new Frame();
            Window.Current.Content = rootFrame;
            rootFrame.Navigate(typeof(MainPage), uri.Query);
            Window.Current.Activate();
        }
    }
}

XAML ページの背後にあるコードで、ページに渡されたパラメーターを使用するように OnNavigatedTo メソッドをオーバーライドします。 この場合、このページに渡された緯度と経度を使用してマップに場所を表示します。

protected override void OnNavigatedTo(NavigationEventArgs e)
 {
     if (e.Parameter != null)
     {
         WwwFormUrlDecoder decoder = new WwwFormUrlDecoder(e.Parameter.ToString());

         double lat = Convert.ToDouble(decoder[0].Value);
         double lon = Convert.ToDouble(decoder[1].Value);

         BasicGeoposition pos = new BasicGeoposition();

         pos.Latitude = lat;
         pos.Longitude = lon;

         myMap.Center = new Geopoint(pos);

         myMap.Style = MapStyle.Aerial3D;

     }

     base.OnNavigatedTo(e);
 }

デスクトップ アプリケーションを共有ターゲットにする

デスクトップ アプリケーションを共有ターゲットにすることで、共有をサポートしている他のアプリのデータ (画像など) をユーザーが簡単に共有できるようになります。

たとえば、ユーザーは、Microsoft Edge やフォト アプリから画像を共有するためにアプリケーションを選択できます。 その機能を備えた WPF サンプル アプリケーションを次に示します。

share target

完全なサンプルについては、こちらを参照してください

設計パターン

アプリケーションを共有ターゲットにするには、以下の手順を実行します。

1️⃣ 共有ターゲットの拡張機能を追加する

2️⃣ OnShareTargetActivated イベント ハンドラーをオーバーライドする

3️⃣ UWP プロジェクトにデスクトップ拡張機能を追加する

4️⃣ 完全信頼のプロセス拡張機能を追加する

5️⃣ 共有ファイルを取得するようにデスクトップ アプリケーションを変更する

次の手順に従います

共有ターゲットの拡張機能を追加する

ソリューション エクスプローラーで、ソリューション内にパッケージ プロジェクトの package.appxmanifest ファイルを開き、共有ターゲットの拡張機能を追加します。

<Extensions>
      <uap:Extension
          Category="windows.shareTarget"
          Executable="ShareTarget.exe"
          EntryPoint="App">
        <uap:ShareTarget>
          <uap:SupportedFileTypes>
            <uap:SupportsAnyFileType />
          </uap:SupportedFileTypes>
          <uap:DataFormat>Bitmap</uap:DataFormat>
        </uap:ShareTarget>
      </uap:Extension>
</Extensions>  

UWP プロジェクトによって生成された実行可能ファイルの名前と、エントリ ポイント クラスの名前を指定します。 このマークアップでは、UWP アプリの実行可能ファイルの名前が ShareTarget.exe であることを前提としています。

アプリとの間で共有できるようにするファイルの種類を指定することも必要です。 この例では、WPF PhotoStoreDemo デスクトップ アプリケーションをビットマップ イメージの共有ターゲットとしています。そのため、サポートされているファイルの種類に Bitmap を指定します。

OnShareTargetActivated イベント ハンドラーをオーバーライドする

UWP プロジェクトの App クラスで、OnShareTargetActivated イベント ハンドラーをオーバーライドします。

このイベント ハンドラーは、ユーザーがファイルを共有するためにアプリを選択するときに呼び出されます。


protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    shareWithDesktopApplication(args.ShareOperation);
}

private async void shareWithDesktopApplication(ShareOperation shareOperation)
{
    if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        var items = await shareOperation.Data.GetStorageItemsAsync();
        StorageFile file = items[0] as StorageFile;
        IRandomAccessStreamWithContentType stream = await file.OpenReadAsync();

        await file.CopyAsync(ApplicationData.Current.LocalFolder);
            shareOperation.ReportCompleted();

        await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
    }
}

このコードでは、ユーザーによって共有されているイメージをアプリのローカル ストレージ フォルダーに保存します。 後で、その同じフォルダーからイメージをプルするように、デスクトップ アプリケーションを変更します。 デスクトップ アプリケーションは、UWP アプリと同じパッケージに含まれているために、これを行うことができます。

UWP プロジェクトにデスクトップ拡張機能を追加する

UWP アプリ プロジェクトに [Windows Desktop Extensions for the UWP] 拡張機能を追加します。 拡張機能の複数のバージョン (たとえば 10.0.18362.0 と 10.0.19041.0) が表示されます。 バージョンの選択方法については、「拡張機能 SDK とその参照方法」を参照してください。

desktop extension

完全信頼のプロセス拡張機能を追加する

ソリューション エクスプローラーで、ソリューション内にパッケージ プロジェクトの package.appxmanifest ファイルを開き、以前にこのファイルに追加している共有ターゲット拡張機能の横に、完全信頼のプロセス拡張機能を追加します。

<Extensions>
  ...
      <desktop:Extension Category="windows.fullTrustProcess" Executable="PhotoStoreDemo\PhotoStoreDemo.exe" />
  ...
</Extensions>  

この拡張機能によって、UWP アプリでは、ファイルを共有するデスクトップ アプリケーションを起動できるようになります。 例では、WPF PhotoStoreDemo デスクトップ アプリケーションの実行可能ファイルを参照しています。

共有ファイルを取得するようにデスクトップ アプリケーションを変更する

共有ファイルを検索して処理するように、デスクトップ アプリケーションを変更します。 この例では、UWP アプリによって、ローカル アプリ データ フォルダー内に共有ファイルが保存されました。 そのため、そのフォルダーから写真をプルするように、WPF PhotoStoreDemo デスクトップ アプリケーションを変更します。

Photos.Path = Windows.Storage.ApplicationData.Current.LocalFolder.Path;

ユーザーによって既に開かれているデスクトップ アプリケーションのインスタンスでは、FileSystemWatcher イベントを処理して、ファイルの場所へのパスを渡すこともできます。 これにより、開かれたデスクトップ アプリケーションのインスタンスでは、共有された写真が表示されます。

...

   FileSystemWatcher watcher = new FileSystemWatcher(Photos.Path);

...

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
    // new file got created, adding it to the list
    Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
    {
        if (File.Exists(e.FullPath))
        {
            ImageFile item = new ImageFile(e.FullPath);
            Photos.Insert(0, item);
            PhotoListBox.SelectedIndex = 0;
            CurrentPhoto.Source = (BitmapSource)item.Image;
        }
    }));
}

バックグラウンド タスクを作成する

バックグラウンド タスクを追加して、アプリが一時停止されているときでもコードを実行できます。 バックグラウンド タスクは、ユーザーの操作を必要としない小さなタスクに最適です。 たとえば、タスクはメールのダウンロード、受信チャット メッセージに関するトースト通知の表示、システムの状態の変化に対する対応を行うことができます。

バックグラウンド タスクを登録する WPF サンプル アプリケーションを以下に示します。

background task

タスクは http 要求を行い、要求が応答を返すのにかかる時間を測定します。 タスクはさらに興味深いものと考えられますが、このサンプルはバックグラウンド タスクの基本的なしくみを学習するのに適しています。

完全なサンプルについては、こちらを参照してください。

設計パターン

バックグラウンド サービスを作成するには、以下の手順を実行します。

1️⃣ バックグラウンド タスクの実装

2️⃣ バックグラウンド タスクの構成

3️⃣ バックグラウンド タスクの登録

バックグラウンド タスクの実装

Windows ランタイム コンポーネント プロジェクトにコードを追加することで、バックグラウンド タスクを実装します。

public sealed class SiteVerifier : IBackgroundTask
{
    public async void Run(IBackgroundTaskInstance taskInstance)
    {

        taskInstance.Canceled += TaskInstance_Canceled;
        BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
        var msg = await MeasureRequestTime();
        ShowToast(msg);
        deferral.Complete();
    }

    private async Task<string> MeasureRequestTime()
    {
        string msg;
        try
        {
            var url = ApplicationData.Current.LocalSettings.Values["UrlToVerify"] as string;
            var http = new HttpClient();
            Stopwatch clock = Stopwatch.StartNew();
            var response = await http.GetAsync(new Uri(url));
            response.EnsureSuccessStatusCode();
            var elapsed = clock.ElapsedMilliseconds;
            clock.Stop();
            msg = $"{url} took {elapsed.ToString()} ms";
        }
        catch (Exception ex)
        {
            msg = ex.Message;
        }
        return msg;
    }

バックグラウンド タスクの構成

マニフェスト デザイナーで、ソリューション内にパッケージ プロジェクトの package.appxmanifest ファイルを開きます。

[宣言] タブで、 [バックグラウンド タスク] 宣言を追加します。

Background task option

次に、必要なプロパティを選択します。 サンプルでは、Timer プロパティを使います。

Timer property

バックグラウンド タスクを実装する Windows ランタイム コンポーネントで、クラスの完全修飾名を指定します。

Specify entry point

バックグラウンド タスクの登録

バックグラウンド タスクを登録するデスクトップ アプリケーション プロジェクトにコードを追加します。

public void RegisterBackgroundTask(String triggerName)
{
    var current = BackgroundTaskRegistration.AllTasks
        .Where(b => b.Value.Name == triggerName).FirstOrDefault().Value;

    if (current is null)
    {
        BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
        builder.Name = triggerName;
        builder.SetTrigger(new MaintenanceTrigger(15, false));
        builder.TaskEntryPoint = "HttpPing.SiteVerifier";
        builder.Register();
        System.Diagnostics.Debug.WriteLine("BGTask registered:" + triggerName);
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("Task already:" + triggerName);
    }
}

質問に対する回答を見つける

ご質問があるでしょうか。 Stack Overflow でお問い合わせください。 Microsoft のチームでは、これらのタグをチェックしています。 こちらから質問することもできます。