22
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C#備忘録 - Windowハンドル関連の逆引きリファレンス的なやつ(主にWin32API)

Last updated at Posted at 2019-12-27

はじめに

Windowsで他アプリと何かしらやりとりしたいときは、大抵ウィンドウハンドルが必要になる。
いちいち調べなおすのは面倒なので、使ったことのあるAPIとかメソッドをまとめてみた。

Windows APIの使用に際して

C# では、ウィンドウハンドルの型はIntPtrとなる。
IntPtrのサイズは32bit/64bit環境に依存する1ので、型をint(32bit)とかと間違えないようにすること。

APIについては、とりあえず[DllImport("user32.dll", CharSet = CharSet.Auto)]つけておけば動くはず。(テキトウ・・)

1. ウィンドウハンドルを取得する

No 概要 APIまたはメソッド
(Microsoftへリンク)
使用例へリンク
1-1 スクリーン座標から取得 (API) WindowFromPoint https://qiita.com/kob58im/items/3587d8e595e655e9391d
1-2 全体から検索・列挙 (API) FindWindowEx https://qiita.com/kob58im/items/d5828e6cf5416d776549
1-3 親を取得 (API) GetAncestor https://qiita.com/kob58im/items/3587d8e595e655e9391d
- 現在アクティブ(≒フォーカスを持っている)ウィンドウを取得 (API) GetForegroundWindow
1-4 トップレベルウィンドウを列挙しつつ処理する (API) EnumWindows https://qiita.com/kob58im/items/3587d8e595e655e9391d
1-5 子ウィンドウを列挙しつつ処理する★ (API) EnumChildWindows https://qiita.com/kob58im/items/3587d8e595e655e9391d
1-A コントロールから取得 Control.Handle https://qiita.com/kob58im/items/4bd061df83212a723be7

★・・・ 子のウィンドウハンドルはWin32APIでは取得できないケースがあるようなので注意。

2. ハンドルを使って何かする

No 概要 APIまたはメソッド
(Microsoftへリンク)
使用例へリンク
2-1 クラス名を得る (API) GetClassName https://qiita.com/kob58im/items/3587d8e595e655e9391d
2-2 タイトルを得る (API) GetWindowText https://qiita.com/kob58im/items/3587d8e595e655e9391d
2-3 プロセスIDを得る (API) GetWindowThreadProcessId https://qiita.com/kob58im/items/d5828e6cf5416d776549
2-4 座標やサイズなどの情報を得る (API) GetWindowInfo https://qiita.com/kob58im/items/3587d8e595e655e9391d
2-5 メッセージを送る (API) SendMessage https://qiita.com/kob58im/items/4bd061df83212a723be7
2-6 フォーカスを移す (API) SetForegroundWindow 【外部サイトリンク】外部アプリケーションのウィンドウをアクティブにする - dobon.net
2-7 DCを取得する (API) GetWindowDC 【外部サイトリンク】画面をキャプチャする - dobon.net
2-A UI Automationを使う AutomationElement.FromHandle https://qiita.com/kob58im/items/3587d8e595e655e9391d

抜粋

※抜粋しているので、そのままでは動きません。使用例の記事を参照ください。
※この記事でのDllImportの属性指定や、メソッド宣言のパラメータ指定は、あくまで例であり、これを推奨するものではありません。

1-1. WindowFromPoint


// 定義部
static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT {
        public int x;
        public int y;
    }
    [DllImport("user32.dll",SetLastError = true)]
    public static extern IntPtr WindowFromPoint(POINT point);
}

// 使用部
void hoge()
{
    var p = new POINT();
    NativeMethods.GetCursorPos(out p);
    IntPtr hWnd = NativeMethods.WindowFromPoint( p );
}

1-2. FindWindowEx


// 定義部
static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindowEx(IntPtr parentWnd, IntPtr previousWnd, string className, string windowText);
}

// 使用部
void hoge()
{
    IntPtr hWnd = IntPtr.Zero;
    while ( IntPtr.Zero != (hWnd = NativeMethods.FindWindowEx(IntPtr.Zero, hWnd, クラス名文字列, ウィンドウ名文字列))) {
        // クラス名・ウィンドウ名に一致したウィンドウに対する処理をここに書く
    }
}

1-3. GetAncestor


// 定義部
static class NativeMethods
{
    [DllImport("user32.dll",SetLastError = true)]
    public static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
    public const uint GA_PARENT    = 1;
    public const uint GA_ROOT      = 2;
    public const uint GA_ROOTOWNER = 3;
}

