potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# 9&Win32 API ウィンドウハンドルを列挙する機能を提供するクラス

ウィンドウハンドルを列挙する機能を提供するクラスのコードです。

// ウィンドウのウィンドウハンドルを階層構造付きで列挙するサンプルコード
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

foreach (var handle in WindowHandles.GetTopLevelWindowHandles())
{
    ConsoleWriteWindowHandles(handle, 0);
}

static void ConsoleWriteWindowHandles(IntPtr handle, int indent)
{
    Console.WriteLine(new String(' ', indent) + "0x" + handle.ToString("x08"));
    foreach (var childHandle in WindowHandles.GetChildWindowHandles(handle))
    {
        ConsoleWriteWindowHandles(childHandle, indent + 1);
    }
}

public static class WindowHandles
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private delegate bool EnumWindowsProcDelegate(IntPtr windowHandle, IntPtr lParam);

    private static bool EnumWindowProc(IntPtr handle, IntPtr lParam)
    {
        var handles = (List<IntPtr>)GCHandle.FromIntPtr(lParam).Target;
        handles.Add(handle);
        return true;
    }

    public static IntPtr[] GetTopLevelWindowHandles()
    {
        var handles = new List<IntPtr>();
        var gch = GCHandle.Alloc(handles, GCHandleType.Weak);
        if (!NativeMethods.EnumWindows(EnumWindowProc, GCHandle.ToIntPtr(gch)))
            if (Marshal.GetLastWin32Error() != 0)
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        gch.Free();
        return handles.ToArray();
    }

    public static IntPtr[] GetChildWindowHandles(IntPtr windowHandle)
    {
        if (windowHandle == default)
            throw new ArgumentException(nameof(windowHandle));
        var handles = new List<IntPtr>();
        var gch = GCHandle.Alloc(handles, GCHandleType.Weak);
        if (!NativeMethods.EnumChildWindows(windowHandle, EnumWindowProc, GCHandle.ToIntPtr(gch)))
            if (Marshal.GetLastWin32Error() != 0)
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        gch.Free();
        return handles.ToArray();
    }

    public static IntPtr[] GetThreadWindowHandles(uint threadId)
    {
        var handles = new List<IntPtr>();
        var gch = GCHandle.Alloc(handles, GCHandleType.Weak);
        if (!NativeMethods.EnumThreadWindows(threadId, EnumWindowProc, GCHandle.ToIntPtr(gch)))
            if (Marshal.GetLastWin32Error() != 0)
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        gch.Free();
        return handles.ToArray();
    }

    private static class NativeMethods
    {
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumWindows(
            EnumWindowsProcDelegate enumProc,
            IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(
            IntPtr handle,
            EnumWindowsProcDelegate enumProc,
            IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumThreadWindows(
            uint threadId,
            EnumWindowsProcDelegate enumProc,
            IntPtr lParam);
    }
}