次の方法で共有


WinUI 3 と Win32 相互運用機能を使用して C# .NET アプリを作成する

この記事では、プラットフォーム呼び出しサービス (PInvoke) を使用して、WinUI 3 と Win32 の相互運用機能を備えた基本的な C# .NET アプリケーションを構築する方法について説明します。

前提条件

  1. WinUI の概要

基本的なマネージド C#/.NET アプリ

この例では、アプリ ウィンドウの場所とサイズを指定し、適切な DPI に合わせて変換とスケーリングを行い、ウィンドウの最小化と最大化のボタンを無効にします。最後に、現在のプロセスに対してクエリを実行し、現在のプロセスに読み込まれているモジュールの一覧を表示します。

初期テンプレート アプリケーションから、サンプル アプリケーションをビルドします (「前提条件」を参照してください)。 「Visual Studio での WinUI 3 テンプレート」も参照してください。

MainWindow.xaml ファイル

WinUI 3 では、XAML マークアップで Window クラスのインスタンスを作成できます。

XAML の Window クラスは、デスクトップ ウィンドウをサポートするために拡張され、UWP およびデスクトップ アプリ モデルで使用される低レベル ウィンドウの各実装を抽象化したものになりました。 具体的には、UWP の CoreWindow と Win32 のウィンドウ ハンドル (HWND) です。

次のコード例は初期テンプレート アプリの MainWindow.xaml ファイルを示し、アプリのルート要素として Window クラスを使用します。

<Window
    x:Class="WinUI_3_basic_win32_interop.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUI_3_basic_win32_interop"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
    </StackPanel>
</Window>

構成

  1. User32.dll で公開される Win32 API を呼び出すには、オープン ソースの PInvoke.User32 NuGet パッケージを VS プロジェクトに追加します (Visual Studio のメニューから、[ツール] -> [NuGet パッケージ マネージャー] -> [ソリューションの NuGet パッケージの管理] の順に選択して "Pinvoke.User32" を検索します)。 詳細については、「マネージド コードからのネイティブ関数の呼び出し」を参照してください。

    PInvoke.User32 が選択されている Visual Studio NuGet パッケージ マネージャーのスクリーンショット。
    "PInvoke.User32 が選択されている NuGet パッケージ マネージャー。 "

    VS プロジェクトの Packages フォルダーをチェックして、インストールが正常に完了したことを確認します。

    PInvoke.User32 が選択されている Visual Studio ソリューション エクスプローラー パッケージのスクリーンショット。
    "PInvoke.User32 が選択されているソリューション エクスプローラー パッケージ。 "

    次に、アプリケーション プロジェクト ファイルをダブルクリック (または右クリックして [プロジェクト ファイルの編集] を選択) してテキスト エディターでファイルを開き、プロジェクト ファイルに "PInvoke.User32" の NuGet PackageReference が含まれていることを確認します。

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
        <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
        <RootNamespace>WinUI_3_basic_win32_interop</RootNamespace>
        <ApplicationManifest>app.manifest</ApplicationManifest>
        <Platforms>x86;x64;arm64</Platforms>
        <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
        <UseWinUI>true</UseWinUI>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.ProjectReunion" Version="0.8.1" />
        <PackageReference Include="Microsoft.ProjectReunion.Foundation" Version="0.8.1" />
        <PackageReference Include="Microsoft.ProjectReunion.WinUI" Version="0.8.1" />
        <PackageReference Include="PInvoke.User32" Version="0.7.104" />
        <Manifest Include="$(ApplicationManifest)" />
      </ItemGroup>
    </Project>
    