// 使用部
void hoge(IntPtr hWnd)
{
    IntPtr hWndRoot = IntPtr.Zero;
    hWndRoot = NativeMethods.GetAncestor(hWnd, NativeMethods.GA_ROOT);
}

1-4. EnumWindows


// 定義部
static class NativeMethods
{
    public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc,   IntPtr lparam);
}

// 使用部
bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
{
    // hWndを使った処理をここに書く

    // trueを返すことで、すべてのウィンドウを列挙する
    return true;
}
void hoge()
{
    NativeMethods.EnumWindows(EnumWindowCallBack, IntPtr.Zero);
}

FindWindowExのほうが使いやすいかも

1-5. EnumChildWindows


// 定義部
static class NativeMethods
{
    public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

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

// 使用部
bool EnumChildWindowCallBack(IntPtr hWnd, IntPtr lparam)
{
    // 処理...

    // hWndの子ウィンドウを探す
    NativeMethods.EnumChildWindows(hWnd, EnumChildWindowCallBack, IntPtr.Zero);

    return true;   // すべての兄弟ウィンドウを列挙する
}
void hoge()
{
    NativeMethods.EnumChildWindows(ルートとなるウィンドウのハンドル, EnumChildWindowCallBack, IntPtr.Zero);
}

FindWindowExのほうが使いやすいかも

★子のウィンドウハンドルはWin32APIでは取得できないケースがあるようなので注意。

2-1. GetClassName


// 定義部
static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}

// 使用部
public static string MyGetClassName(IntPtr hWnd, out int retCode)
{
    StringBuilder csb = new StringBuilder(MaxTextLength);
    retCode = NativeMethods.GetClassName(hWnd, csb, csb.Capacity);
    if ( retCode > 0 ) {
        return csb.ToString();
    }
    else {
        return string.Empty;
    }
}

2-2. GetWindowText


// 定義部
static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
}

// 使用部
public static string MyGetWindowText(IntPtr hWnd, out int retCode)
{
    //ウィンドウのタイトルを取得する
    StringBuilder tsb = new StringBuilder(MaxTextLength);
    retCode = NativeMethods.GetWindowText(hWnd, tsb, tsb.Capacity);

    if ( retCode > 0 ) { // 成功すると長さが返る
        return tsb.ToString();
    }
    else { // 0の場合はエラーの可能性がある
        return string.Empty;
    }
}

2-3. GetWindowThreadProcessId


// 定義部
static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
}

// 使用部
void hoge()
{
    int pid;
    NativeMethods.GetWindowThreadProcessId(hWnd, out pid);
    Process p = Process.GetProcessById(pid);
}

2-4. GetWindowInfo


// 定義部
static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWINFO
    {
        public int   cbSize;
        public RECT  rcWindow;
        public RECT  rcClient;
        public int   dwStyle;
        public int   dwExStyle;
        public int   dwWindowStatus;
        public uint  cxWindowBorders;
        public uint  cyWindowBorders;
        public short atomWindowType;
        public short wCreatorVersion;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    [DllImport("user32.dll",SetLastError = true)]
    public static extern int GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
}

// 使用部
public static WINDOWINFO MyGetWindowInfo(IntPtr hWnd, out int retCode)
{
    var wi = new WINDOWINFO();
    wi.cbSize = Marshal.SizeOf(wi);
    retCode = NativeMethods.GetWindowInfo(hWnd, ref wi);
    return wi;
}

2-5. SendMessage

本質と関係ないとこで解放処理が漏れてます。そのうち見直します。(というか例を変えたい・・)


// 定義部
static class NativeMethods
{
    [DllImport("user32")]
    public extern static IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
}

// 使用部
static IntPtr SetTabStop(TextBoxBase t, int tabSize)
{
    int[] tabarray = new int[] { tabSize*4 };
    int wparam = tabarray.Length;

    IntPtr parray = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * tabarray.Length);
    Marshal.Copy(tabarray, 0, parray, tabarray.Length);
    IntPtr ret = SendMessage(t.Handle, EM_SETTABSTOPS, new IntPtr(wparam), parray);
    // 解放処理が漏れている。 Marshal.FreeCoTaskMem とかで解放してください。
    return ret;
}

脱線編 - WinAPIまわりのメモリ確保と解放について

メモリの解放は完璧にしようとすると奥が深そう(参考サイト参照)であるが、趣味の範疇ならMarshal.AllocCoTaskMem, Marshal.FreeCoTaskMem確保;try{処理;}finally{解放;}で十分かと。

参考サイト

  1. 正確にはコンパイル時のオプション指定による。

22
37
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
22
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?