LoginSignup
1
0

More than 3 years have passed since last update.

libclangでWindowsKitsをDllImportするライブラリを作った(ている)

Last updated at Posted at 2020-06-27

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
Program.cs
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# のコード生成なので捨てちゃった。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0