コード

  1. App.xaml.cs の分離コード ファイルで、WindowNative.GetWindowHandle WinRT COM 相互運用機能メソッドを使用して ウィンドウのハンドルを取得します (「ウィンドウ ハンドルを取得する (HWND)」を参照してください)。

    このメソッドは、次に示すように、アプリの OnLaunched ハンドラーから呼び出されます。

    /// <summary>
    /// Invoked when the application is launched normally by the end user.  Other entry points
    /// will be used such as when the application is launched to open a specific file.
    /// </summary>
    /// <param name="args">Details about the launch request and process.</param>
    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
    
        var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(m_window);
    
        SetWindowDetails(hwnd, 800, 600);
    
        m_window.Activate();
    }
    
  2. 次に、SetWindowDetails メソッドを呼び出して、ウィンドウハンドルと優先ディメンションを渡します。 using static PInvoke.User32; ディレクティブを忘れずに追加してください。

    この方法では:

    • GetDpiForWindow を呼び出して、ウィンドウのドット/インチ (dpi) の値を取得します (Win32 では、実際のピクセルが使用されますが、WinUI 3 では有効ピクセルが使用されます)。 この dpi 値は、スケール ファクターを計算し、ウィンドウに指定された幅と高さに適用するために使用されます。
    • 次に、SetWindowPos を呼び出して、ウィンドウの目的の場所を指定します。
    • 最後に、SetWindowLong を呼び出して、"最小化" と "最大化" のボタンを無効にします。
    private static void SetWindowDetails(IntPtr hwnd, int width, int height)
    {
        var dpi = GetDpiForWindow(hwnd);
        float scalingFactor = (float)dpi / 96;
        width = (int)(width * scalingFactor);
        height = (int)(height * scalingFactor);
    
        _ = SetWindowPos(hwnd, SpecialWindowHandles.HWND_TOP,
                                    0, 0, width, height,
                                    SetWindowPosFlags.SWP_NOMOVE);
        _ = SetWindowLong(hwnd, 
               WindowLongIndexFlags.GWL_STYLE,
               (SetWindowLongFlags)(GetWindowLong(hwnd,
                  WindowLongIndexFlags.GWL_STYLE) &
                  ~(int)SetWindowLongFlags.WS_MINIMIZEBOX &
                  ~(int)SetWindowLongFlags.WS_MAXIMIZEBOX));
    }
    
  3. MainWindow.xaml ファイルで、ContentDialogScrollViewer を使用して、現在のプロセスに読み込まれたすべてのモジュールの一覧を表示します。

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Display loaded modules</Button>
    
        <ContentDialog x:Name="contentDialog" CloseButtonText="Close">
            <ScrollViewer>
                <TextBlock x:Name="cdTextBlock" TextWrapping="Wrap" />
            </ScrollViewer>
        </ContentDialog>
    
    </StackPanel>
    
  4. 次に、MyButton_Click イベント ハンドラーを次のコードに置き換えます。

    ここでは、GetCurrentProcess を呼び出して現在のプロセスへの参照を取得します。 次に、モジュールのコレクションを反復処理し、各 ProcessModule のファイル名を表示文字列に追加します。

    private async void myButton_Click(object sender, RoutedEventArgs e)
    {
        myButton.Content = "Clicked";
    
        var description = new System.Text.StringBuilder();
        var process = System.Diagnostics.Process.GetCurrentProcess();
        foreach (System.Diagnostics.ProcessModule module in process.Modules)
        {
            description.AppendLine(module.FileName);
        }
    
        cdTextBlock.Text = description.ToString();
        await contentDialog.ShowAsync();
    }
    
  5. アプリをコンパイルして実行します。

  6. ウィンドウが表示されたら、[読み込まれたモジュールの表示] ボタンを選択します。

    このトピックで説明されている基本的な Win32 相互運用アプリケーションのスクリーンショット。
    "このトピックで説明されている基本的な Win32 相互運用アプリケーション。 "

概要

このトピックでは、基になるウィンドウ実装 (この例では Win32 と HWND) へのアクセス方法、および WinRT API と共に Win32 API を使用する方法について説明しました。 ここでは、新しい WinUI 3 デスクトップ アプリを作成するときの、既存のデスクトップ アプリケーション コードを使用方法も示しています。

より広範なサンプルについては、Window App SDK サンプルの GitHub リポジトリにある AppWindow ギャラリーのサンプルを参照してください。

関連項目