WindowsKitsのヘッダ C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0
を libclang でパースして大量の DllImport を生成しました。
使い方
> mkdir Hello
> cd Hello
> dotnet new console
> dotnet add package NWindowsKits
using System;
using NWindowsKits;
namespace HelloWorld
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
var hInstance = default(HINSTANCE);
var nCmdShow = C.SW_SHOW;
// Register the window class.
var CLASS_NAME = "Sample Window Class";
var wc = new WNDCLASSW { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
user32.RegisterClassW(ref wc);
// Create the window.
var hwnd = user32.CreateWindowExW(
0, // Optional window styles.
CLASS_NAME, // Window class
"Learn to Program Windows", // Window text
C.WS_OVERLAPPEDWINDOW, // Window style
// Size and position
C.CW_USEDEFAULT, C.CW_USEDEFAULT, C.CW_USEDEFAULT, C.CW_USEDEFAULT,
default, // Parent window
default, // Menu
hInstance, // Instance handle
default // Additional application data
);
if (hwnd.ptr == IntPtr.Zero)
{
return;
}
user32.ShowWindow(hwnd, nCmdShow);
// Run the message loop.
var msg = new MSG { };
while (user32.GetMessageW(ref msg, default, 0, 0) != 0)
{
user32.TranslateMessage(ref msg);
user32.DispatchMessageW(ref msg);
}
}
static long WindowProc(HWND hwnd, uint uMsg, ulong wParam, long lParam)
{
switch (uMsg)
{
case C.WM_DESTROY:
user32.PostQuitMessage(0);
return 0;
case C.WM_PAINT:
{
PAINTSTRUCT ps = default;
var hdc = user32.BeginPaint(hwnd, ref ps);
// All painting occurs here, between BeginPaint and EndPaint.
var hbrush = new HBRUSH
{
ptr = new IntPtr(C.COLOR_WINDOW + 1),
};
user32.FillRect(hdc, ref ps.rcPaint, hbrush);
user32.EndPaint(hwnd, ref ps);
}
return 0;
}
return user32.DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
}
}
上記のコードは、
をベタに移植したものです。
C++ の知識があれば、 IntPtr の扱いとか CreateWindowExW
になっているところとかなんとなくわかると思う。
関数は、dllと同じ名前の static class に、マクロ定数は static class C
に押し込んだ。namespace に直接定義できないのでやむを得ない。
d3d11あるよ
という感じになった。Comのベースクラスを、C++ ComPtr を模して作った。
using var device = new ID3D11Device();
var hr = CreateCom(ref device.NewPtr); // C++ の void** ppVoid に相当する
if(hr!=0)
{
throw new COMException("", hr);
}
のように使うクラスで、
ComPtr<ID3D11Device>::operator& // ref IntPtr device.NewPtr が相当。void** ppVoid
ComPtr<ID3D11Device>::Get() // IntPtr device.Ptr が相当。T*
となっている。
C# は、 Com をサポートしているのになんでそんなことするのかというと、いろいろとうまく行かなかったからです。
// こういうやつ。記述が間違っているとエラーになりますが、どう間違っているかわからないので対処できません。
// だいたい仮想関数のindexがずれているのだけど Marshal.GetStartComSlot 等を駆使してデバッグするのがつらいっていうかなおせなかった。
// Marshal の細かいところをエスパーする必要がありつらい。
[ComImport, Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISomeCOMInterface
{
// Define interface methods here, using PInvoke conversion between types
}
あと、ComPtr のようなスマートポインタで Release したい。
なので、IDisposable にした。
コード生成はLLVMのlibclangで作った
去年から何回か作り直していて、 4代目か5代目くらい。
tag名, typedef, macro, 前方宣言, struct(union)の無名フィールドといった、 C言語 特有の問題に対処する必要がある。
だいたい、 libclang の API で何とかなるが、マクロは本当につらい。int 定数の定義だけ雑に対応している。
あと、const。まじめに対応すると複雑になるのだが、C# のコード生成なので捨てちゃった。