【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

関数ポインタのマーシャリング

.NET Compact Framework 2.0では関数ポインタのマーシャリングがサポートされましたが、引数や戻り値の型に制限があります。boolは変換できないためNotSupportedExceptionになります。boolの代わりにintを使えば問題ありません。

このことを知らずにP/InvokeでEnumWindows()を使おうとしてハマりました。以下に正常動作する例を示します。 "coredll.dll" を "user32.dll" に変更すればPCでも使用できます。

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int EnumWindowsProc(IntPtr hWnd, int lParam);

[DllImport("coredll.dll")]
public static extern int EnumWindows(EnumWindowsProc lpEnumFunc, int lParam);

[DllImport("coredll.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

[DllImport("coredll.dll", CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

public static IntPtr GetWindowHandle(string className)
{
    var ret = IntPtr.Zero;
    var curpid = Process.GetCurrentProcess().Id;
    EnumWindows((hWnd, lParam) =>
    {
        int pid;
        GetWindowThreadProcessId(hWnd, out pid);
        if (pid == curpid)
        {
            var sb = new StringBuilder(64);
            GetClassName(hWnd, sb, sb.Capacity);
            if (sb.ToString() == className)
            {
                ret = hWnd;
                return 0;
            }
        }
        return 1;
    }, 0);
    return ret;
}

クロージャのマーシャリングはサンクで実装されているようです。