.NET 5 環境で Win 32 API を利用しようと思ったら、従来なら P/Invoke(DllImport)で利用する関数や構造体なんかを再定義して利用していたと思います。2021 年末を完成目標にして「win32metadata」が立ち上がっているので、こっちを利用してみるテスト。
win32metadata は、Win 32 API を定義したメタデータなので、それを呼び出すためには、次の拡張を利用するという関係になっている。
このあたりについて、実際にコードとして、ウマ娘のウィンドウ画像をキャプチャーするサンプルを書いてみたのでメモ。
使い方のメモ
- プロジェクトに「NativeMethods.txt」テキストファイルを追加する。
- 「NativeMethods.txt」テキストファイルの記述ルールは次のとおり:
- 必要な関数名(例:FindWindow)(必要に応じて、A, W のサフィックスが含まれることがある)を記入する。
- モジュール名の後に
.*
をつけると、モジュールのメソッドが全部生成される。(例:Kernel32.*) - 生成することができるものは、struct, enum, 定数、または、インターフェースの名前です。
- コメント (
//
) から始まる行、空白行は無視する。
テキストファイルに追加した関数は、次のコードで利用します:
using Microsoft.Windows.Sdk; // 関数呼び出し PInvoke.追加した関数名(/*args*/); // 定数呼び出し Constants.追加した定数名
「NativeMethods.txt」テキストファイルをプロジェクトに追加していないとき、または、内容が空白のとき、Microsoft.Windows.CsWin32
はコードをジェネレートしていないみたいです。このときは、名前空間 Microsoft.Windows.Sdk
も定義されていません。
なので「NativeMethods.txt」を編集する前にコーディングをすると、必ずエラーになります。注意しましょう。
WPF 環境の場合(コンパイルエラーになる問題)
WPF + .NET 5 環境だと 2021 年 6 月時点、バグがあってコードインテリジェンスで記述している時点では、ジェネレーターが追加した win32 用の関数等を正常に読み込めているけど、コンパイル時は解決できずに失敗します。
この問題は、以下で修正案が指摘されていました。
プロジェクトの .csproj
ファイルを開いて、以下のコードを追加します。
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation> > <UseWPF>true</UseWPF>
PropertyGroup
の中に加えます。(UseWPF
は最初から記述されているかも)サンプルは以下のとおりです。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net5.0-windows</TargetFramework> <IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation> <UseWPF>true</UseWPF> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.1.422-beta"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="System.Drawing.Common" Version="5.0.2" /> </ItemGroup> </Project>
サンプル
GitHub に「CoreAndWin32Test」のプロジェクトを追加しています。テストコードでは、メモ帳のウィンドウコピー、DMM 版ウマ娘のウィンドウコピーを実装してみました。
NativeMethods に追加した関数は次のとおりです。
BITMAPINFO BITMAPINFOHEADER BitBlt CreateCompatibleDC CreateDIBSection FindWindow GetClientRect GetWindowDC MapWindowPoints ReleaseDC SelectObject
こんな感じで、ウィンドウの枠を除いた画像をコピーすることができます。
個人的な意見
NativeMethods に登録した関数 FindWindow だと、HWND のような構造体のようなクラスも自動的にコンパイルされるようになります。 ただ、このあたりは win32 API を知らない人だと、単に IntPtr で操作したほうが楽だったりもすると思うので、便利なんだけど型を具体的にする一方で面倒もあるなという感じでした。(ポインタを使わざるを得なくなるところもあって、unsafe コードになったりもしたし)