C# Win32API完全入門

# はじめに

## 対象とする読者について

本記事の対象者としては以下のような人を想定しています。  

* C#でこれからWin32APIを使ってみたい。  
* C言語のことがあまりよく分かっていない。  
* 今までは適当に使っていたので一度きちんと理解したい。  

自分が同じような状況であったため、一から調べて整理してみました。自分が理解した順番や内容で記載することで、また、具体的な使用例によってできることの広さや動作を感じ取ってもらうことで、理解の助けになればと思っています。  

但し、分かっている人からすると冗長な説明になっている部分や好ましくない内容、正確性に欠ける内容などもあると思います。実際に使用する場合はその点にご留意願います。  

## Win32APIについて
[Windows API - Wikipedia](https://ja.wikipedia.org/wiki/Windows_API)  
> Microsoft Windowsのシステムコール用APIのこと。特に32ビットプロセッサで動作するWindows 95以降やWindows NTで利用できるものはWin32 APIと呼ばれる。  
> 64ビットプロセッサ向けのWin64 APIも含める場合は「Windows API」という包括的な名称が正確だが、慣習的にWin32と言えばWin64も含んでいることがある  

APIについて調べたい場合は、  

* 目的別  
[プログラミングのメモ帳(C/C++/HSP)](https://blog.goo.ne.jp/masaki_goo_2006)  

* インデックス別  
[Windows API インデックス - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/apiindex/windows-api-list)  
[Win32API 一覧](https://codepanic.itigo.jp/win32.html)  

## C#からWin32APIを利用するにあたって

[マネージド](https://learn.microsoft.com/ja-jp/dotnet/standard/managed-code)である`C#`からアンマネージド（ネイティブ）である`Win32API`を利用する場合、  

[アンマネージ DLL 関数の処理 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/consuming-unmanaged-dll-functions)  
> プラットフォーム呼び出しは、マネージド コードがダイナミック リンク ライブラリ (DLL) に実装されたアンマネージド関数 (Windows API に含まれているものなど) を呼び出すことを可能にするサービスです。 これはエクスポートされた関数を見つけて呼び出し、必要に応じて相互運用の境界を越えて、その引数 (整数、文字列、配列、構造体、その他) をマーシャリングします。  

[型のマーシャリング - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/type-marshalling)  
> マーシャリングとは、マネージド コードとネイティブ コードの間でやり取りする必要がある場合に型を変換するプロセスです。  

[プラットフォーム呼び出し (P/Invoke) - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/pinvoke)  
> P/Invoke は、アンマネージド ライブラリ内の構造体、コールバック、および関数をマネージド コードからアクセスできるようにするテクノロジです。  

## Microsoft Learnリンク一覧

* [アンマネージ コードとの相互運用 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/)  
* [アンマネージ DLL 関数の処理 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/consuming-unmanaged-dll-functions)  
* [相互運用マーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/interop-marshalling)  
    * [既定のマーシャリングの動作 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/default-marshalling-behavior)  
        * [Blittable 型と非 Blittable 型 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/blittable-and-non-blittable-types)  
        * [コピーと固定 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/copying-and-pinning)  
        * [方向属性 | Microsoft Learn](https://learn.microsoft.com/ja-jp/previous-versions/dotnet/netframework-4.0/77e6taeh(v=vs.100))  
        * [文字列に対する既定のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/default-marshalling-for-strings)  
    * [プラットフォーム呼び出しによるデータのマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-data-with-platform-invoke)  
        * [文字列のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-strings)  
        * [クラス、構造体、および共用体のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-classes-structures-and-unions)  
        * [さまざまな型の配列のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-different-types-of-arrays)  
* [ネイティブ相互運用性 - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/)  
    * [プラットフォーム呼び出し (P/Invoke) - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/pinvoke)  
    * [ネイティブ相互運用性のベスト プラクティス - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/best-practices)  
* [言語間の相互運用性 | Microsoft Learn](https://learn.microsoft.com/ja-jp/previous-versions/visualstudio/visual-studio-2008/730f1wy3(v=vs.90))  
* [System.Runtime.InteropServices 名前空間 | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices?view=netframework-4.8)  
    * [DllImportAttribute クラス (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute?view=net-8.0)  
    * [UnmanagedType 列挙型 (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.unmanagedtype?view=netframework-4.8)  

## Microsoft Learn 日本語⇔英語切り替え

Microsoft Learnを見ているとき、日本語と英語を切り替えながら読みたいときがあります。以下のブラウザ拡張機能を使うとページ右下に言語を切り替えるボタン（リンク）が追加されます。  

1. 「en-us と他の言語の切り替え」( <a href="https://chromewebstore.google.com/detail/en-us-%E3%81%A8%E4%BB%96%E3%81%AE%E8%A8%80%E8%AA%9E%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88/jcadeffanknabngcmkkieiiekillhodj">Chrome/Edge&#129133;</a> <a href="https://addons.mozilla.org/ja/firefox/addon/switching-between-languages/">Firefox&#129133;</a> )をインストールします。  
1. オプション画面を開いて「ja-jp」を選択してSaveします。  
1. 適当な[Microsoft Learn](https://learn.microsoft.com/ja-jp/)のページを開くとページ右下に`en-us`ボタンが表示されます。  
1. ボタンをクリックするか、右クリックしてリンクを新しいタブで開くを選択して英語サイトを開きます。  
1. 英語サイトの場合でも同様です。`ja-jp`ボタンが表示されるのでクリックして日本語サイトを表示します。  

# 実際に使ってみる

[SetWindowText](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowtextw)を使用して、起動した`winver.exe`のタイトルを変更します。  

以下のファイルを`sample.cs`として保存し、`C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe -target:winexe sample.cs`でコンパイルし`sample.exe`を作成します。
通常`winver.exe`を実行すると「Windows のバージョン情報」というタイトルになりますが`sample.exe`を実行すると「変更後のタイトルバーのテキスト」という表示になります。  

```cs
/*
起動したwinver.exeのタイトルを変更
ファイル名:sample.cs
コンパイル方法:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe -target:winexe sample.cs
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace win32api_sample
{
    //---------- Win32API用定義開始 ----------

    //Windows APIの定義
    internal static class NativeMethods
    {
        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
    }

    //---------- Win32API用定義終了 ----------

    internal class Program
    {
        static void Main(string[] args)
        {
            using (Process process = new Process())
            {
                //メモ帳を起動
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.FileName = @"C:\Windows\System32\winver.exe";
                process.Start();
                process.WaitForInputIdle(3000);

                //タイトルバーのテキスト変更
                NativeMethods.SetWindowText(process.MainWindowHandle, "変更後のタイトルバーのテキスト");
            }
        }
    }
}
```

`SetWindowText`を使用するにあたり、C言語の記述を  
```cpp
BOOL SetWindowTextW(
  [in]           HWND    hWnd,
  [in, optional] LPCWSTR lpString
);
```
C#の記述に置き換えて定義しています。
```cs
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
}
```
1. [`System.Runtime.InteropServices`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices?view=net-8.0)を`using`します。  
    * `System.Runtime.InteropServices`はCOM 相互運用機能とプラットフォーム呼び出しサービスをサポートするさまざまなメンバーを提供します。  
1. [`internal`](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/internal)キーワードを付けた`NativeMethods`クラス内に定義します。  
    * `public`にしてはいけません。  
    [CA1401: P/Invoke を参照可能にしない (コード分析) - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/fundamentals/code-analysis/quality-rules/ca1401)  
    * `NativeMethods`クラス内に定義します。  
    [CA1060: P-Invoke を NativeMethods クラスに移動します (コード分析) - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/fundamentals/code-analysis/quality-rules/ca1060)  
    
1. [`DllImport`](#dllimport属性)を使用してDLLと関数を指定します。  
    今回の場合は、[SetWindowText](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowtextw)ページの要件（最下部）の箇所を見て`user32.dll`であることを確認します。  
1. 関数名を記載します（[末尾のA,W](#末尾のaw)参照）。  
1. 関数を[`static`](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/static)、[`extern`](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/extern)修飾子でマークします。   
    [アンマネージ DLL 関数の処理 - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/consuming-unmanaged-dll-functions#to-consume-exported-dll-functions)  
1. [引数を設定](#引数の設定)します。  

# 関数を定義する手順

## DllImport属性の設定

以下の例の`CharSet = CharSet.Unicode`の部分。とりあえず使用したいだけであれば、ネット上の使用例をそのまま記載すればいいと思います。詳細は[`DllImport`](#dllimport属性)を参照。  
```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
```

## 関数名の設定

### 末尾のA,W

関数名には`SetWindowTextA`や`SetWindowTextW`のように末尾がA,Wで終わるものがあります。  

`A` : ANSI版（つまり古い）  
`W` : Unicode版  

* `FunctionA`と`FunctionW`がある場合は末尾から`A`,`W`を取った`Function`を使用します。  
* 実際には`Function`は存在しません。マクロ定義により、プラットフォームに応じて`FunctionA`と`FunctionW`のいずれかが呼び出されます。  
* 使用する場合はUnicodeであることを明示します。  
* パフォーマンスを少しでも上げたい場合は、`ExactSpelling = true`と組み合わせて、直接`FunctionW`を指定します。（[CharSet/ExactSpelling](#charsetexactspelling)参照）  

例）[SetWindowTextA](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowtexta)と[SetWindowTextW](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowtextw)の場合  
1. 末尾から`A`,`W`を取った`SetWindowText`を使用します。  
2. Unicodeを使用することを明示します。`CharSet = CharSet.Unicode`  

```cs
internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
}
```

**参考URL**  
* [WindowsAPI系の関数はなぜAやWが末尾についてるんでしょうか?](https://teratail.com/questions/283340)  
* [.NET TIPS Win32 APIやDLL関数に文字列や文字列バッファを渡すには？ - C# - ＠IT](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/025w32string/w32string.html)  

## 引数の設定

例えば`SetWindowTextW`の場合、  
```cpp
BOOL SetWindowTextW(
  [in]           HWND    hWnd,
  [in, optional] LPCWSTR lpString
);
```
第1引数である`HWND hWnd`はC#では`IntPtr hWnd`のように記載を変える必要があります。  
1. 変数名についてはそのまま流用します。  
1. 型については対応するC#での記載に変更します。詳細は[型](#型)を参照。  

## 定数の設定

関数で使用する定数（[ごみ箱の中身をクリア](#ごみ箱の中身をクリア)の`SHERB_NOCONFIRMATION`など）を定義する必要があります。

定数値を調べたい場合は、
1. [Microsoft Learn](https://learn.microsoft.com/ja-jp/)で検索して確認します。  
    1. [`HWND_TOPMOST -1`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowpos)のように直接記載があるものはその値を使用します。  
    1. 列挙型（`enum`）の場合は、明示がない限りゼロ始まりです。例えば[DWMWINDOWATTRIBUTE](https://learn.microsoft.com/ja-jp/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute)であれば`DWMWA_NCRENDERING_ENABLED`が0、`DWMWA_NCRENDERING_POLICY`が1になります。`DWMWA_CAPTION_COLOR`は`DWMWA_WINDOW_CORNER_PREFERENCE`が33なので35になります。  
    ```cpp
    typedef enum DWMWINDOWATTRIBUTE {
        DWMWA_NCRENDERING_ENABLED,
        DWMWA_NCRENDERING_POLICY,
        DWMWA_TRANSITIONS_FORCEDISABLED,
        //...
        DWMWA_WINDOW_CORNER_PREFERENCE = 33,
        DWMWA_BORDER_COLOR,
        DWMWA_CAPTION_COLOR,
        //...
    } ;
    ```
1. 見つからない場合は、
    * MinGW-w64を利用する場合  
        1. [MinGW-w64](https://www.mingw-w64.org/)を開きます。  
        1. `MingW-W64-builds`をdownloadします。  
        1. includeフォルダ（`mingw32\i686-w64-mingw32\include`）のみ解凍します。
        1. 解凍先フォルダをVSCodeで開いて調べたい値を検索します。
    * VSCodeとWindowsSDKを利用する場合  
        1. `C:\Program Files (x86)\Windows Kits\10\Include`を[VSCode](https://azure.microsoft.com/ja-jp/products/visual-studio-code)で開きます。  
        1. 検索(`Ctrl+Shift+F`)で調べたい値を検索します。  
        1. 必要に応じて調べたい値を選択して`F12`を押して定義に移動します。  


# 型

[整数など](#整数など) / [ブーリアン](#ブーリアン) / [文字列](#文字列)  
[ポインタ、配列](#ポインタ配列) / [コールバック関数へのポインタ](#コールバック関数へのポインタ) / [任意の型へのポインタ](#任意の型へのポインタ)  
[構造体、共用体](#構造体共用体)  

## 型の調べ方

1. 型の定義（範囲）を確認します。  
    [Windows Data Types (BaseTsd.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types)  
    例えば、`DWORD`であれば、  
    1. `DWORD`を検索します。 
    1. `typedef unsigned long DWORD;`のため`unsigned long`と同義。  
    1. [Data Type Ranges | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170)を確認します。   
    1. `unsigned long`は4バイトで 0 ～ 4,294,967,295

    例えば、`WPARAM`であれば、  
    1. `WPARAM`を検索します。  
    1. `typedef UINT_PTR WPARAM;`のため`UINT_PTR`と同義。  
    1. `UINT_PTR`を検索します。  
    1. 32ビットか64ビットかによって定義が変わります。  
        34bitであれば`unsigned int`なので0 ～ 4,294,967,295  
        64bitであれば`unsigned __int64`なので0 ～ 18,446,744,073,709,551,615  

1. 対応するC#の型を確認します。  
    [整数数値型 - C# リファレンス - C# | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/integral-numeric-types)  
    [プラットフォーム呼び出しのデータ型](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-data-with-platform-invoke#platform-invoke-data-types)  
    例えば、`DWORD`であれば、  
    1. 0 ～ 4,294,967,295なので、`uint`  

    例えば、`WPARAM`であれば、      
    1. 34bitであれば0 ～ 4,294,967,295なので`uint`、64bitであれば0 ～ 18,446,744,073,709,551,615なので`ulong`  
    1. つまり[ポインターの有効桁数の型](https://learn.microsoft.com/ja-jp/windows/win32/learnwin32/windows-coding-conventions#pointer-precision-types)なので`UIntPtr`。  
        * [Windows のデータ型](https://learn.microsoft.com/ja-jp/previous-versions/dotnet/articles/ms973190(v=msdn.10)#data-types-in-windows)  
        * [UIntPtr 構造体 (System) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.uintptr?view=net-8.0)  

## 整数など

|データ型|C言語|C#|.NET|サイズ|範囲|
|:----|:----|:----|:----|:----|:----|
|`BYTE`<br>`UCHAR`<br>`UINT8`|`unsigned char`|`byte`|`System.Byte`|1(8)|0 ～ 255|
|`CHAR`|`char`|`sbyte`|`System.SByte`|1(8)|-128 ～ 127|
|`INT8`|`signed char`|`sbyte`|`System.SByte`|1(8)|-128 ～ 127|
|`SHORT`|`short`|`short`|`System.Int16`|2(16)|-32,768 ～ 32,767|
|`INT16`|`signed short`|`short`|`System.Int16`|2(16)|-32,768 ～ 32,767|
|`ATOM`<br>`WORD`<br>`USHORT`<br>`UINT16`|`unsigned short`|`ushort`|`System.UInt16`|2(16)|0 ～ 65,535|
|`INT`|`int`|`int`|`System.Int32`|4(32)|-2,147,483,648<br>～ 2,147,483,647|
|`INT32`<br>`LONG32`|`signed int`|`int`|`System.Int32`|4(32)|-2,147,483,648<br>～ 2,147,483,647|
|`LONG`|`long`|`int`|`System.Int32`|4(32)|-2,147,483,648<br>～ 2,147,483,647|
|`UINT`<br>`UINT32`<br>`DWORD32`<br>`ULONG32`|`unsigned int`|`uint`|`System.UInt32`|4(32)|0 ～ 4,294,967,295|
|`DWORD`<br>`ULONG`|`unsigned long`|`uint`|`System.UInt32`|4(32)|0 ～ 4,294,967,295|

`int`は処理系依存（[基本データ型](https://manabu.quu.cc/up/3/e31510.htm)）などを意識する必要はなく4バイト固定です。  

次のデータ型は常にポインターのサイズ  
|データ型|C言語|C#|.NET|サイズ|
|:----|:----|:----|:----|:----|
|`INT_PTR`|`int` or<br>`__int64`|`nint`|`IntPtr`|4(32) or<br>8(64)|
|`LPARAM`<br>`LRESULT`<br>`LONG_PTR`|`long` or<br>`__int64`|`nint`|`IntPtr`|4(32) or<br>8(64)|
|`WPARAM`<br>`UINT_PTR`|`unsigned int` or<br>`unsigned __int64`|`nuint`|`UIntPtr`|4(32) or<br>8(64)|
|`SIZE_T`<br>`ULONG_PTR`|`unsigned long` or<br>`unsigned __int64`|`nuint`|`UIntPtr`|4(32) or<br>8(64)|

## ブーリアン

|データ型|C言語|C#|.NET|サイズ|
|:----|:----|:----|:----|:----|
|`BOOLEAN`|`unsigned char`|`byte` or<br>`[MarshalAs(UnmanagedType.U1)] bool`|`System.Byte`|1(8)|
|`BOOL`|`int`|`int` or `bool` or<br>`[MarshalAs(UnmanagedType.Bool)] bool`|`System.Int32`|4(32)|

[ブール値と相互運用機能](https://learn.microsoft.com/ja-jp/dotnet/fundamentals/runtime-libraries/system-boolean#booleans-and-interop)によると、構造体内の`bool`とP/Invokeのパラメータの`bool`の既定は4バイト整数値になります。但し、移植される可能性がある規則（[非移植規則](https://learn.microsoft.com/ja-jp/visualstudio/code-quality/fxcop-unported-rules?view=vs-2019)）の中に[CA1414](https://learn.microsoft.com/ja-jp/visualstudio/code-quality/ca1414?view=vs-2019&tabs=csharp)があるため、`BOOLEAN`、`BOOL`いずれであっても`MarshalAs`で明示するほうがよいと思います。  

```cs
//1バイトBOOLEAN
public byte b;
[MarshalAs(UnmanagedType.U1)] public bool b;//明示してboolで使用

//4バイトBOOL
//既定は4バイトBOOL
public bool b;

//明示せずintで使用
public int b;

//明示して使用
[MarshalAs(UnmanagedType.Bool)] public bool b;

//明示して使用（戻り値の例）
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EmptyClipboard();
```

**参考URL**

* [C#とC++の相互運用のための属性の設定方法 | C# プログラミング解説](https://so-zou.jp/software/tech/programming/c-sharp/cpp/attribute.htm#marshal-as)  
* [ブール値フィールドのマーシャリングのカスタマイズ](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/customize-struct-marshalling#customizing-boolean-field-marshalling)  
* [一般的な Windows のデータ型](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/best-practices#common-windows-data-types)  
* [レガシ コード分析のルール セットを構成する - Visual Studio (Windows) | Microsoft Learn](https://learn.microsoft.com/ja-jp/visualstudio/code-quality/how-to-configure-code-analysis-for-a-managed-code-project?view=vs-2019)  


## 文字列
|データ型 &nbsp; &nbsp; &nbsp; &nbsp;|C言語|文字列 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|C#|
|:----|:----|:----|:----|
|`PSTR`<br>`LPSTR`|`char*`|ANSI|[`GetWindowTextA`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowtexta)のようなANSI版で使われる型。Unicode版がある場合はUnicode版の関数を使用します。<br>（[末尾のA,W](#末尾のaw)を参照）<br>[`GetProcAddress`](#getprocaddress)のようにANSI文字列のみを受け付ける関数があります。その場合は以下の「**ANSI文字列の使用例**」を参照|
|`PCSTR`|`const char*`|ANSI|同上|
|`LPCSTR`|`__nullterminated const char*`|ANSI|同上|
|`PWSTR`<br>`LPWSTR`|`wchar_t*`|Unicode|以下の「**Unicode文字列の使用例**」を参照|
|`PCWSTR`<br>`LPCWSTR`|`const wchar_t*`|Unicode|同上|
|`LPTSTR`||ANSI<br>Unicode|ANSIの場合 : `LPSTR`と同義<br>Unicodeの場合 : `LPWSTR`と同義<br>つまり`LPWSTR`とみなしてよい|
|`LPCTSTR`||ANSI<br>Unicode|ANSIの場合 : `LPCSTR`と同義<br>Unicodeの場合 : `LPCWSTR`と同義<br>つまり`LPCWSTR`とみなしてよい|
|`WCHAR`|`wchar_t`|Unicode|[`char`](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/char)([`System.Char`](https://learn.microsoft.com/ja-jp/dotnet/api/system.char?view=net-8.0))|

**ANSI文字列の使用例**

以下の「Unicode文字列の使用例」において、  
1. `CharSet = CharSet.Unicode` を `CharSet = CharSet.Ansi` に変更  
1. `[MarshalAs(UnmanagedType.LPWStr)]` を `[MarshalAs(UnmanagedType.LPStr)]` に変更  

**Unicode文字列の使用例**

例）[SetWindowText](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowtextw)を使ってウィンドウのタイトル バーのテキストを変更  

`string`を利用して文字列を渡します。  
```cs
using System;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
}

private void SetText(IntPtr hWnd, string str)
{
    NativeMethods.SetWindowText(hWnd, str);
}
```
例）[GetWindowText](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowtextw)を使ってウィンドウのタイトル バーのテキストを取得  

以下のURLによると`StringBuilder`は効率が悪く、問題が起きる可能性があるため、推奨は手段1の[`ArrayPool`](https://learn.microsoft.com/ja-jp/dotnet/api/system.buffers.arraypool-1?view=net-8.0)からの`char[]`の利用のようです。効率が必須であれば直接C言語で記載すると思いますので、どちらを使用するかは問題が起きる可能性を考慮して決めればよいと思います。  

[ネイティブ相互運用性のベスト プラクティス - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/best-practices#string-parameters)  

手段1 `char[]`を利用して文字列を受け取ります。  
```cs
using System;
using System.Buffers;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
}

private string GetText(IntPtr hWnd)
{
    char[] buffer = ArrayPool<char>.Shared.Rent(256 + 1);
    int length = NativeMethods.GetWindowText(hWnd, buffer, buffer.Length);
    return new string(buffer, 0, length);
}
```

手段2 `StringBuilder`を利用して文字列を受け取ります。  
```cs
using System;
using System.Text;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int GetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpString, int nMaxCount);
}

private string GetText(IntPtr hWnd)
{
    var buffer = new StringBuilder(256);
    NativeMethods.GetWindowText(hWnd, buffer, buffer.Capacity);
    return buffer.ToString();
}
```

**参考URL**  
* [.NET TIPS Win32 APIやDLL関数に文字列や文字列バッファを渡すには？ - C# - ＠IT](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/025w32string/w32string.html)  
* [文字列に対する既定のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/default-marshalling-for-strings)  
* [文字列の操作 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/learnwin32/working-with-strings)  
* [NuGet Gallery | System.Buffers 4.5.1](https://www.nuget.org/packages/System.Buffers/)  

## ポインタ、配列

* ほとんどのポインター型名は、プレフィックス`P`または`LP`で始まります。これらは値渡しではなく、参照渡しとなります。入出力で使う場合は`ref`、出力を受け取る場合は`out`とします。  
* 参照型の配列は、既定では In パラメーターとして渡されます。呼び出し元が結果を受け取るためには、明示的に[InAttribute](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.inattribute?view=netframework-4.8)と[OutAttribute](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.outattribute?view=netframework-4.8)を適用する必要があります。
* `PWSTR`（`P`ointer to `W`ide `STR`ing）などの文字列の場合は[文字列](#文字列)を参照して設定します。  

|例|説明|C言語の例|C#の例|
|:----|:----|:----|:----|
|`LPDWORD`<br>(`LP`+`DWORD`)|`DWORD`へのポインタ|`LPDWORD word`|`ref uint word`（入出力）<br>`out uint word`（出力）|
|`LPRECT`<br>(`LP`+`RECT`)|`RECT`構造体へのポインタ|`LPRECT lpRect`|[`out RECT lpRect`](#getwindowrect)|
||整数の配列<br>整数の多次元配列<br>文字列の配列<br>構造体の配列|`int* arr`<br>`int arr[][COL_DIM]`<br>`char** arr`<br>`INPUT* arr`|`[In, Out] int[] arr`<br>`[In, Out] int[,] arr`<br>`[In, Out] string[] arr`<br>[`[In, Out] INPUT[] arr`](#sendinput)|
||整数の配列（参照）<br>サイズ変更可能|`int** ppArray`|`ref IntPtr array`|

**参考URL**  
* [さまざまな型の配列のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-different-types-of-arrays)  
* [farポインタとポインタの違い - simotin13's message](https://mcommit.hatenadiary.com/entry/2017/06/19/223237)  
* [farポインタを理解する(目指せガンダムエンジニア) - 少ないリソースを酷使する](https://t-takeda.hatenablog.com/entry/2020/12/06/224303)  
* [最近プログラムの勉強をしています「far」[near]の意味がよく... - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1312241603)  
* [メモリ モデル](https://www.tamasoft.co.jp/lc/hlp/F069.html)  
* [C言語ポインタの far と near の違い。 (FarNear - MemoWiki v5)](http://www.nilab.info/wiki/FarNear.html)  
* [3.C/C言語](https://www.cqpub.co.jp/try/kijidb/yougo/san.htm#far-p)  


## コールバック関数へのポインタ

例えば[EnumWindows](#enumwindows)の場合、  

C言語  
```cpp
BOOL EnumWindows(
  [in] WNDENUMPROC lpEnumFunc,
  [in] LPARAM      lParam
);
```
の第1引数は「コールバック関数へのポインタ」になります。端的に言えば引数が関数ということです。このような場合、C#では[デリゲート](https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/delegates/)を使用します。デリゲートはメソッドへの参照を表す型ですので、C言語でいう関数ポインタ、関数のアドレス渡しのようなものになります。  


C#  
```cs
internal static class NativeMethods
{
    //関数を渡すためにデリゲートを使用
    [return: MarshalAs(UnmanagedType.Bool)]
    public delegate bool EnumWC(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern int EnumWindows(EnumWC lpEnumFunc, IntPtr lParam);
}
```

**参考URL**  
* [デリゲート - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/sp_delegate.html)  
* [コールバック メソッドとしてのデリゲートのマーシャ リング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-a-delegate-as-a-callback-method)  
* [プラットフォーム呼び出し (P/Invoke) - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/pinvoke#invoking-managed-code-from-unmanaged-code)  
* [【C#】UIスレッド以外からUIのコントロールを操作する │ FPGA完全に理解した](https://hikotech.net/post-388/)  
* [【C#】delegate, Action, Funcについて 多分一番易しい解説 │ FPGA完全に理解した](https://hikotech.net/post-449/)  

## 任意の型へのポインタ

ウィンドウハンドル`HWND`について[型の調べ方](#型の調べ方)で確認すると、  
`typedef HANDLE HWND;`→`typedef PVOID HANDLE;`→`typedef void *PVOID;`  
つまり「任意の型へのポインタ」となります。ポインタにアドレスを確保するためには、アドレスのサイズの容量が必要です。アドレスのサイズを確保するため`IntPtr`を使用します。  

|データ型|C言語|C#|.NET|サイズ|
|:----|:----|:----|:----|:----|
|`HWND`<br>`HANDLE`<br>`HINSTANCE`<br>`PVOID`|`void * `|`nint`|`IntPtr`|4(32) or 8(64)|

また、`null`の代わりに[`IntPtr.Zero`](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.zero?view=net-8.0)を使用します。  
```cs
//nullの代わりにIntPtr.Zeroを使用する例
if(NativeMethods.GetWindow(hWnd, GW_OWNER) == IntPtr.Zero)
{
    //オーナーウィンドウでない場合の処理
}
```

**参考URL**  
* [ポインタ | C/C++ の関数における値渡し、ポインタ渡しおよび参照渡しについて](https://bi.biopapyrus.jp/cpp/syntax/pointer.html)  
* [【C言語/C++】データ型のサイズ・範囲の一覧【32bit/64bit環境】 | MaryCore](https://marycore.jp/prog/c-lang/data-type-ranges-and-bit-byte-sizes/)  
* [あなたホントに64ビットを説明できますか？：64ビットコンピューティング最前線（1/2 ページ） - ITmedia エンタープライズ](https://www.itmedia.co.jp/enterprise/articles/0506/08/news003.html)  
* [64ビットになると何が変わる？――64ビットプログラミングのデータモデル：64ビットコンピューティング最前線（1/2 ページ） - ITmedia エンタープライズ](https://www.itmedia.co.jp/enterprise/articles/0506/13/news006.html)  
* [C#でポインタを使用する方法 | C# プログラミング解説](https://so-zou.jp/software/tech/programming/c-sharp/grammar/type/pointer/#no6)  

## 構造体、共用体


* ここでは一番簡単な例で説明しますが、`SendInput`の[INPUT構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-input)のように、構造体のメンバとして更に構造体や共用体があるような入れ子の場合も同様です。（[SendInput](#sendinput)を参照）

* 詳細は下記のURLを参照。  
    [クラス、構造体、および共用体のマーシャリング - .NET Framework | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/framework/interop/marshalling-classes-structures-and-unions)  
    [構造体のマーシャリングのカスタマイズ - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/customize-struct-marshalling)  

### 構造体

[`StructLayoutAttribute`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute?view=net-8.0)で[`LayoutKind`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.layoutkind?view=net-8.0)を`Sequential`に設定します。これにより各メンバーが出現する順番でメモリ内に順次配列されます。メンバーを定義する場合はC言語と同じ順番とします。  

C言語  
```cpp
typedef struct tagRECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT, *PRECT, *NPRECT, *LPRECT;
```
C#  
```cs
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
```
### 共用体

構造体として定義したうえで、[`StructLayoutAttribute`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute?view=net-8.0)で[`LayoutKind`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.layoutkind?view=net-8.0)を`Explicit`に設定し、[FieldOffsetAttribute](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.fieldoffsetattribute?view=net-8.0)を使用することでフィールドの物理的な位置を指定します。共用体とするために`FieldOffset(0)`としています。  

C言語  
```cpp
union MYUNION
{
    int number;
    double d;
}
```
C#  
```cs
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(0)]
    public double d;
}
```

# DllImport属性

[ネイティブ相互運用性のベスト プラクティス - .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/best-practices#dllimport-attribute-settings)  
|設定|デフォルト|推奨|説明|
|:----|:----|:----|:----|
|[`PreserveSig`](#preservesig)|`true`|既定値を維持|戻り値として`HRESULT`を持つ関数の場合、<br>`true` : `HRESULT`が返ります。<br>`false` : 例外発生(戻り値は null)|
|[`SetLastError`](#setlasterror)|`false`|API によって異なります|[`Marshal.GetLastWin32Error`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error?view=net-8.0)を使用してエラー値を取得する場合は、`true`に設定|
|[`CharSet`](#charsetexactspelling)|-|文字を使うときは明示|基本今のWindowsでは`CharSet.Unicode`<br>（[末尾のA,W](#末尾のaw)参照）<br>※[`GetProcAddress`](#getprocaddress)のようにANSI文字列のみを受け付ける関数もあります。|
|[`ExactSpelling`](#charsetexactspelling)|`false`|`true`|以下をしないので少しパフォーマンスが上昇<br>`CharSet.Ansi`の場合`FunctionA`を探します。<br>`CharSet.Unicode`の場合`FunctionW`を探します。<br>（[末尾のA,W](#末尾のaw)参照）|

## PreserveSig

[DllImportAttribute.PreserveSig フィールド (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig?view=net-8.0#system-runtime-interopservices-dllimportattribute-preservesig)  

戻り値として`HRESULT`を持つ関数の受け取り方を設定します。  

`true`（既定） : 失敗した場合、`HRESULT`(`int`)として受け取ります。  

```cs
internal static class NativeMethods
{
    [DllImport("shlwapi.dll")]
    public static extern int SHAutoComplete(IntPtr hwndEdit, uint dwFlags);
}
private void ForceError(object sender, EventArgs e)
{
    int HRESULT = NativeMethods.SHAutoComplete(new IntPtr(0), 0);
    MessageBox.Show(HRESULT.ToString());
}
```

`false` : 失敗した場合、例外を発生させます。  
```cs
internal static class NativeMethods
{
    [DllImport("shlwapi.dll", PreserveSig = false)]//失敗で例外を発生させる
    public static extern void SHAutoComplete(IntPtr hwndEdit, uint dwFlags);
}

private void ForceError(object sender, EventArgs e)
{
    try
    {
        NativeMethods.SHAutoComplete(new IntPtr(0), 0);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
```

## SetLastError

[DllImportAttribute.SetLastError フィールド (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute.setlasterror?view=net-8.0#system-runtime-interopservices-dllimportattribute-setlasterror)  

エラーを取得する必要がある場合は、誤って上書きされないように`true`に設定し、他の呼び出しを行う前にエラーを取得します。  
* [Win32 APIのWin32エラー・コードを取得するには？［C#、VB］ - ＠IT](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/740win32errcode/win32errcode.html)  
* [Win32エラー・コードからエラー・メッセージを取得するには？［C#、VB］ - ＠IT](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/741win32errmsg/win32errmsg.html)  
```cs
internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);

    [DllImport("kernel32.dll")]
    public static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr Arguments);
}
private void ForceError()
{
    int ret = NativeMethods.SetWindowText(IntPtr.Zero, "");
    if (ret == 0)
    {
        //エラーコードを取得
        int errCode = Marshal.GetLastWin32Error();

        //エラーコードの表示
        MessageBox.Show($"エラーコード:{errCode:X8}");

        //エラーメッセージを取得
        StringBuilder message = new StringBuilder(256);
        NativeMethods.FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)errCode, 0, message, message.Capacity, IntPtr.Zero);

        //エラーメッセージを表示
        MessageBox.Show($"エラーメッセージ:{message}");
    }
}
```

## CharSet/ExactSpelling

[DllImportAttribute.CharSet フィールド (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute.charset?view=net-8.0#system-runtime-interopservices-dllimportattribute-charset)  
[DllImportAttribute.ExactSpelling フィールド (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute.exactspelling?view=net-8.0#system-runtime-interopservices-dllimportattribute-exactspelling)  

引数に文字列がある場合は、`Ansi`または`Unicode`を明示します。  
```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
```
上記の場合は、`Unicode`指定ですので末尾が`W`の`SetWindowTextW`が呼ばれますが、直接`SetWindowTextW`を指定することもできます。この場合は末尾`W`の関数を探索する必要はないので`ExactSpelling = true`とします。  
```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern int SetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString);
```

# 各種関数

## GetWindowText

[GetWindowTextW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowtextw)  

ウィンドウタイトルを取得します。使用例は[マウスカーソル下のウィンドウ](#マウスカーソル下のウィンドウ)を参照。  

```cs
using System.Buffers;
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpString, int nMaxCount);
```

## GetWindowTextLength

[GetWindowTextLengthW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowtextlengthw)  

ウィンドウタイトルの文字数を取得します。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowTextLength(IntPtr hWnd);
```

## GetClassName

[GetClassNameW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getclassnamew)  

ウィンドウのクラス名を取得します。使用例は[マウスカーソル下のウィンドウ](#マウスカーソル下のウィンドウ)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, [Out] char[] lpClassName, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpClassName, int nMaxCount);
```

## GetCursorPos

[GetCursorPos 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getcursorpos)  

マウス カーソルの位置を画面座標で取得します。使用例は[マウスカーソル下のウィンドウ](#マウスカーソル下のウィンドウ)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(out POINT lpPoint);
```
```cs
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int x;
    public int y;
}
```

## WindowFromPoint

[WindowFromPoint 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-windowfrompoint)  

指定したポイントを含むウィンドウへのハンドルを取得します。使用例は[マウスカーソル下のウィンドウ](#マウスカーソル下のウィンドウ)を参照。  
引数のPOINT構造体については[`GetCursorPos`](#getcursorpos)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(POINT Point);
```

## GetWindowInfo

[GetWindowInfo 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowinfo)  

指定したウィンドウに関する情報を取得します。使用例は[ウィンドウ情報の取得](#ウィンドウ情報の取得)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool GetWindowInfo(IntPtr hWnd, ref WINDOWINFO pwi);
```
```cs
//WINDOWINFO構造体
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWINFO
{
    public int cbSize;
    public RECT rcWindow;
    public RECT rcClient;
    public uint dwStyle;
    public uint dwExStyle;
    public uint dwWindowStatus;
    public uint cxWindowBorders;
    public uint cyWindowBorders;
    public ushort atomWindowType;
    public ushort wCreatorVersion;
}

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

## GetForegroundWindow

[GetForegroundWindow 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getforegroundwindow)  

フォアグラウンドウィンドウへのハンドルを取得します。使用例は[ウィンドウ領域の取得](#ウィンドウ領域の取得)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
```

## GetWindowRect

[GetWindowRect 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowrect)  

ウィンドウの領域を取得します。使用例は[ウィンドウ領域の取得](#ウィンドウ領域の取得)を参照。  

C言語  
```cpp
BOOL GetWindowRect(
  [in]  HWND   hWnd,
  [out] LPRECT lpRect //LP始まり。RECTへのポインタ
);
```
C#  
```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
```
```cs
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
```

## GetClientRect

[GetClientRect 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getclientrect)  

ウィンドウのクライアント領域の座標を取得します。使用例は[ウィンドウ領域の取得](#ウィンドウ領域の取得)を参照。  
引数のRECT構造体については[`GetWindowRect`](#getwindowrect)を参照。
```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
```

## DwmGetWindowAttribute

[DwmGetWindowAttribute 関数 (dwmapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/dwmapi/nf-dwmapi-dwmgetwindowattribute)  

ウィンドウに適用されている指定されたデスクトップ ウィンドウ マネージャー (DWM) 属性の現在の値を取得します。使用例は[ウィンドウ領域の取得](#ウィンドウ領域の取得)を参照。  

`dwAttribute`（第2引数） : 取得したい値を示すフラグ（[`DWMWINDOWATTRIBUTE`](https://learn.microsoft.com/ja-jp/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute)列挙値）  
`pvAttribute`（第3引数） : 取得値を受け取るポインタ  

例えばウィンドウ可視領域（拡張フレーム境界の四角形）を取得したい場合は、
> `DWMWA_EXTENDED_FRAME_BOUNDS`  
> `DwmGetWindowAttribute` で を使用します。 画面空間内の拡張フレーム境界の四角形を取得します。 取得された値は RECT 型です。

のため`dwAttribute`に`DWMWA_EXTENDED_FRAME_BOUNDS`を設定し`pvAttribute`に`RECT`型へのポインタを設定します。  

```cs
//第2引数をDWMWA_EXTENDED_FRAME_BOUNDSとする場合
[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(IntPtr hWnd, uint dwAttribute, out RECT pvAttribute, int cbAttribute);
```

## DwmSetWindowAttribute

[DwmSetWindowAttribute 関数 (dwmapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute)  

ウィンドウに適用されている指定されたデスクトップ ウィンドウ マネージャー (DWM) 属性の現在の値を設定します。使用例は[タイトルバーの色を変更](#タイトルバーの色を変更)を参照。  

```cs
//第2引数をDWMWA_CAPTION_COLORとする場合
[DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(IntPtr hWnd, uint dwAttribute, ref uint pvAttribute, int cbAttribute);
```

## GetWindow

[GetWindow 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindow)  

ウィンドウとのリレーションシップ (Z オーダー または所有者) を持つウィンドウへのハンドルを取得します。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("user32.dll")]
public extern static IntPtr GetWindow(IntPtr hWnd, uint uCmd);
```

## IsWindowVisible

[IsWindowVisible 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-iswindowvisible)  

ウィンドウの表示状態を取得します。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsWindowVisible(IntPtr hWnd);
```

## GetWindowThreadProcessId

[GetWindowThreadProcessId 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid)  

ウィンドウを作成したスレッドIDとプロセスIDを取得します。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
```

## GetModuleFileNameEx

[GetModuleFileNameExW 関数 (psapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw)  

指定したモジュールを含むファイルのフルパスを取得します。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("Psapi.dll", CharSet = CharSet.Unicode)]
public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpFilename, uint nSize);
```

## OpenProcess

[OpenProcess 関数 (processthreadsapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)  

プロセスオブジェクトを開きます。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
```

## CloseHandle

[CloseHandle 関数 (handleapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle)  

オブジェクトハンドルを閉じます。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  

```cs
[DllImport("Kernel32.dll")]
public static extern int CloseHandle(IntPtr hObject);
```

## EnumWindows

[EnumWindows 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-enumwindows)  

コールバック関数に渡すことによって、画面上のすべての最上位ウィンドウを列挙します。使用例は[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)を参照。  
```cs
[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool EnumWC(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
public static extern int EnumWindows(EnumWC lpEnumFunc, IntPtr lParam);
```

## GetAncestor

[GetAncestor 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getancestor)  

ウィンドウの親子階層を親側方向に遡ってウィンドウハンドル取得します。使用例は[全ての子ウィンドウを表示](#全ての子ウィンドウを表示)を参照。  

```cs
[DllImport("user32.dll")]
public extern static IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
```

## GetParent

[GetParent 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getparent)  

親ウィンドウハンドルを取得します。使用例は[全ての子ウィンドウを表示](#全ての子ウィンドウを表示)を参照。  

```cs
[DllImport("user32.dll")]
public extern static IntPtr GetParent(IntPtr hWnd);
```
## EnumChildWindows

[EnumChildWindows 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-enumchildwindows)  

子ウィンドウを列挙します。使用例は[全ての子ウィンドウを表示](#全ての子ウィンドウを表示)を参照。  

```cs
[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool EnumCW(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hWndParent, EnumCW lpEnumFunc, IntPtr lParam);
```

## SetWindowPos

[SetWindowPos 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowpos)  

ウィンドウのサイズ、位置、Z の順序を変更します。使用例は[ウィンドウの最前面化、サイズや位置の変更](#ウィンドウの最前面化サイズや位置の変更)を参照。  
```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
```

## FindWindowEx

[FindWindowExW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-findwindowexw)  

指定したクラス名とウィンドウ名と一致するウィンドウへのハンドルを取得します。 使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
```

## PostMessage

[PostMessageW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-postmessagew)  

指定されたウィンドウを作成したスレッドに関連付けられたメッセージキューにメッセージを送ります。スレッドがメッセージを処理するのを待たずに戻ります（非同期）。使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
```

## MapVirtualKey

[MapVirtualKeyW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw)  

仮想キーコードをスキャンコードまたは文字値に変換、またはスキャンコードを仮想キーコードに変換します。使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
```

## SendInput

[SendInput 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendinput)  

キーストローク、マウスの動き、ボタンのクリックを合成します。使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

C言語  
```
UINT SendInput(
  [in] UINT    cInputs,
  [in] LPINPUT pInputs,
  [in] int     cbSize
);
```
C#  
```cs
[DllImport("user32.dll")]
public static extern uint SendInput(uint cInputs, [In, Out] INPUT[] pInputs, int cbSize);
```

[構造体と共用体](#構造体共用体)を使って[INPUT構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-input)を定義します。  
```cs
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
    public uint type;
    public DUMMYUNIONNAME input;
}

[StructLayout(LayoutKind.Explicit)]
public struct DUMMYUNIONNAME
{
    [FieldOffset(0)]
    public MOUSEINPUT mi;
    [FieldOffset(0)]
    public KEYBDINPUT ki;
    [FieldOffset(0)]
    public HARDWAREINPUT hi;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
    public int dx;
    public int dy;
    public uint mouseData;
    public uint dwFlags;
    public uint time;
    public UIntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
    public ushort wVk;
    public ushort wScan;
    public uint dwFlags;
    public uint time;
    public UIntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
    public uint uMsg;
    public ushort wParamL;
    public ushort wParamH;
}
```
```cs
private const uint INPUT_MOUSE = 0;
private const uint INPUT_KEYBOARD = 1;
private const uint INPUT_HARDWARE = 2;
```

## GetMessageExtraInfo

[GetMessageExtraInfo 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getmessageextrainfo)  

現在のスレッドの追加のメッセージ情報を取得します。使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr GetMessageExtraInfo();
```

## keybd_event

[keybd_event関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-keybd_event)  

キーストロークを合成します。現在は`SendInput`に置き換わっています。使用例は[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)を参照。  

```cs
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
```

## IsWindow

[IsWindow 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-iswindow)  

ウィンドウが存在するかどうか。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsWindow(IntPtr hWnd);
```

## IsWindowEnabled

[IsWindowEnabled 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-iswindowenabled)  

ウィンドウでマウス入力およびキーボード入力が有効かどうか。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsWindowEnabled(IntPtr hWnd);
```

## IsWindowUnicode

[IsWindowUnicode 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-iswindowunicode)  

指定したウィンドウがネイティブ Unicode ウィンドウかどうか。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsWindowUnicode(IntPtr hWnd);
```

## IsIconic

[IsIconic 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-isiconic)  

指定したウィンドウが最小化状態かどうか。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsIconic(IntPtr hWnd);
```

## IsZoomed

[IsZoomed 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-iszoomed)  

指定したウィンドウが最大化状態かどうか。   

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public extern static bool IsZoomed(IntPtr hWnd);
```

## ShowCursor

[ShowCursor 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-showcursor)  

マウスカーソルを表示または非表示にします。使用例は[マウスカーソルの表示切替](#マウスカーソルの表示切替)を参照。  

```cs
[DllImport("user32.dll")]
public static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)] bool bShow);
```

## GetDC

[GetDC 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getdc)  

デバイス コンテキスを取得します。使用例は[マウスカーソル位置の色を取得](#マウスカーソル位置の色を取得)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
```

## GetPixel

[GetPixel 関数 (wingdi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/wingdi/nf-wingdi-getpixel)  

指定した座標にあるピクセルの赤、緑、青 (RGB)を取得します。使用例は[マウスカーソル位置の色を取得](#マウスカーソル位置の色を取得)を参照。  

```cs
[DllImport("Gdi32.dll")]
public static extern uint GetPixel(IntPtr hdc, int x, int y);
```

## ReleaseDC

[ReleaseDC 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-releasedc)  

デバイス コンテキストを解放します。使用例は[マウスカーソル位置の色を取得](#マウスカーソル位置の色を取得)を参照。  

```cs
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc);
```

## SetProcessDPIAware

[SetProcessDPIAware 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setprocessdpiaware)  

プロセスの既定のDPIをシステムDPI対応に設定します。使用例は[マウスカーソル位置の色を取得](#マウスカーソル位置の色を取得)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetProcessDPIAware();
```

## GetCursorInfo

[GetCursorInfo 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getcursorinfo)  

グローバル カーソルに関する情報を取得します。使用例は[スクリーンキャプチャ](#スクリーンキャプチャ)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorInfo(ref CURSORINFO pci);
```

```cs
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
    public uint cbSize;
    public uint flags;
    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int x;
    public int y;
}
```

## OpenClipboard

[OpenClipboard 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-openclipboard)  

クリップボードを開きます。使用例は[クリップボードの中身をクリア](#クリップボードの中身をクリア)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool OpenClipboard(IntPtr hWnd);
```

## EmptyClipboard

[EmptyClipboard 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-emptyclipboard)  

クリップボードを空にします。使用例は[クリップボードの中身をクリア](#クリップボードの中身をクリア)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EmptyClipboard();
```

## CloseClipboard

[CloseClipboard 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-closeclipboard)  

クリップボードを閉じます。使用例は[クリップボードの中身をクリア](#クリップボードの中身をクリア)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseClipboard();
```

## SHEmptyRecycleBin

[SHEmptyRecycleBinW 関数 (shellapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/shellapi/nf-shellapi-shemptyrecyclebinw)  

指定したドライブのごみ箱を空にします。使用例は[ごみ箱の中身をクリア](#ごみ箱の中身をクリア)を参照。  

```cs
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern uint SHEmptyRecycleBin(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), In] string pszRootPath, uint dwFlags);
```

## LoadLibrary

[LoadLibraryW 関数 (libloaderapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw)  

指定したモジュールを呼び出し元プロセスのアドレス空間に読み込みます。使用例は[DLLを動的に呼び出す](#dllを動的に呼び出す)を参照。  

```cs
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName);
```

## GetProcAddress

[GetProcAddress 関数 (libloaderapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress)  

エクスポートされた関数または変数のアドレスをDLLから取得します。引数の型はANSI文字列`LPCSTR`になります。使用例は[DLLを動的に呼び出す](#dllを動的に呼び出す)を参照。  

```cs
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
```

## FreeLibrary

[FreeLibrary 関数 (libloaderapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary)  

DLL解放し、必要に応じて参照カウントをデクリメントします。使用例は[DLLを動的に呼び出す](#dllを動的に呼び出す)を参照。  

```cs
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hLibModule);
```

## SetWindowsHookEx

[SetWindowsHookExW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexw)  

フック チェーンの先頭にフック プロシージャをインストールします。使用例は[フック](#フック)を参照。  

```cs
public delegate IntPtr SetWindowsHookExCallback(int nCode, UIntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SetWindowsHookEx(int idHook, SetWindowsHookExCallback lpfn, IntPtr hmod, uint dwThreadId);
```

## UnhookWindowsHookEx

[UnhookWindowsHookEx 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-unhookwindowshookex)  

`SetWindowsHookEx`関数によってフック チェーンにインストールされているフック プロシージャを削除します。使用例は[フック](#フック)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
```

## CallNextHookEx

[CallNextHookEx 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-callnexthookex)  

フック情報を現在のフック チェーンの次のフック プロシージャに渡します。使用例は[フック](#フック)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
```

## GetCurrentThreadId

[GetCurrentThreadId 関数 (processthreadsapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid)  

呼び出し元スレッドのスレッド識別子を取得します。使用例は[フック](#フック)を参照。  

```cs
[DllImport("Kernel32.dll")]
public static extern uint GetCurrentThreadId();
```

## GetWindowLong

[GetWindowLongW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowlongw)  

ウィンドウに関する情報を取得します。使用例は[フック](#フック)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
```

## GetWindowLongPtr

[GetWindowLongPtrW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getwindowlongptrw)  

ウィンドウに関する情報を取得します。使用例は[フック](#フック)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
```

## GetSystemMenu

[GetSystemMenu 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getsystemmenu)  

ウィンドウメニュー（システム メニュー、コントロール メニュー）のコピーへのハンドルを取得します。使用例は[ウィンドウメニューに項目を追加](#ウィンドウメニューに項目を追加)を参照。  

```cs
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, [MarshalAs(UnmanagedType.Bool)] bool bRevert);
```

## InsertMenuItem

[InsertMenuItemW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-insertmenuitemw)  

メニュー内の指定した位置に新しいメニュー項目を挿入します。使用例は[ウィンドウメニューに項目を追加](#ウィンドウメニューに項目を追加)を参照。  

```cs
[DllImport("user32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InsertMenuItemW(IntPtr hmenu, uint item, [MarshalAs(UnmanagedType.Bool)] bool fByPosition, ref MENUITEMINFO lpmi);
```
```cs
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MENUITEMINFO
{
    public uint cbSize;
    public uint fMask;
    public uint fType;
    public uint fState;
    public uint wID;
    public IntPtr hSubMenu;
    public IntPtr hbmpChecked;
    public IntPtr hbmpUnchecked;
    public UIntPtr dwItemData;
    public string dwTypeData;
    public uint cch;
    public IntPtr hbmpItem;
}
```

## SetMenuItemInfo

[SetMenuItemInfoW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setmenuiteminfow)  

メニュー項目の設定を変更します。使用例は[ウィンドウメニューに項目を追加](#ウィンドウメニューに項目を追加)を参照。  

```cs
[DllImport("user32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetMenuItemInfoW(IntPtr hmenu, uint item, [MarshalAs(UnmanagedType.Bool)] bool fByPositon, ref MENUITEMINFO lpmii);
```

## AddClipboardFormatListener

[AddClipboardFormatListener 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener)  

指定したウィンドウ ハンドルをクリップボード形式のリスナーリストに配置します。使用例は[クリップボードの監視](#クリップボードの監視)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
```

## RemoveClipboardFormatListener

[RemoveClipboardFormatListener 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-removeclipboardformatlistener)  

指定したウィンドウ ハンドルをクリップボード形式のリスナーリストから削除します。使用例は[クリップボードの監視](#クリップボードの監視)を参照。  

```cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
```

## CountClipboardFormats

[CountClipboardFormats 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-countclipboardformats)  

クリップボードに現在にあるデータ形式の数を取得します。使用例は[クリップボードの監視](#クリップボードの監視)を参照。  

```cs
[DllImport("user32.dll")]
public static extern int CountClipboardFormats();
```

## EnumClipboardFormats

[EnumClipboardFormats 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-enumclipboardformats)  

クリップボードで現在使用できるデータ形式を取得します。使用例は[クリップボードの監視](#クリップボードの監視)を参照。  

```cs
[DllImport("user32.dll")]
public static extern uint EnumClipboardFormats(uint format);
```

## GetClipboardFormatName

[GetClipboardFormatNameW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getclipboardformatnamew)  

登録済みのデータ形式名をクリップボードから取得します。使用例は[クリップボードの監視](#クリップボードの監視)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetClipboardFormatName(uint format, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpszFormatName, int cchMaxCount);
```

## GetGUIThreadInfo

[GetGUIThreadInfo 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getguithreadinfo)  

フォアグラウンド スレッドまたは指定した GUI スレッドに関する情報を取得します。使用例は[IMEをオフにする](#imeをオフにする)を参照。  

```cs
[DllImport("user32.dll")]
public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO pgui);
```

```cs
//GUITHREADINFO構造体の定義
[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
    public uint cbSize;
    public uint flags;
    public IntPtr hwndActive;
    public IntPtr hwndFocus;
    public IntPtr hwndCapture;
    public IntPtr hwndMenuOwner;
    public IntPtr hwndMoveSize;
    public IntPtr hwndCaret;
    public RECT rcCaret;
}

//RECT構造体の定義
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
```

## ImmGetDefaultIMEWnd

[ImmGetDefaultIMEWnd 関数 (imm.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/imm/nf-imm-immgetdefaultimewnd)  

IME クラスのデフォルトのウィンドウハンドルを取得します。使用例は[IMEをオフにする](#imeをオフにする)を参照。  

```cs
[DllImport("Imm32.dll")]
public static extern IntPtr ImmGetDefaultIMEWnd(IntPtr unnamedParam1);
```

## SendMessage

[SendMessageW 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendmessagew)  

指定したメッセージをウィンドウに送ります。スレッドがメッセージを処理するのを待ちます（同期）。使用例は[IMEをオフにする](#imeをオフにする)を参照。  

```cs
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SendMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
```

# 使用例

## はじめに

ここでの使用例は、  
* 動作環境 Windows11 + Visual Studio 2022  
* ソースコードの最初に`//Windows フォーム アプリケーション(.NET Framewrok)`と書かれているものは、  
    1. Visual Studio 2022を起動し、「新しいプロジェクトの作成」  
    1. 「Windows フォーム アプリケーション(.NET Framewrok)」を選択し次へ  
    1. プロジェクト名「WindowsFormsApp1」、フレームワーク「.NET Framework 4.8」として次へ  
    1. Form1.csを右クリックして「コードの表示」  
    1. すべてのコードを記載のソースコードに置き換え  
    1. `x64`指定のあるものは構成マネージャでプラットフォームを`x64`に
    1. 「F5」でデバッグ（実行）  

## マウスカーソル下のウィンドウ
1秒ごとにマウスカーソル下のウィンドウのタイトルとクラス名を取得してテキストボックスに表示します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //ウィンドウタイトルを取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpString, int nMaxCount);

            //クラスの名前を取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetClassName(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpClassName, int nMaxCount);

            //マウス カーソルの位置を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetCursorPos(out POINT lpPoint);

            //指定したポイントを含むウィンドウへのハンドルを取得
            [DllImport("user32.dll")]
            public static extern IntPtr WindowFromPoint(POINT Point);
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //---------- Win32API用定義終了 ----------

        //出力用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            Dock = DockStyle.Fill,
            ScrollBars = ScrollBars.Both,
            WordWrap = false
        };

        //タイマー
        Timer timer = new Timer();

        public Form1()
        {
            InitializeComponent();

            TopMost = true;
            Controls.Add(textBox);

            //1秒毎に動作
            timer.Enabled = true;
            timer.Interval = 1000;
            timer.Tick += (sender, e) =>
            {
                //マウスカーソル座標取得
                NativeMethods.GetCursorPos(out POINT point);

                //マウスカーソル下のウィンドウ取得
                IntPtr hWnd;
                hWnd = NativeMethods.WindowFromPoint(point);

                if (hWnd != IntPtr.Zero)
                {
                    //ウィンドウタイトル取得
                    StringBuilder windowTitle = new StringBuilder(256);
                    NativeMethods.GetWindowText(hWnd, windowTitle, windowTitle.Capacity);

                    //クラス名取得
                    StringBuilder className = new StringBuilder(256);
                    NativeMethods.GetClassName(hWnd, className, className.Capacity);

                    //出力
                    textBox.Text = windowTitle.ToString() + Environment.NewLine + className.ToString();
                }
            };

            FormClosing += (sender, e) =>
            {
                timer.Stop();
                timer.Dispose();
            };
        }
    }
}
```

**参考URL**  

* [参照渡し - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/sp_ref.html#out-var)  

## ウィンドウ情報の取得

自分自身のウィンドウ情報を取得します。  
```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //指定したウィンドウに関する情報を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public extern static bool GetWindowInfo(IntPtr hWnd, ref WINDOWINFO pwi);
        }

        //WINDOWINFO構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        internal struct WINDOWINFO
        {
            public int cbSize;
            public RECT rcWindow;
            public RECT rcClient;
            public uint dwStyle;
            public uint dwExStyle;
            public uint dwWindowStatus;
            public uint cxWindowBorders;
            public uint cyWindowBorders;
            public ushort atomWindowType;
            public ushort wCreatorVersion;
        }

        //RECT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        internal struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        //定数の定義
        public const uint WS_ICONIC = 0x20000000;
        public const uint WS_MAXIMIZE = 0x01000000;
        public const uint WS_VISIBLE = 0x10000000;
        public const uint WS_EX_TOPMOST = 0x00000008;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            //最前面、最小化で起動
            TopMost = true;
            this.WindowState = FormWindowState.Minimized;

            Shown += (sender, e) =>
            {
                //自分自身のハンドル取得
                IntPtr hWnd = Handle;

                //ウィンドウ情報取得
                WINDOWINFO windowinfo = new WINDOWINFO();
                windowinfo.cbSize = Marshal.SizeOf(windowinfo);
                NativeMethods.GetWindowInfo(hWnd, ref windowinfo);

                //情報抽出
                bool winmin = (windowinfo.dwStyle & WS_ICONIC) == WS_ICONIC;//最小化
                bool winmax = (windowinfo.dwStyle & WS_MAXIMIZE) == WS_MAXIMIZE;//最大化
                bool winvisible = (windowinfo.dwStyle & WS_VISIBLE) == WS_VISIBLE;//表示
                bool wintop = (windowinfo.dwExStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST;//最前面化

                //表示用
                StringBuilder output = new StringBuilder();
                output.AppendLine($"最小化:{winmin}");
                output.AppendLine($"最大化:{winmax}");
                output.AppendLine($"表示:{winvisible}");
                output.AppendLine($"最前面化:{wintop}");

                //表示
                MessageBox.Show(output.ToString());
            };
        }
    }
}
```

## ウィンドウ領域の取得

フォアグラウンドウィンドウのウィンドウ領域、ウィンドウ領域（可視領域）、クライアント領域のサイズを表示。ウィンドウの可視領域のサイズの取得のため`DwmGetWindowAttribute`を使用しています。  
```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //フォアグラウンドウィンドウのウィンドウハンドルを取得
            [DllImport("user32.dll")]
            public static extern IntPtr GetForegroundWindow();

            //ウィンドウの領域を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

            //ウィンドウのクライアント領域を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

            //デスクトップ ウィンドウ マネージャー (DWM) 属性を取得
            //ウィンドウ可視領域を取得するために使用
            [DllImport("dwmapi.dll")]
            public static extern int DwmGetWindowAttribute(IntPtr hWnd, uint dwAttribute, out RECT pvAttribute, int cbAttribute);
        }

        //RECT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        //定数の定義
        private const uint DWMWA_EXTENDED_FRAME_BOUNDS = 9;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Shown += (sender, e) =>
            {
                //フォアグラウンドウィンドウのハンドル取得
                IntPtr hWnd = NativeMethods.GetForegroundWindow();

                //取得できた場合
                if (hWnd != IntPtr.Zero)
                {
                    //サイズ取得
                    NativeMethods.GetWindowRect(hWnd, out RECT windowSize);
                    NativeMethods.GetClientRect(hWnd, out RECT clientSize);
                    NativeMethods.DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out RECT windowVisibleSize, Marshal.SizeOf(typeof(RECT)));

                    //表示用
                    StringBuilder sb = new StringBuilder();

                    sb.Append("ウィンドウサイズ:");
                    sb.Append($"{windowSize.right - windowSize.left}");
                    sb.Append(" x ");
                    sb.AppendLine($"{windowSize.bottom - windowSize.top}");

                    sb.Append("ウィンドウサイズ(可視領域):");
                    sb.Append($"{windowVisibleSize.right - windowVisibleSize.left}");
                    sb.Append(" x ");
                    sb.AppendLine($"{windowVisibleSize.bottom - windowVisibleSize.top}");

                    sb.Append("クライアントサイズ:");
                    sb.Append($"{clientSize.right - clientSize.left}");
                    sb.Append(" x ");
                    sb.AppendLine($"{clientSize.bottom - clientSize.top}");

                    //表示
                    MessageBox.Show(sb.ToString());
                }
            };
        }
    }
}
```

**参考URL**  

7pixelの隙間ができる  
* [Re1: フォームの表示がおかしいです。](http://bbs.wankuma.com/index.cgi?mode=al2&namber=92428&KLOG=159)  
   回答：その隙間はほぼ透明ではあるがフォームのボーダー的な扱いで、例えばフォームのサイズ変更ができます。また下に見えているウィンドウへのクリックになりません。  
* [フォームを並べて動かしています。Windows10では幅が変わってフォームの間隔が空いてしまいます。](https://social.msdn.microsoft.com/Forums/ja-JP/24a29e45-afde-448a-9958-d2fb2dfd4183/windows10?forum=vsgeneralja)  
* [Retrieving size of Windows 10 shadowing border - Stack Overflow](https://stackoverflow.com/questions/31920709/retrieving-size-of-windows-10-shadowing-border)  

## タイトルバーの色を変更

自身のタイトルバー(キャプション)とそのテキストの色を変更（Windows 11 ビルド 22000 以降）  
<!--
Hyper-V上のWindows11で動作確認をしています。  
* [Windows10 Pro で Hyper-V を有効にする方法 | GENESIS BLOG](https://genesis-tech.jp/blog/how-to-enable-hyperv/)  
* [Download a Windows virtual machine - Windows app development | Microsoft Developer](https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/)  
* [WindowsでHyper-Vの仮想マシンを作成する：Tech TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/0808/22/news131.html)  
-->

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //デスクトップ ウィンドウ マネージャー (DWM) 属性を設定
            [DllImport("dwmapi.dll")]
            public static extern int DwmSetWindowAttribute(IntPtr hWnd, uint dwAttribute, ref uint pvAttribute, int cbAttribute);
        }

        //定数の定義
        private uint DWMWA_CAPTION_COLOR = 35;
        private uint DWMWA_TEXT_COLOR = 36;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Shown += (sender, e) =>
            {
                uint color;

                //タイトルバー(キャプション)の色を変更
                color = 0x00277FFF;//BGR
                NativeMethods.DwmSetWindowAttribute(Handle, DWMWA_CAPTION_COLOR, ref color, Marshal.SizeOf(typeof(uint)));

                //タイトルバーのテキスト(キャプションテキスト)の色を変更
                color = 0x00FF0000;//BGR
                NativeMethods.DwmSetWindowAttribute(Handle, DWMWA_TEXT_COLOR, ref color, Marshal.SizeOf(typeof(uint)));
            };
        }
    }
}
```

## 全ウィンドウの情報を表示

表示状態にある全ウィンドウの情報を表示します。  

`EnumWindows`を使用することで画面上のすべての最上位ウィンドウを列挙できます。`EnumWindows`は、各ウィンドウハンドルを順番に、`EnumWindows`の第1引数の関数、下記の例では`EnumWindowsProc`に渡します。そのため`EnumWindowsProc`内に各ウィンドウハンドルの処理内容を記載することで、すべての最上位ウィンドウを順番に処理できます。なお、`EnumWindowsProc`が`false`を戻した場合はその時点で処理が終了します。  

下記の例では、タイトルがあってかつ表示されているオーナーウィンドウだけを抽出し、ウィンドウハンドル、プロセスID、スレッドID、タイトル、クラス名、ファイルパスを表示しています。  

1. `IsWindowVisible`を使用してウィンドウが表示されているか確認します。  
1. `GetWindow`を使用してオーナーウィンドウか確認します。  
1. 表示されていてかつオーナーウィンドウの場合は、`GetWindowThreadProcessId`でプロセスID、スレッドIDを、`GetWindowText`でタイトルを取得します。  
1. タイトルがある場合は、更に`GetClassName`でクラス名を取得します。  
1. 取得した情報から出力文字列を作成してテキストボックスに出力します。  
1. このときファイルパスも取得しています。取得する処理はユーザ定義関数`FilePathFromProcess`にまとめています。  
    1. [`OpenProcess`](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)を使用してプロセスオブジェクトを開きます。この後[`GetModuleFileNameEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw)でファイルパスを取得しますが[`PROCESS_QUERY_LIMITED_INFORMATION`アクセス権のみが必要](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw#parameters)なので、そのアクセス権のみ指定します。  
        * [OpenProcess 関数 (processthreadsapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)  
        * [プロセス ハンドルと識別子 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/procthread/process-handles-and-identifiers)  
        * [セキュリティとアクセス権の処理 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/procthread/process-security-and-access-rights)  
    1. [`GetModuleFileNameEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw#parameters)でファイルパスを取得します。  
        * [GetModuleFileNameExW 関数 (psapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw)  
        * [ウィンドウハンドルから実行ファイル名を取得する - 趣味グラマ。ゆとり。そっちの話だけ。](https://yu-hr.hatenadiary.org/entry/20100323/1269356160)  
        * [追加のプロセス情報の取得 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/procthread/obtaining-additional-process-information)  
    1. `CloseHandle`でプロセスオブジェクトを閉じます。  
        * [CloseHandle 関数 (handleapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle)  
        * [OpenProcess 関数 (processthreadsapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess#remarks)  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //EnumWindowsのコールバック関数用のデリゲート
            [return: MarshalAs(UnmanagedType.Bool)]
            public delegate bool EnumWC(IntPtr hWnd, IntPtr lParam);

            //画面上のすべての最上位ウィンドウを列挙
            [DllImport("user32.dll")]
            public static extern int EnumWindows(EnumWC lpEnumFunc, IntPtr lParam);

            //ウィンドウとのリレーションシップ (Z オーダー または所有者) を持つウィンドウへのハンドルを取得
            //オーナーウィンドウの判定に使用
            [DllImport("user32.dll")]
            public extern static IntPtr GetWindow(IntPtr hWnd, uint uCmd);

            //ウィンドウの表示状態を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public extern static bool IsWindowVisible(IntPtr hWnd);

            //ウィンドウタイトルを取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpString, int nMaxCount);

            //クラスの名前を取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetClassName(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpClassName, int nMaxCount);

            //ウィンドウを作成したスレッドIDとプロセスIDを取得
            [DllImport("user32.dll")]
            public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

            //指定したモジュールを含むファイルのフルパスを取得
            [DllImport("Psapi.dll", CharSet = CharSet.Unicode)]
            public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpFilename, uint nSize);

            //プロセスオブジェクトを開く
            [DllImport("Kernel32.dll", SetLastError = true)]
            public static extern IntPtr OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);

            //プロセスオブジェクトを閉じる
            [DllImport("Kernel32.dll")]
            public static extern int CloseHandle(IntPtr hObject);
        }

        //定数の定義
        private const uint GW_OWNER = 4;
        private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;

        //---------- Win32API用定義終了 ----------

        //プロセスIDからファイルパスを取得
        private string FilePathFromProcessId(uint processId)
        {
            string ret = "";

            //プロセスオブジェクトを開く
            IntPtr hProcess = NativeMethods.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId);

            if (hProcess != IntPtr.Zero)
            {
                //ファイルパスを取得
                StringBuilder lpFilename = new StringBuilder(256);
                NativeMethods.GetModuleFileNameEx(hProcess, IntPtr.Zero, lpFilename, (uint)lpFilename.Capacity);

                ret = lpFilename.ToString();

                //オブジェクトハンドルを閉じる
                NativeMethods.CloseHandle(hProcess);
            }

            return ret;
        }

        //EnumWindowsのコールバック関数
        public bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
        {
            bool winvisible = NativeMethods.IsWindowVisible(hWnd);
            bool owner = NativeMethods.GetWindow(hWnd, GW_OWNER) == IntPtr.Zero;
            if (winvisible && owner)
            {
                //スレッドIDとプロセスID取得
                uint threadId = NativeMethods.GetWindowThreadProcessId(hWnd, out uint processId);

                //タイトル取得
                StringBuilder winTitle = new StringBuilder(256);
                NativeMethods.GetWindowText(hWnd, winTitle, winTitle.Capacity);

                if (winTitle.ToString() != "")
                {
                    //クラス名取得
                    StringBuilder className = new StringBuilder(256);
                    NativeMethods.GetClassName(hWnd, className, className.Capacity);

                    //出力情報作成
                    StringBuilder output = new StringBuilder();
                    output.AppendLine($"hWnd:0x{hWnd.ToString("X8")}");//ウィンドウハンドル
                    output.AppendLine($"PID:{processId}");//プロセスID
                    output.AppendLine($"ThreadID:{threadId}");//スレッドID
                    output.AppendLine(winTitle.ToString());//タイトル
                    output.AppendLine(className.ToString());//クラス名
                    output.AppendLine(FilePathFromProcessId(processId));//ファイルパス
                    output.AppendLine("");

                    //出力
                    textBox.AppendText(output.ToString());
                }
            }

            return true;
        }

        //出力用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            Dock = DockStyle.Fill,
            ScrollBars = ScrollBars.Both,
            WordWrap = false
        };

        public Form1()
        {
            InitializeComponent();

            Controls.Add(textBox);

            NativeMethods.EnumWindows(EnumWindowsProc, IntPtr.Zero);
        }
    }
}
```

`System.Diagnostics.Process`を使用する場合は、  

[32ビットプロセスは、64ビットプロセスのモジュールにアクセスできません - kk_AtakaのScrapbox](https://scrapbox.io/gosyujin/32%E3%83%93%E3%83%83%E3%83%88%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AF%E3%80%8164%E3%83%93%E3%83%83%E3%83%88%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%9B%E3%82%93)  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
//ビルドで「32 ビットを選ぶ」のチェックをオフ
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        internal static class NativeMethods
        {
            //クラスの名前を取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetClassName(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpClassName, int nMaxCount);

            //ウィンドウを作成したスレッドIDとプロセスIDを取得
            [DllImport("user32.dll")]
            public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
        }

        TextBox textBox;

        public Form1()
        {
            InitializeComponent();

            textBox = new TextBox
            {
                Multiline = true,
                Dock = DockStyle.Fill,
                ScrollBars = ScrollBars.Both,
                WordWrap = false
            };
            Controls.Add(textBox);

            foreach (Process process in Process.GetProcesses())
            {
                if (process.MainWindowTitle.Length != 0 && process.MainWindowHandle != IntPtr.Zero)
                {
                    IntPtr hWnd = process.MainWindowHandle;
                    uint threadId = NativeMethods.GetWindowThreadProcessId(hWnd, out int processId);

                    //クラス名取得
                    StringBuilder className = new StringBuilder(256);
                    NativeMethods.GetClassName(hWnd, className, className.Capacity);

                    StringBuilder output = new StringBuilder();

                    output.AppendLine($"hWnd:0x{hWnd.ToString("X8")}");//ウィンドウハンドル
                    output.AppendLine($"PID:{processId}");//プロセスID
                    output.AppendLine($"ThreadID:{threadId}");//スレッドID
                    output.AppendLine(process.MainWindowTitle);//タイトル
                    output.AppendLine(className.ToString());//クラス名
                    output.AppendLine(process.MainModule.FileName);//ファイルパス
                    output.AppendLine("");

                    textBox.AppendText(output.ToString());
                }
            }
        }
    }
}
```

## 全ての子ウィンドウを表示

1秒ごとにマウスカーソル下のアプリケーションのウィンドウ階層を表示します。また、現在その階層のどこにマウスカーソルがあるかも表示します。例えばメモ帳でテキストを入力する部分にマウスカーソルがある場合は、  

```
タイトルなし - メモ帳/Notepad
   DesktopWindowXamlSource/Windows.UI.Composition.DesktopWindowContentBridge
      /Windows.UI.Input.InputSite.WindowClass
   DesktopWindowXamlSource/Windows.UI.Composition.DesktopWindowContentBridge
      /Windows.UI.Input.InputSite.WindowClass
   /NotepadTextBox
      /RichEditD2DPT ← ■マウスカーソル下のウィンドウ■
   DesktopWindowXamlSource/Windows.UI.Core.CoreWindow
   DesktopWindowXamlSource/Windows.UI.Composition.DesktopWindowContentBridge
      /Windows.UI.Input.InputSite.WindowClass
   DesktopWindowXamlSource/Windows.UI.Composition.DesktopWindowContentBridge
      /Windows.UI.Input.InputSite.WindowClass
   DesktopWindowXamlSource/Windows.UI.Composition.DesktopWindowContentBridge
      /Windows.UI.Input.InputSite.WindowClass
...
```

といった形で表示します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //マウス カーソルの位置を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetCursorPos(out POINT lpPoint);

            //指定したポイントを含むウィンドウへのハンドルを取得
            [DllImport("user32.dll")]
            public static extern IntPtr WindowFromPoint(POINT Point);

            //指定したウィンドウの先祖へのハンドルを取得
            [DllImport("user32.dll")]
            public extern static IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);

            //EnumChildWindowsのコールバック関数用のデリゲート
            [return: MarshalAs(UnmanagedType.Bool)]
            public delegate bool EnumCW(IntPtr hWnd, IntPtr lParam);

            //すべての子ウィンドウを列挙
            [DllImport("user32.dll")]
            public static extern int EnumChildWindows(IntPtr hWndParent, EnumCW lpEnumFunc, IntPtr lParam);

            //親ウィンドウハンドルを取得
            [DllImport("user32.dll")]
            public extern static IntPtr GetParent(IntPtr hWnd);

            //ウィンドウタイトルを取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpString, int nMaxCount);

            //クラスの名前を取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetClassName(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpClassName, int nMaxCount);
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //定数の定義
        private const uint GA_ROOT = 2;

        //---------- Win32API用定義終了 ----------

        //出力用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            Dock = DockStyle.Fill,
            ScrollBars = ScrollBars.Both,
            WordWrap = false
        };

        //タイマー
        Timer timer = new Timer();

        //マウスカーソル下のウィンドウ
        IntPtr hWnd;

        //すべての子ウィンドウ
        List<IntPtr> childrenhWnd;

        //すべての子ウィンドウを取得
        public bool EnumChildProc(IntPtr hWnd, IntPtr lParam)
        {
            childrenhWnd.Add(hWnd);
            return true;
        }

        //階層化用クラス
        class stratify
        {
            public IntPtr Parent { get; set; }//親ウィンドウ
            public List<stratify> Children { get; set; }//子ウィンドウ

            public stratify(IntPtr parent)
            {
                Parent = parent;
                Children = new List<stratify>();
            }
        }

        //ウィンドウの階層化
        void SetChildren(stratify window)
        {
            //直下の子ウィンドウを取得
            foreach (IntPtr hWnd in childrenhWnd)
            {
                if (NativeMethods.GetParent(hWnd) == window.Parent)
                {
                    window.Children.Add(new stratify(hWnd));
                }
            }

            //直下の子ウィンドウの子ウィンドウを取得
            foreach (stratify child in window.Children)
            {
                SetChildren(child);
            }
        }

        //ウィンドウ階層を表示
        void ViewChildren(stratify window, int level)
        {
            //ウィンドウタイトル取得
            StringBuilder windowTitle = new StringBuilder(256);
            NativeMethods.GetWindowText(window.Parent, windowTitle, windowTitle.Capacity);

            //クラス名取得
            StringBuilder className = new StringBuilder(256);
            NativeMethods.GetClassName(window.Parent, className, className.Capacity);

            //表示
            textBox.Text += "".PadLeft(level * 3, ' ')//階層に応じて文頭にスペース追加
                + windowTitle.ToString() + "/" + className.ToString()
                + (hWnd == window.Parent ? " ← ■マウスカーソル下のウィンドウ■" : "")
                + Environment.NewLine;

            //子ウィンドウ情報も表示
            foreach (var child in window.Children)
            {
                ViewChildren(child, level + 1);
            }
        }

        public Form1()
        {
            InitializeComponent();

            TopMost = true;
            Controls.Add(textBox);

            //1秒毎に動作
            timer.Enabled = true;
            timer.Interval = 1000;
            timer.Tick += (sender, e) =>
            {
                //初期化
                hWnd = IntPtr.Zero;
                textBox.Text = "";
                childrenhWnd = new List<IntPtr>();

                //マウスカーソル座標取得
                NativeMethods.GetCursorPos(out POINT point);

                //マウスカーソル下のウィンドウ取得
                hWnd = NativeMethods.WindowFromPoint(point);

                if (hWnd != IntPtr.Zero)
                {
                    //ルートウィンドウ取得
                    IntPtr roothWnd = NativeMethods.GetAncestor(hWnd, GA_ROOT);
                    if (roothWnd != IntPtr.Zero)
                    {
                        //すべての子ウィンドウ取得
                        NativeMethods.EnumChildWindows(roothWnd, EnumChildProc, IntPtr.Zero);

                        //ウィンドウの階層化
                        stratify kaisou = new stratify(roothWnd);
                        SetChildren(kaisou);

                        //ウィンドウ階層を表示
                        ViewChildren(kaisou, 0);
                    }
                }
            };

            FormClosing += (sender, e) =>
            {
                timer.Stop();
                timer.Dispose();
            };
        }
    }
}
```

## ウィンドウの最前面化、サイズや位置の変更

起動した`winver.exe`を最前面化します。  
```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //ウィンドウのサイズ、位置、Zオーダーを変更
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        }

        //定数の定義
        private const int SWP_NOMOVE = 0x0002;//現在位置を保持（X,Yパラメーターを無視）。
        private const int SWP_NOSIZE = 0x0001;//現在のサイズを保持（cx,cyパラメーターを無視）。
        private const int HWND_NOTOPMOST = -2;//最前面の背後
        private const int HWND_TOPMOST = -1;//最前面

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            using (Process process = new Process())
            {
                //winver.exeを起動
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.FileName = @"C:\Windows\System32\winver.exe";
                process.Start();
                process.WaitForInputIdle(3000);

                //最前面化する
                NativeMethods.SetWindowPos(process.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

                //位置指定
                //NativeMethods.SetWindowPos(process.MainWindowHandle, HWND_NOTOPMOST, 100, 300, 0, 0, SWP_NOSIZE);

                //サイズ指定
                //NativeMethods.SetWindowPos(process.MainWindowHandle, HWND_NOTOPMOST, 0, 0, 200, 300, SWP_NOMOVE);
            }
        }
    }
}
```
**参考URL**

エクスプローラではうまくいかないようです。Windows11のメモ帳でもうまくいかないようです。  
* [[Delphi:91190] Explorer.exe を CreateProcess した場合の、Explorer.exe のウインドウハンドルの取得](https://groups.google.com/g/delphicbuilder-ml-archive/c/7jZ3b0cGp0o)  
* [Windowsスマートチューニング(413) Win 10編: エクスプローラーにプロセスIDを示す文字列を追加する | マイナビニュース](https://news.mynavi.jp/article/windows-413/)  
* [ツールを使わずに、アプリの起動位置を指定したい - Microsoft コミュニティ](https://answers.microsoft.com/ja-jp/windows/forum/all/%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E4%BD%BF/9bc3e225-3dc3-41b5-97b6-25a11eaa2c35)  

## 非アクティブウィンドウにキーを送信

ボタンを押したらメモ帳にメッセージを送信（POST）します。  

**AキーDOWNメッセージを送信し、文字「a」を入力**  

[キーストロークをウインドウに送信 | C#](https://tarukichi.chu.jp/codetips/sendkeys.html)  

1. [FindWindowEx](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-findwindowexw)で子ウィンドウを検索します。第1引数が`NULL`の場合はデスクトップウィンドウを親ウィンドウとして検索します。`NULL`(`IntPtr.Zero`)を設定することで、デスクトップ上のメモ帳を探します。  
1. 更にメモ帳の文字を入力するところ（子ウィンドウ`NotepadTextBox`の更に子ウィンドウの`RichEditD2DPT`）を検索します。メモ帳の子ウィンドウについては[全ての子ウィンドウを表示](#全ての子ウィンドウを表示)を参照。  
1. `PostMessage`でAキーDOWNメッセージを送信します。  
    1. 第1引数`hWnd`には対象のウィンドウハンドル（`RichEditD2DPT`子ウィンドウ）を指定します。  
    1. 第2引数`Msg`には投稿するメッセージを指定します。今回はAキーDOWNメッセージを送るため[`WM_KEYDOWN`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-keydown)を指定します。[`WM_KEYDOWN`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-keydown#parameters)によると`wParam`に[仮想キーコード](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/virtual-key-codes)、`lParam`に[キーストローク メッセージ フラグ](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags)を指定します。  
    1. 第3引数`wParam`には[Keys列挙型](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.keys?view=windowsdesktop-8.0)を利用して`A`キーの仮想キーコードを設定します。  
    1. 第4引数`lParam`の情報は[通常必要ない](https://learn.microsoft.com/ja-jp/windows/win32/learnwin32/keyboard-input#key-down-and-key-up-messages)ため`0`を指定します。  
1. AキーDOWNメッセージだけでは、実際にキーが押されたままになるわけではないため、AキーUPメッセージは送信しません。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //指定したクラス名とウィンドウ名と一致するウィンドウハンドルを取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

            //メッセージを送信（非同期）
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
        }

        //定数の定義
        private const uint WM_KEYDOWN = 0x0100;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Width = Height = 200;

            //メモ帳を起動しておく
            using (Process process = new Process())
            {
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.FileName = @"C:\Windows\System32\notepad.exe";
                process.Start();
                process.WaitForInputIdle(3000);
            }

            //ボタン
            Button button = new Button { Text = "Send key" };
            Controls.Add(button);

            //ボタンを押したとき
            button.Click += (sender, e) =>
            {
                IntPtr hWnd = IntPtr.Zero;

                //メモ帳を探す
                hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "Notepad", null);
                if (hWnd == IntPtr.Zero) return;

                //NotepadTextBoxクラス名を探す
                hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "NotepadTextBox", null);
                if (hWnd == IntPtr.Zero) return;

                //RichEditD2DPTクラス名を探す
                hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "RichEditD2DPT", null);
                if (hWnd == IntPtr.Zero) return;

                //キー送信
                //AキーDOWNメッセージを送信
                NativeMethods.PostMessage(hWnd, WM_KEYDOWN, (UIntPtr)Keys.A, (IntPtr)0);
            };
        }
    }
}
```

第4引数`lParam`の情報をきちんと作成して送信する場合は、[`WM_KEYDOWN`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-keydown#parameters)を見て、  

|bit|値|内容|
|:----|:----|:----|
|31|0|0固定|
|30|0|前のキーの状態。メッセージが送信される前にキーが<br>・ダウンしている場合1<br>・アップしている場合0|
|29|0|0固定|
|25-28(4bit)|0|未使用|
|24|0|拡張キーでない場合は0|
|16-23(8bit)|※|送信するキーの[スキャンコード](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89)（1バイト）<br>※[`MapVirtualKey`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw)を使用して取得|
|0-15|1|メッセージの繰り返し数なので1|

`MapVirtualKey`と定数を定義して、  
```cs
//Windows APIの定義に追加
//仮想キーコードをスキャンコードに変換
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern uint MapVirtualKey(uint uCode, uint uMapType);

//定数の定義に追加
private const uint MAPVK_VK_TO_VSC = 0;
```
`lParam`を作成して送信します。  
```cs
//キー送信
uint keycode, scancode, flag;

//仮想キーコード
keycode = (uint)Keys.A;

//仮想キーコードからスキャンコードを取得
scancode = NativeMethods.MapVirtualKey(keycode, MAPVK_VK_TO_VSC);

//キーストローク メッセージ フラグ
flag = 1;//繰り返し数
flag += scancode << 16;//スキャンコード 下位が16bit

//AキーDOWNメッセージを送信
NativeMethods.PostMessage(hWnd, WM_KEYDOWN, (UIntPtr)keycode, (IntPtr)flag);
```

**Alt+Hメッセージを送信し、ヘルプメニューを表示**  

`WM_KEYDOWN`ではなく[`WM_SYSKEYDOWN`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-syskeydown)を使用します。`Alt`を押しながら`H`を押したいので、[`lParam`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-syskeydown#parameters)のbit29を1にします。  

```cs
//定数の定義に追加
private const uint WM_SYSKEYDOWN = 0x0104;

//キー送信
//Alt+Hメッセージを送信
NativeMethods.PostMessage(hWnd, WM_SYSKEYDOWN, (UIntPtr)Keys.H, (IntPtr)(1 << 29));
```

**Ctrl+Oを送信し、ファイルを開くダイアログを表示（keybd_event）**  

1. [`keybd_event`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-keybd_event)を使って送信前に`Ctrl`を押します。  
1. `WM_KEYDOWN`で`O`を送信します。  
1. 送信後`Ctrl`を離します。  

```cs
//Windows APIの定義に追加
//キーストロークを合成
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

//定数の定義に追加
private const uint KEYEVENTF_KEYUP = 2;

//キー送信
//Ctrlキーを押す
NativeMethods.keybd_event((byte)Keys.ControlKey, 0, 0, (UIntPtr)0);

//OキーDOWNメッセージを送信
NativeMethods.PostMessage(hWnd, WM_KEYDOWN, (UIntPtr)Keys.O, (IntPtr)0);

//Ctrlキーを離す
NativeMethods.keybd_event((byte)Keys.ControlKey, 0, KEYEVENTF_KEYUP, (UIntPtr)0);
```

**Ctrl+Oを送信し、ファイルを開くダイアログを表示（SendInput）**  

`keybd_event`関数は現在は`SendInput`に置き換わっています。`SendInput`を使用する場合は、  
1. `SendInput`の引数作成のときに使用する以下の関数を定義します。  
    * `GetMessageExtraInfo` : 現在のスレッドの追加のメッセージ情報を取得。  
    * `MapVirtualKey` : 仮想キーコードからスキャンコードに変換  
1. [`SendInput`](#sendinput)の引数である`INPUT`構造体を定義します。  
    コード上の`INPUT構造体の定義（ここから）`から`INPUT構造体の定義（ここまで）`  
1. 必要な定数を定義します。  
1. あとは[`SendInput`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendinput)のページを見て引数を作成して使用します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //指定したクラス名とウィンドウ名と一致するウィンドウハンドルを取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

            //メッセージを送信（非同期）
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);

            //キーストローク、マウスの動き、ボタンのクリックを合成
            [DllImport("user32.dll")]
            public static extern uint SendInput(uint cInputs, [In, Out] INPUT[] pInputs, int cbSize);

            //現在のスレッドの追加のメッセージ情報を取得
            [DllImport("user32.dll")]
            public static extern IntPtr GetMessageExtraInfo();

            //仮想キーコードをスキャンコードに変換
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern uint MapVirtualKey(uint uCode, uint uMapType);
        }

        //INPUT構造体の定義（ここから）
        [StructLayout(LayoutKind.Sequential)]
        public struct INPUT
        {
            public uint type;
            public DUMMYUNIONNAME input;
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct DUMMYUNIONNAME
        {
            [FieldOffset(0)]
            public MOUSEINPUT mi;
            [FieldOffset(0)]
            public KEYBDINPUT ki;
            [FieldOffset(0)]
            public HARDWAREINPUT hi;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MOUSEINPUT
        {
            public int dx;
            public int dy;
            public uint mouseData;
            public uint dwFlags;
            public uint time;
            public UIntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public UIntPtr dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct HARDWAREINPUT
        {
            public uint uMsg;
            public ushort wParamL;
            public ushort wParamH;
        }
        //INPUT構造体の定義（ここまで）

        //定数の定義
        private const uint INPUT_KEYBOARD = 1;
        private const uint MAPVK_VK_TO_VSC = 0;
        private const uint WM_KEYDOWN = 0x0100;
        private const uint KEYEVENTF_KEYUP = 2;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Width = Height = 200;

            //メモ帳を起動しておく
            using (Process process = new Process())
            {
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.FileName = @"C:\Windows\System32\notepad.exe";
                process.Start();
                process.WaitForInputIdle(3000);
            }

            //ボタン
            Button button = new Button { Text = "Send key" };
            Controls.Add(button);

            //ボタンを押したとき
            button.Click += (sender, e) =>
            {
                IntPtr hWnd = IntPtr.Zero;

                //メモ帳を探す
                hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "Notepad", null);
                if (hWnd == IntPtr.Zero) return;

                //キー送信

                //Ctrlキー
                INPUT input = new INPUT { type = INPUT_KEYBOARD };
                input.input.ki.wVk = (ushort)Keys.ControlKey;
                input.input.ki.wScan = (ushort)NativeMethods.MapVirtualKey((uint)Keys.ControlKey, MAPVK_VK_TO_VSC);
                input.input.ki.time = 0;
                input.input.ki.dwExtraInfo = (UIntPtr)NativeMethods.GetMessageExtraInfo().ToInt32();

                //Ctrlキーを押す
                input.input.ki.dwFlags = 0;
                NativeMethods.SendInput(1, new[] { input }, Marshal.SizeOf(typeof(INPUT)));

                //OキーDOWNメッセージを送信
                NativeMethods.PostMessage(hWnd, WM_KEYDOWN, (UIntPtr)Keys.O, (IntPtr)0);

                //Ctrlキーを離す
                input.input.ki.dwFlags = KEYEVENTF_KEYUP;
                NativeMethods.SendInput(1, new[] { input }, Marshal.SizeOf(typeof(INPUT)));
            };
        }
    }
}
```

**参考URL**

* [【C#】SendMessageとPostMessage | 育児パパの人生備忘録](https://t19488sns.com/sendmessagepostmessage/397/)  
* [マウスやキーボードの自動操作 - 七誌の開発日記](https://7shi.hateblo.jp/entry/20090526/1243307160)  

## マウスカーソルの表示切替

Alt+Bでボタンを押すたびにマウスカーソルの表示を切り替えます。  

マウスカーソルを表示するかどうかを決定する内部表示カウントは、  
* 初期値は0  
* `ShowCursor(true)`で+1  
* `ShowCursor(false)`で-1  
* 0以上の場合に、マウスカーソルを表示  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //マウスカーソルの表示切替
            [DllImport("user32.dll")]
            public static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)] bool bShow);
        }

        //---------- Win32API用定義終了 ----------

        bool show = true;//初期は表示状態

        public Form1()
        {
            InitializeComponent();

            //ボタン
            Button button = new Button { Text = "&B" };
            Controls.Add(button);

            //ボタンを押す(Alt+B)たびにマウスカーソルの表示切替
            button.Click += (sender, e) =>
            {
                show = !show;
                NativeMethods.ShowCursor(show);
            };
        }
    }
}
```

## マウスカーソル位置の色を取得

1秒ごとにマウスカーソル位置の色を取得し、フォームの背景色に設定します。  

1. Windowsの「拡大と縮小のレイアウト」の設定が「100%」でない場合、正しく動作しません。そのため、ウィンドウ作成前に`SetProcessDPIAware`を使用してプロセス既定のDPI認識をシステムDPI対応にします。以下のURLに記載の通り、この方法は推奨されませんが、使用例としての使用だけですので使用しています。  
    * [Windows 10でディスプレイの表示スケールの設定を変更して見やすくする：Tech TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/1912/05/news019.html)  
    * [C++で色取得する方法について以下のプログラムを実行すると明らかに異なる色が... - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13265823048)  
    * [DPI 認識モード](https://learn.microsoft.com/ja-jp/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows?redirectedfrom=MSDN#dpi-awareness-mode)  
    * [プログラムによる既定の認識の設定](https://learn.microsoft.com/ja-jp/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-programmatically)  
1. `GetDC`の引数に`NULL`（`IntPtr.Zero`）を指定し、画面全体のデバイス コンテキストを取得します。  
1. `GetCursorPos`、`GetPixel`を使用して、デバイス コンテキストからマウスカーソル位置の色情報を取得します。`GetPixel`の戻り値が色情報です。  
1. `GetDC`で取得したデバイス コンテキストを`ReleaseDC`で解放します。  
1. `GetPixel`の戻り値は4バイトの[`COLORREF`](https://learn.microsoft.com/ja-jp/windows/win32/gdi/colorref)で上位から順に`0固定` `B` `G` `R`となっているため、1つずつ抽出しています。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //デバイス コンテキスを取得
            [DllImport("user32.dll")]
            public static extern IntPtr GetDC(IntPtr hWnd);

            //デバイス コンテキストを解放
            [DllImport("user32.dll")]
            public static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc);

            //マウス カーソルの位置を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetCursorPos(out POINT lpPoint);

            //指定した座標にあるピクセルの赤、緑、青 (RGB)を取得
            [DllImport("Gdi32.dll")]
            public static extern uint GetPixel(IntPtr hdc, int x, int y);

            //システムDPI対応に設定
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetProcessDPIAware();
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //---------- Win32API用定義終了 ----------

        Timer timer = new Timer();
        IntPtr hdc = IntPtr.Zero;

        public Form1()
        {
            InitializeComponent();

            //フォームのサイズ
            Height = 100;
            Width = 400;

            //システムDPI対応
            NativeMethods.SetProcessDPIAware();

            //1秒毎に動作
            Shown += (sender, e) =>
            {
                timer.Enabled = true;
                timer.Interval = 1000;
            };
            timer.Tick += (sender, e) =>
            {
                //画面全体のデバイス コンテキストを取得
                hdc = NativeMethods.GetDC(IntPtr.Zero);

                //マウスカーソル座標取得
                NativeMethods.GetCursorPos(out POINT point);

                //マウスカーソル位置の色を取得
                int color = (int)NativeMethods.GetPixel(hdc, point.x, point.y);

                //デバイス コンテキストを解放
                if (NativeMethods.ReleaseDC(IntPtr.Zero, hdc) == 1)
                {
                    hdc = IntPtr.Zero;
                }

                //RGBを取得
                byte blue = (byte)((color >> 16) & 0xFF);
                byte green = (byte)((color >> 8) & 0xFF);
                byte red = (byte)(color & 0xFF);

                //背景色とタイトル変更
                BackColor = Color.FromArgb(red, green, blue);
                Text = $"R{red},G{green},B{blue}";
            };

            FormClosing += (sender, e) =>
            {
                timer.Stop();
                timer.Dispose();
                //デバイス コンテキストが解放されていない場合は解放
                if (hdc != IntPtr.Zero)
                {
                    NativeMethods.ReleaseDC(IntPtr.Zero, hdc);
                }
            };
        }
    }
}
```

**参考URL**  

* [描画処理 - Windows API 入門](http://kaitei.net/winapi/gdi-drawing/)  

## スクリーンキャプチャ

1秒ごとにプライマリモニタのスクリーンをキャプチャし、実行ファイルと同じフォルダに保存します。マウスカーソルもあわせてキャプチャします。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //グローバルカーソルに関する情報を取得
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetCursorInfo(ref CURSORINFO pci);
        }

        //CURSORINFO構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct CURSORINFO
        {
            public uint cbSize;
            public uint flags;
            public IntPtr hCursor;
            public POINT ptScreenPos;
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //---------- Win32API用定義終了 ----------

        //タイマー
        Timer timer = new Timer();

        public Form1()
        {
            InitializeComponent();

            //保存先
            string exefilepath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string directory = Path.GetDirectoryName(exefilepath) + @"\";

            //1秒毎に動作
            timer.Enabled = true;
            timer.Interval = 1000;
            timer.Tick += (sender, e) =>
            {
                string filepath = directory + DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".png";
                CaptureRectangle(Screen.PrimaryScreen.Bounds, filepath);
            };

            FormClosing += (sender, e) =>
            {
                timer.Stop();
                timer.Dispose();
            };
        }

        //領域キャプチャと保存
        private void CaptureRectangle(Rectangle rectangle, string filepath)
        {
            using (Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height))
            {
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    //領域キャプチャ
                    graphics.CopyFromScreen(rectangle.Location, new Point(0, 0), bitmap.Size);

                    //マウスカーソル取得
                    CURSORINFO cursorinfo = new CURSORINFO();
                    cursorinfo.cbSize = (uint)Marshal.SizeOf(cursorinfo);
                    if (NativeMethods.GetCursorInfo(ref cursorinfo))
                    {
                        Cursor cursor = new Cursor(cursorinfo.hCursor);
                        Point hotSpot = cursor.HotSpot;
                        Point cursorScreenPosition = Cursor.Position;

                        //マウスカーソルを描画
                        graphics.DrawIcon(Icon.FromHandle(cursorinfo.hCursor), cursorScreenPosition.X - hotSpot.X - rectangle.X, cursorScreenPosition.Y - hotSpot.Y - rectangle.Y);
                    }

                    //保存
                    bitmap.Save(filepath, System.Drawing.Imaging.ImageFormat.Png);
                }
            }
        }
    }
}
```

**参考URL**  

* [C#またはDelphiでカーソル付きでスクリーンキャプチャし、ちょっ… - 人力検索はてな](https://q.hatena.ne.jp/1213429698)  
* [C#でスクショを撮る時にカーソル付きで撮るには？ - C＃のCopy... - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14108984594)  

## クリップボードの中身をクリア

ボタンを押すとクリップボードの中身をクリアします。  

[クリップボードのデータをクリア - プログラミングのメモ帳(C/C++/HSP)](https://blog.goo.ne.jp/masaki_goo_2006/e/7df61e047740541665d9ebfde0547756)  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool OpenClipboard(IntPtr hWnd);

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EmptyClipboard();

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseClipboard();
        }

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Button button = new Button { Text = "実行" };
            button.Click += (sender, e) => ClearClip();
            Controls.Add(button);
        }

        private void ClearClip()
        {
            if (NativeMethods.OpenClipboard(IntPtr.Zero))
            {
                if (NativeMethods.EmptyClipboard())
                {
                    MessageBox.Show("クリップボードをクリアしました。");
                }
                else
                {
                    MessageBox.Show("クリップボードのクリアに失敗しました。");
                }
                NativeMethods.CloseClipboard();
            }
        }
    }
}
```

## ごみ箱の中身をクリア

ボタンを押すとごみ箱を空にします。  

[ごみ箱のクリア - プログラミングのメモ帳(C/C++/HSP)](https://blog.goo.ne.jp/masaki_goo_2006/e/0913071bc00376babecb42a60f50da15)  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
            public static extern uint SHEmptyRecycleBin(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr), In] string pszRootPath, uint dwFlags);
        }

        //定数の定義
        private const uint SHERB_NOCONFIRMATION = 0x00000001;//オブジェクトの削除を確認するダイアログ ボックスは表示されません。
        private const uint SHERB_NOPROGRESSUI = 0x00000002;//進行状況を示すダイアログ ボックスは表示されません。
        private const uint SHERB_NOSOUND = 0x00000004;//操作が完了すると、サウンドは再生されません。
        private const uint S_OK = 0;

        //---------- Win32API用定義終了 ----------

        public Form1()
        {
            InitializeComponent();

            Button button = new Button { Text = "実行" };
            button.Click += (sender, e) => ClearDust();
            Controls.Add(button);
        }

        private void ClearDust()
        {
            uint result = NativeMethods.SHEmptyRecycleBin(IntPtr.Zero, "", SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND);

            if (result == S_OK)
            {
                MessageBox.Show("ごみ箱を空にしました。");
            }
            else
            {
                MessageBox.Show("ごみ箱を空にできませんでした。");
            }
        }
    }
}
```

## DLLを動的に呼び出す

[C#でLoadLibraryを使用してアンマネージDLLを使用する - 閑古鳥](https://www.little-cuckoo.jp/entry/20070919/1190166134)  

`User32.dll`内の[`MessageBoxW`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-messageboxw)を動的に呼び出して使用します。  

1. [`LoadLibrary`](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw)を使用してDLLを読み込みます。（[ダイナミック リンク ライブラリの検索順序](https://learn.microsoft.com/ja-jp/windows/win32/dlls/dynamic-link-library-search-order)を参照）  
1. [`GetProcAddress`](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress)を使用してDLL内の関数へのポインタを取得します。この関数の引数は`LPCSTR`なのでANSI文字列になります。  
1. [`Marshal.GetDelegateForFunctionPointer`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=net-8.0)を使用して関数ポインタをデリゲートに変換します。    
1. 変換したデリゲートを使って関数を呼びます。  
1. [`FreeLibrary`](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary)でDLLを解放します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //DLL読み込み
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName);

            //DLL解放
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool FreeLibrary(IntPtr hLibModule);

            //DLL内の関数を取得
            [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
            public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
        }

        //---------- Win32API用定義終了 ----------

        //MessageBoxW用のdelegate
        public delegate int delegateMessageBox(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpText, [MarshalAs(UnmanagedType.LPWStr)] string lpCaption, uint uType);

        public Form1()
        {
            InitializeComponent();

            //メッセージ表示用ボタン
            Button button = new Button { Text = "button" };

            //ボタンが押されたときにMessageBoxWを使ってメッセージを表示
            button.Click += (sender, e) =>
            {
                //DLL読み込み
                IntPtr hModule = NativeMethods.LoadLibrary("user32");
                if (hModule == IntPtr.Zero) return;

                //MessageBoxW関数へのポインタ
                IntPtr funcPtr = NativeMethods.GetProcAddress(hModule, "MessageBoxW");
                if (hModule != IntPtr.Zero)
                {
                    //アンマネージ関数ポインタをデリゲートに変換
                    delegateMessageBox MessageBox = (delegateMessageBox)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(delegateMessageBox));

                    //関数の実行
                    MessageBox(IntPtr.Zero, "message", "title", 0);
                }

                //DLL解放
                NativeMethods.FreeLibrary(hModule);
            };

            Controls.Add(button);
        }
    }
}
```

**自作したDLLをアプリケーションと同じ場所において呼び出す**  

呼ばれる側のDLLをC言語で作成します。ここでは単純に引数の2つの数を足したものを返す関数を作ります。まずは`addfunc.cpp`を以下のように作成し、`addfunc.dll`を作成します。  


[MinGW+C言語でWindows用Dllを作成する #Windows - Qiita](https://qiita.com/nekotadon/items/3da07aa1d20017f9bd16)  

```cpp
/*
g++ -shared -o addfunc.dll addfunc.cpp
*/
#include <windows.h>

extern "C" __declspec(dllexport) int __stdcall AddXY(short x, short y)
{
	return x + y;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	return TRUE;
}
```
C#で呼び出します。DLLと同じ`x64`でビルドします。  
```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //DLL読み込み
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName);

            //DLL解放
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool FreeLibrary(IntPtr hLibModule);

            //DLL内の関数を取得
            [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
            public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
        }

        //---------- Win32API用定義終了 ----------

        //AddXY用のdelegate
        public delegate int delegateAddFunc(short x, short y);

        public Form1()
        {
            InitializeComponent();

            //結果表示用ボタン
            Button button = new Button { Text = "button" };

            //ボタンが押されたときにAddXYの結果を表示
            button.Click += (sender, e) =>
            {
                //実行ファイルのパス
                string filepath = System.Reflection.Assembly.GetExecutingAssembly().Location;

                //DLLファイルのパス
                string dllpath = System.IO.Path.GetDirectoryName(filepath) + @"\addfunc.dll";

                //DLLの存在確認
                if (!System.IO.File.Exists(dllpath)) return;

                //DLL読み込み
                IntPtr hModule = NativeMethods.LoadLibrary(dllpath);
                if (hModule == IntPtr.Zero) return;

                //AddXY関数へのポインタ
                IntPtr funcPtr = NativeMethods.GetProcAddress(hModule, "AddXY");
                if (hModule != IntPtr.Zero)
                {
                    //アンマネージ関数ポインタをデリゲートに変換
                    delegateAddFunc AddXY = (delegateAddFunc)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(delegateAddFunc));

                    //関数の実行
                    int ret = AddXY(3, 5);

                    //結果の表示
                    MessageBox.Show(ret.ToString());
                }

                //DLL解放
                NativeMethods.FreeLibrary(hModule);
            };

            Controls.Add(button);
        }
    }
}
```

項目の趣旨からは外れますが、もちろん`DllImport`を使用しても構いません。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        internal static class NativeMethods
        {
            [DllImport("addfunc.dll", EntryPoint = "AddXY")]
            public static extern int AddXY(short x, short y);
        }

        public Form1()
        {
            InitializeComponent();
            MessageBox.Show(NativeMethods.AddXY(3, 5).ToString());
        }
    }
}
```

**参考URL**  

* [C/C++で実装したDLLをC#から呼び出す(1)遅延ロードをしてみる | いくぞ！田舎在住技術職！！](https://country-programmer.dfkp.info/2021/12/load_native_dll_001/)  
* [動的リンクのRun-Time - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/dlls/run-time-dynamic-linking)  

## フック

### キーボードフック（固有スレッド）

[フックの概要 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/about-hooks)  

[`SetWindowsHookEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexw)を使用して、キーボードフックを実装します。正確性よりもイメージを優先すると、  

`SetWindowsHookEx(idHook, lpfn, hmod, dwThreadId)`  

|パターン| idHook|lpfn|hmod|dwThreadId|
|:----|:----|:----|:----|:----|
||フックの種類|フック関数|フック関数を含む DLL へのハンドル|フックを関連付けるスレッド|
|`#1`|種類を指定|DLL内の関数|DLL へのハンドル|0 ※下記参照|
|`#2`|種類を指定|DLL内の関数|DLL へのハンドル|別のスレッド|
|`#3`|種類を指定|現在のコード内の関数で可|`NULL`(`IntPtr.Zero`)|現在のスレッド|

※0の場合、呼び出し元のスレッドと同じデスクトップで実行されているすべての既存のスレッド  

となります。今回はDLLの作成はしませんので、現在のスレッド（自分自身）に対するフック（`#3`）をしてみます。
|フックの種類|スコープ|
|:----|:----|
|`WH_CALLWNDPROC` `WH_CALLWNDPROCRET` `WH_CBT` `WH_DEBUG` `WH_FOREGROUNDIDLE` `WH_GETMESSAGE` `WH_KEYBOARD` `WH_MOUSE` `WH_MSGFILTER` `WH_SHELL`|スレッドまたはグローバル|
|`WH_KEYBOARD_LL` `WH_MOUSE_LL` `WH_SYSMSGFILTER`|グローバルのみ|

ここでは`WH_KEYBOARD`を例にして作成します。  
> WH_KEYBOARD 2  
> キーストローク メッセージを監視するフック プロシージャをインストールします。 詳細については、 KeyboardProc フックの手順に関するページを参照してください。  


1. [`SetWindowsHookEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexw)を使用してフック プロシージャをインストールします。  
    1. 第1引数にはインストールするフック プロシージャの種類である`WH_KEYBOARD`を指定します。  
    1. 第2引数にはフック プロシージャへのポインタ（メッセージを処理する関数）を指定します。下記の例では`HookProc`になります。  
    1. 第3引数には`NULL`(`IntPtr.Zero`)を設定します。  
    1. 第4引数には[`GetCurrentThreadId`](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid)で取得した、呼び出し元のスレッドID（＝自身のスレッドID）を設定します。  
1. メッセージを受け取ったときに処理する関数、下記の場合だと`HookProc`を実装します。ここでは単純に受け取ったキーメッセージを表示するだけとしています。[`KeyboardProc`](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/keyboardproc)のページを確認し、    
    1. `nCode`が0未満の場合は、何もせず[`CallNextHookEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-callnexthookex)を使用して次のフックプロシージャにイベントを渡します。`nCode=0`の場合のみ処理をします。  
    1. `wParam`は仮想キーコードになるので[`Keys 列挙型`](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.keys?view=windowsdesktop-8.0)を使って文字列に変換しています。  
        * [【C#入門】enumの値を文字列に変換する方法(属性の定義も解説) | 侍エンジニアブログ](https://www.sejuku.net/blog/42539)  
        * [【C#】いろんなパディング（空白埋めやゼロ埋め） | Black Everyday Company](https://kuroeveryday.blogspot.com/2014/03/padding.html)  
    1. `lParam`は[`キーストローク メッセージ フラグ`](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags)になります。`WM_KEYDOWN`や`WM_KEYUP`などを確認すれば分かりますが項目ごとにbitの区分がありますので、`-`で区切って`0-0-0-0000-0-00011110-0000000000000001`のように変換します。  
        * [WM_KEYDOWN メッセージ (Winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-keydown#parameters)  
        * [WM_KEYUP メッセージ (Winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/wm-keyup#parameters)  
        * [.NET TIPS 2進数文字列と16進数文字列を相互に変換するには？ - C# - ＠IT](https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/057convhex/convhex.html)  
    1. 今回は単純にメッセージを表示するだけなので、次のフックチェーンにメッセージを渡すために、[`CallNextHookEx`](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-callnexthookex)を呼んで戻します。  
1. 終了時に[UnhookWindowsHookEx](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-unhookwindowshookex)を使用してインストールしたフック プロシージャを削除しています。  

キーを押すたびに「キー文字列」と「キーストローク メッセージ フラグ」を表示します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //フックコールバック関数
            public delegate IntPtr SetWindowsHookExCallback(int nCode, UIntPtr wParam, IntPtr lParam);

            //フックの設定
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr SetWindowsHookEx(int idHook, SetWindowsHookExCallback lpfn, IntPtr hmod, uint dwThreadId);

            //フックの解除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(IntPtr hhk);

            //フック情報を次のフックプロシージャに渡す
            [DllImport("user32.dll")]
            public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

            //自身のスレッドID取得
            [DllImport("Kernel32.dll")]
            public static extern uint GetCurrentThreadId();
        }

        //定数の定義
        private const int WH_KEYBOARD = 2;//キーボードフック
        private const int HC_ACTION = 0;

        //---------- Win32API用定義終了 ----------

        //フック プロシージャ
        IntPtr HookProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode == HC_ACTION)
            {
                //wParam 仮想キーコード
                textBox.AppendText(((Keys)wParam.ToUInt32()).ToString().PadRight(12, ' '));

                //lParam キーストローク メッセージ フラグ
                textBox.AppendText(KeyMsgFlgFromUInt32(lParam) + Environment.NewLine);
            }

            return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }

        //キーストローク メッセージ フラグを2進数にして区切りを入れる
        private string KeyMsgFlgFromUInt32(IntPtr lParam)
        {
            string ret = "";

            //2進数に変換
            int num = (int)(IntPtr.Size == 4 ? lParam.ToInt32() : lParam.ToInt64());
            string strbin = Convert.ToString(num, 2).PadLeft(32, '0');

            //区切りの挿入
            int[] split = { 1, 2, 3, 7, 8, 16 };
            for (int i = 0; i < strbin.Length; i++)
            {
                ret += (split.Contains(i) ? "-" : "") + strbin[i];
            }

            return ret;
        }

        IntPtr hookPtr = IntPtr.Zero;

        //メッセージ表示用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            WordWrap = false,
            ScrollBars = ScrollBars.Both,
            Top = 30,
            Left = 10,
            Width = 500,
            Height = 300,
            Font = new System.Drawing.Font("ＭＳ ゴシック", 9F)//等幅
        };

        public Form1()
        {
            InitializeComponent();

            //キー入力用テキストボックス
            TextBox txt = new TextBox { Top = 5, Left = 10, Width = 100 };
            Controls.Add(txt);

            //メッセージ表示用テキストボックス
            Controls.Add(textBox);

            //起動時にフック開始
            Shown += (sender, e) =>
            {
                hookPtr = NativeMethods.SetWindowsHookEx(WH_KEYBOARD, HookProc, IntPtr.Zero, NativeMethods.GetCurrentThreadId());
            };

            //終了時にフック解除
            FormClosing += (sender, e) =>
            {
                NativeMethods.UnhookWindowsHookEx(hookPtr);
                hookPtr = IntPtr.Zero;
            };
        }
    }
}
```

### マウスフック（固有スレッド）

基本的に[キーボードフック（固有スレッド）](#キーボードフック固有スレッド)と同じです。  

`lParam`が[MOUSEHOOKSTRUCT構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-mousehookstruct)へのポインタになるので[`Marshal.PtrToStructure`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.ptrtostructure?view=net-8.0#system-runtime-interopservices-marshal-ptrtostructure(system-intptr-system-type))を使用してポインタから構造体に変換しています。  

マウス操作のたびに「操作内容」と「マウスカーソルの位置」を表示します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //フックコールバック関数
            public delegate IntPtr SetWindowsHookExCallback(int nCode, UIntPtr wParam, IntPtr lParam);

            //フックの設定
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr SetWindowsHookEx(int idHook, SetWindowsHookExCallback lpfn, IntPtr hmod, uint dwThreadId);

            //フックの解除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(IntPtr hhk);

            //フック情報を次のフックプロシージャに渡す
            [DllImport("user32.dll")]
            public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

            //自身のスレッドID取得
            [DllImport("Kernel32.dll")]
            public static extern uint GetCurrentThreadId();
        }

        //MOUSEHOOKSTRUCT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct MOUSEHOOKSTRUCT
        {
            public POINT pt;
            public IntPtr hwnd;
            public uint wHitTestCode;
            public UIntPtr dwExtraInfo;
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //マウス メッセージの識別子(の一部)
        public enum WM
        {
            MOUSEMOVE = 0x0200,
            LBUTTONDOWN = 0x0201,
            LBUTTONUP = 0x0202,
            LBUTTONDBLCLK = 0x0203,
            RBUTTONDOWN = 0x0204,
            RBUTTONUP = 0x0205,
            RBUTTONDBLCLK = 0x0206,
            MBUTTONDOWN = 0x0207,
            MBUTTONUP = 0x0208,
            MBUTTONDBLCLK = 0x0209,
            MOUSEWHEEL = 0x020A,
            NCMOUSEMOVE = 0x00A0,
            NCLBUTTONDOWN = 0x00A1,
            NCLBUTTONUP = 0x00A2,
            NCLBUTTONDBLCLK = 0x00A3,
            NCRBUTTONDOWN = 0x00A4,
            NCRBUTTONUP = 0x00A5,
            NCRBUTTONDBLCLK = 0x00A6,
            NCMBUTTONDOWN = 0x00A7,
            NCMBUTTONUP = 0x00A8,
            NCMBUTTONDBLCLK = 0x00A9
        }

        //定数の定義
        private const int WH_MOUSE = 7;//マウスフック
        private const int HC_ACTION = 0;

        //---------- Win32API用定義終了 ----------

        //フック プロシージャ
        IntPtr HookProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode == HC_ACTION)
            {
                //wParam マウス メッセージの識別子
                textBox.AppendText(((WM)wParam.ToUInt32()).ToString().PadRight(16, ' '));

                //lParam MOUSEHOOKSTRUCT 構造体へのポインタ
                MOUSEHOOKSTRUCT mouse = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT));
                textBox.AppendText(mouse.pt.x.ToString() + "," + mouse.pt.y.ToString() + Environment.NewLine);
            }

            return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }

        IntPtr hookPtr = IntPtr.Zero;

        //メッセージ表示用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            WordWrap = false,
            ScrollBars = ScrollBars.Both,
            Top = 10,
            Left = 10,
            Width = 500,
            Height = 300,
            Font = new System.Drawing.Font("ＭＳ ゴシック", 9F)//等幅
        };

        public Form1()
        {
            InitializeComponent();

            //メッセージ表示用テキストボックス
            Controls.Add(textBox);

            //起動時にフック開始
            Shown += (sender, e) =>
            {
                hookPtr = NativeMethods.SetWindowsHookEx(WH_MOUSE, HookProc, IntPtr.Zero, NativeMethods.GetCurrentThreadId());
            };

            //終了時にフック解除
            FormClosing += (sender, e) =>
            {
                NativeMethods.UnhookWindowsHookEx(hookPtr);
                hookPtr = IntPtr.Zero;
            };
        }
    }
}
```

**参考URL**  

* [MouseProc コールバック関数 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/mouseproc)  
* [MOUSEHOOKSTRUCT (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-mousehookstruct)  
* [マウス入力通知 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/inputdev/mouse-input-notifications)  
* [Marshal.PtrToStructure メソッド (System.Runtime.InteropServices) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.ptrtostructure?view=net-8.0#system-runtime-interopservices-marshal-ptrtostructure(system-intptr-system-type))  
* [C#のためのC++の配列、構造体、ポインタの変換処理 | TomoSoft](https://tomosoft.jp/design/?p=4647)  

### キーボードフック（グローバル）

グローバルフックを使用するために、フックプロシージャを含むDLLを作成します。ここでは単純にキーボード操作を無効にするフックプロシージャを作成します。  

[Win32 API キーストロークメッセージをフックする - s-kita’s blog](https://s-kita.hatenablog.com/entry/20121228/1356682345)  

フックプロシージャのインストール方法としては[フックプロシージャのインストールと解放](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/using-hooks#installing-and-releasing-hook-procedures)によると

1. アプリケーション側が[SetWindowsHookEx](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexw)を呼び出す方法
    1. [LoadLibrary](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw)を使用してDLLモジュールのハンドルを取得します。  
    1. [GetProcAddress](https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress)を使用してフックプロシージャへのポインター取得します。  
    1. 最後に、[SetWindowsHookEx](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexw)を使用して、フックチェーンにフックプロシージャをインストールします。
1. DLL内にインストール関数を作成し、それを呼び出す方法  

の2つがあると記載されていますが、参考にしたサイトでは2個目の方法を使用していますので、ここでもその方法で作成します。そのためC#で記載していた`SetWindowsHookEx` `UnhookWindowsHookEx`などのコードをC言語で記載し、DLLを作成します。フック中のキーボード操作を無効にするようにフック関数の[戻り値を1（0以外）](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/keyboardproc#returns)にしています。DLLの作成については以下を参照願います。  

[MinGW+C言語でWindows用Dllを作成する #Windows - Qiita](https://qiita.com/nekotadon/items/3da07aa1d20017f9bd16)  

```cpp
/*
Hook.cpp
g++ -shared -o Hook.dll Hook.cpp -luser32
cl /LD Hook.cpp User32.lib
*/

#include <windows.h>

// グローバル変数をプロセス間で共有
#ifdef __MINGW64__
// MinGW用
HHOOK g_hHook __attribute__((section("shared"), shared)) = NULL;
#else
// VC用
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")
#endif

HINSTANCE g_hInst;

// フックプロシージャ
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        return 1; // キー全無効
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

// フックの開始
extern "C" __declspec(dllexport) BOOL __stdcall StartHook()
{
    g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)HookProc, g_hInst, 0);
    return g_hHook != NULL;
}

// フックの終了
extern "C" __declspec(dllexport) BOOL __stdcall EndHook()
{
    return UnhookWindowsHookEx(g_hHook);
}

BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInst = (HINSTANCE)hModule;
        break;
    }
    return TRUE;
}
```

C#で呼び出します。DLLと同じ`x64`でビルドします。  

`start`ボタンを押すとすべてのキーボード操作が無効になります。`end`ボタンを押すと、通常に戻ります。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        internal static class NativeMethods
        {
            //アプリケーションと同じ場所にHook.dllを置く
            [DllImport("Hook.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool StartHook();

            [DllImport("Hook.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EndHook();
        }

        //フック中かどうか
        bool processing = false;

        //フック開始/終了ボタン
        Button button_start, button_end;

        public Form1()
        {
            InitializeComponent();

            //フック開始ボタン（キー操作を無効化）
            button_start = new Button { Text = "start", Top = 5, Left = 5 };
            button_start.Click += (sender, e) =>
            {
                if (!processing)
                {
                    //フック開始
                    bool ret = NativeMethods.StartHook();

                    //フック開始が成功した場合はボタンの有効無効切り替え
                    if (ret)
                    {
                        processing = true;
                        button_start.Enabled = !processing;
                        button_end.Enabled = processing;
                    }

                    //フック開始の成功失敗を表示
                    Text = "Hook Start " + (ret ? "OK" : "NG");
                }
            };

            //フック終了ボタン（キー操作を通常に戻す）
            button_end = new Button { Text = "end", Top = 35, Left = 5, Enabled = false };
            button_end.Click += (sender, e) =>
            {
                if (processing)
                {
                    //フック解除
                    bool ret = NativeMethods.EndHook();

                    //フック解除が成功した場合はボタンの有効無効切り替え
                    if (ret)
                    {
                        processing = false;
                        button_start.Enabled = !processing;
                        button_end.Enabled = processing;
                    }

                    //フック解除の成功失敗を表示
                    Text = "Hook End " + (ret ? "OK" : "NG");
                }
            };

            //終了時にフック中であればフック解除
            FormClosing += (sender, e) => { if (processing) NativeMethods.EndHook(); };

            //適当な入力用テキストボックス
            TextBox textBox = new TextBox { Top = 65, Left = 5, };

            Controls.Add(button_start);
            Controls.Add(button_end);
            Controls.Add(textBox);
        }
    }
}
```

**参考URL**  

* [第１６０章](http://www.kumei.ne.jp/c_lang/sdk2/sdk_160.htm)  
* [第１６１章](http://www.kumei.ne.jp/c_lang/sdk2/sdk_161.htm)  
* [フック & DLL VC++ 2005 その1](http://dodonpa.la.coocan.jp/hook_dll_vc1.htm)  
* [フックの使用 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/using-hooks)  

### マウスフック（グローバル）

[キーボードフック（グローバル）](#キーボードフックグローバル)と同様です。本来はグローバルフックではなく対象のスレッドに限定すべきですが、使用例としてグローバルフックとしています。  

フック中にエクスプローラの余白をダブルクリックしたときに、上の階層に行くようにします。  
1. 簡易的なアプリケーションのため同じ場所で2回クリックされた場合、クリックの時間間隔によらずダブルクリックと判定しています。マウス メッセージの識別子を確認し、マウスの右ボタンのダウン`WM_LBUTTONDOWN`とアップ`WM_LBUTTONUP`が2回繰り返されたときにダブルクリックと判定します。但しマウスカーソルの移動`WM_MOUSEMOVE`がほぼない状態に限っています。  
1. ダブルクリック判定をしたときに
    * クラス名が`DirectUIHWND`。（事前に[マウスカーソル下のウィンドウ](#マウスカーソル下のウィンドウ)によってエクスプローラのファイルビューのクラス名が`DirectUIHWND`であることを確認）  
    * 実行ファイルが`explorer.exe`。（[全ウィンドウの情報を表示](#全ウィンドウの情報を表示)参照）  
    * 色がファイル/フォルダ未選択時のエクスプローラ背景色である白。（[マウスカーソル位置の色を取得](#マウスカーソル位置の色を取得)参照）  
1. 上記が確認できた場合、Alt＋↑を送信し、上の階層に行くようにします（[非アクティブウィンドウにキーを送信](#非アクティブウィンドウにキーを送信)参照）。

呼び出す側のC#ソースコードは[キーボードフック（グローバル）](#キーボードフックグローバル)と同一です。`start`ボタンでフック開始、`end`ボタンでフック終了です。  

```cpp
/*
Hook.cpp
g++ -shared -o Hook.dll Hook.cpp -luser32 -lgdi32
cl /LD Hook.cpp User32.lib Gdi32.lib
*/

#ifndef UNICODE
#define UNICODE
#endif
#define PSAPI_VERSION 2
#include <windows.h>
#include <psapi.h>

// グローバル変数をプロセス間で共有
#ifdef __MINGW64__
// MinGW用
HHOOK g_hHook __attribute__((section("shared"), shared)) = NULL;
#else
// VC用
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")
#endif

HINSTANCE g_hInst;

// マウスカーソル位置の色
COLORREF CursorPointColor()
{
    // マウスカーソル位置
    POINT point;
    GetCursorPos(&point);

    // デバイス コンテキストを取得
    HDC hdc = GetDC(NULL);

    // マウスカーソル位置の色を取得
    COLORREF color = GetPixel(hdc, point.x, point.y);

    // デバイス コンテキストを解放
    ReleaseDC(NULL, hdc);

    return color;
}

void DCAction()
{
    // マウスカーソル位置
    POINT point;
    GetCursorPos(&point);

    // マウスカーソル位置のウィンドウ
    HWND hWnd = WindowFromPoint(point);

    // クラス名
    wchar_t className[MAX_PATH];
    GetClassName(hWnd, className, _countof(className));

    // エクスプローラのファイルビュー
    if (wcscmp(className, L"DirectUIHWND") == 0)
    {
        // プロセスIDを取得
        DWORD processId;
        GetWindowThreadProcessId(hWnd, &processId);

        // プロセスオブジェクトを開く
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId);

        if (hProcess != NULL)
        {
            // 実行ファイルパスを取得
            wchar_t lpFilename[MAX_PATH];
            DWORD lpdwSize = 0;
            GetModuleFileNameEx(hProcess, NULL, lpFilename, _countof(lpFilename));

            // 実行ファイル名を取得
            wchar_t drive[_MAX_DRIVE];
            wchar_t dir[_MAX_DIR];
            wchar_t fname[_MAX_FNAME];
            wchar_t ext[_MAX_EXT];
            _wsplitpath_s(lpFilename, drive, dir, fname, ext);

            // explorer.exeの場合
            if (wcscmp(fname, L"explorer") == 0 && wcscmp(ext, L".exe") == 0)
            {
                // マウスカーソル位置の色が白の場合（余白の場合）
                if (CursorPointColor() == 0x00FFFFFF)
                {
                    // Alt+↑を送信
                    keybd_event(VK_MENU, 0, 0, 0);
                    keybd_event(VK_UP, 0, 0, 0);
                    keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0);
                    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
                }
            }

            // オブジェクトハンドルを閉じる
            CloseHandle(hProcess);
        }
    }
}

LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    // マウス移動回数
    static int counter = 0;

    // down:1->up:2->down:3->up:4
    static int mode = 0;

    if (nCode == HC_ACTION)
    {
        if (wParam == WM_MOUSEMOVE)
        {
            // マウス移動をカウントしていく
            if (counter <= 255 && mode > 0)
            {
                counter++;
            }

            // 移動5回以上でmodeリセット
            if (counter >= 5)
            {
                mode = 0;
            }
        }
        if (wParam == WM_LBUTTONDOWN)
        {
            if (mode == 0)
            {
                // シングルクリックダウン
                mode = 1;
                counter = 0;
            }
            else if (mode == 2 && counter < 5)
            {
                // マウスカーソル位置の色が白の場合
                if (CursorPointColor() == 0x00FFFFFF)
                {
                    // シングルクリックアップ
                    mode = 3;
                }
            }
        }
        if (wParam == WM_LBUTTONUP)
        {
            if (mode == 1 && counter < 5)
            {
                // ダブルクリックダウン
                mode = 2;
            }
            else if (mode == 3 && counter < 5)
            {
                // ダブルクリックアップ->ダブルクリック判定
                DCAction();
                mode = 0;
            }
        }
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

extern "C" __declspec(dllexport) BOOL __stdcall StartHook()
{
    g_hHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)HookProc, g_hInst, 0);
    return g_hHook != NULL;
}

extern "C" __declspec(dllexport) BOOL __stdcall EndHook()
{
    return UnhookWindowsHookEx(g_hHook);
}

BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInst = (HINSTANCE)hModule;
        break;
    }
    return TRUE;
}
```

**参考URL**  

* [winapi - Error using GetModuleFileNameExA function in Qt - Stack Overflow](https://stackoverflow.com/questions/63653786/error-using-getmodulefilenameexa-function-in-qt)  
* [GetModuleFileNameExW 関数 (psapi.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw#remarks)  
* [_countof Macro | Microsoft Learn](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/countof-macro?view=msvc-170)  
* [strcmp、wcscmp、_mbscmp、_mbscmp_l | Microsoft Learn](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/strcmp-wcscmp-mbscmp?view=msvc-170)  
* [文字列の操作 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/learnwin32/working-with-strings#tchars)  
* [_splitpath_s、_wsplitpath_s | Microsoft Learn](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/splitpath-s-wsplitpath-s?view=msvc-170)  

### ローレベルキーボード/マウスフック（グローバル）

グローバルフックの場合、グローバルフックプロシージャは任意のアプリケーションのコンテキストで呼び出されるため、DLLモジュール内にある必要があります。下記のサイトにそのように記載されています。  

[Hooks Overview - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks?redirectedfrom=MSDN#whgetmessagehook)  

> A global hook procedure can be called in the context of any application in the same desktop as the calling thread, so the procedure must be in a separate DLL module.

但し`WH_KEYBOARD_LL`と`WH_MOUSE_LL`の場合は、グローバフルックでありながら、フックをインストールしたスレッドのコンテキストで呼び出されると書かれています。  

[LowLevelMouseProc callback function - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelmouseproc)  
[LowLevelKeyboardProc callback function - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc)  

> This hook is called in the context of the thread that installed it. 

実際に確認するために、まずは`WH_KEYBOARD_LL`ではなく`WH_KEYBOARD`を利用した[キーボードフック（グローバル）](#キーボードフックグローバル)のDLLを以下のコードから作成し直します。「キーボード操作を無効にする」という動作に変更はありません。但し、DLLがロード/アンロードされたときやフックプロシージャが呼ばれたときにメッセージボックスを表示することで、いつ何が行われたかが分かるようにしています。  

[第１６１章](http://www.kumei.ne.jp/c_lang/sdk2/sdk_161.htm)  
[DllMain エントリ ポイント (Process.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/dlls/dllmain)  

```cpp
/*
Hook.cpp
g++ -shared -o Hook.dll Hook.cpp -luser32
cl /LD Hook.cpp User32.lib
*/

#ifndef UINCODE
#define UNICODE
#endif

#include <windows.h>
#include <stdio.h>

// グローバル変数をプロセス間で共有
#ifdef __MINGW64__
// MinGW用
HHOOK g_hHook __attribute__((section("shared"), shared)) = NULL;
int counter __attribute__((section("shared"), shared)) = 0;
#define MOJI_ENV L"MinGW"
#else
// VC用
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
int counter = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:Shared,rws")
#define MOJI_ENV L"MSVC"
#endif

// デバッグ用
#define OUTPUT_FOR_DEBUG
#ifdef __MINGW64__
// MinGW用
#define MOJI_ENV L"MinGW"
#else
// VC用
#define MOJI_ENV L"MSVC"
#endif

HINSTANCE g_hInst;

// フックプロシージャ
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
#ifdef OUTPUT_FOR_DEBUG
        if (g_hHook == NULL)
        {
            MessageBox(NULL, L"NG", MOJI_ENV, MB_OK);
        }
        else
        {
            MessageBox(NULL, L"OK", MOJI_ENV, MB_OK);
        }
#endif
        return 1; // キー全無効
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

// フックの開始
extern "C" __declspec(dllexport) BOOL __stdcall StartHook()
{
    g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)HookProc, g_hInst, 0);
    return g_hHook != NULL;
}

// フックの終了
extern "C" __declspec(dllexport) BOOL __stdcall EndHook()
{
    return UnhookWindowsHookEx(g_hHook);
}

BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
#ifndef OUTPUT_FOR_DEBUG
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInst = (HINSTANCE)hModule;
        break;
    }
#else
    counter++;
    wchar_t str_counter[256];
    swprintf_s(str_counter, L"%d", counter);
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInst = (HINSTANCE)hModule;
        if (lpReserved == NULL)
        {
            MessageBox(NULL, L"DLLが仮想アドレス空間にロードされた（動的読み込み）", str_counter, MB_OK);
        }
        else
        {
            MessageBox(NULL, L"DLLが仮想アドレス空間にロードされた（静的読み込み）", str_counter, MB_OK);
        }
        break;
    case DLL_PROCESS_DETACH:
        if (lpReserved == NULL)
        {
            MessageBox(NULL, L"DLLが仮想アドレス空間からアンロードされた", str_counter, MB_OK);
        }
        else
        {
            MessageBox(NULL, L"DLLが仮想アドレス空間からアンロードされた（プロセス終了）", str_counter, MB_OK);
        }
        break;
    }
#endif
    return TRUE;
}
```

作成したDLLを利用した場合、  

* アプリケーションの`start`ボタンを押すとDLLがロードされることが分かります。  
* メモ帳を起動してキーを入力すると、入力した時点でDLLがロードされることが分かります。  
* 別のメモ帳を起動してキーを入力すると、入力した時点で同様にDLLがロードされることが分かります。  
* アプリケーションの`end`ボタンを押してもDLLがアンロードされないことが分かります。  
* メモ帳を閉じた場合やアプリケーションが終了したときにDLLがアンロードされることが分かります。  
* 今回の検証からは外れますが、`g_hHook`をプロセス間で共有せず、単に`HINSTANCE g_hInst;`と同じ箇所に`HHOOK g_hHook = NULL;`と宣言した場合、メモ帳がDLLをロードしたとき、`g_hHook`が`NULL`となっていることが分かります。

[フックプロシージャのインストールと解放](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/using-hooks#installing-and-releasing-hook-procedures)に書かれている通り、すべてのアプリケーションのコンテキストでグローバルフックプロシージャが呼び出され、それらのすべてのプロセスに対して`LoadLibrary`関数が暗黙的に呼び出されています。また、`end`ボタンを押し、`UnhookWindowsHookEx`を呼び出すことでグローバル フック プロシージャを解放できますが、フックプロシージャを含むDLLは解放されません。DLLを読み込んだすべてのプロセスが終了した場合、解放されます。  

今度は`WH_KEYBOARD_LL`で確認してみます。上記コードの

```cpp
g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)HookProc, g_hInst, 0);
```

の箇所を、

```cpp
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)HookProc, g_hInst, 0);
```

に変更してDLLを作成し直します。作成したDLLを利用した場合、今度はメモ帳を起動してキー入力をしてもDLLがロードされていないことが分かります。  

確認した結果、実際にインストールしたスレッドのコンテキストでのみ呼び出されていると思われるので、DLLは使用せずに、C#のコードだけで実装してみます。なお「DLLが呼ばれないのであれば、グローバルフックの場合でもDLLにしなくてよい」のかどうかは理解できていません。[フックの概要](https://learn.microsoft.com/ja-jp/windows/win32/winmsg/about-hooks?redirectedfrom=MSDN#whgetmessagehook)の注記に  
> フックは、各メッセージに対してシステムが実行する必要がある処理の量が増えるので、システムの速度が低下する傾向があります。  

> グローバル フックは、デバッグ目的でのみ使用する必要があります。それ以外の場合は、それらを避ける必要があります。  

とありますので、仮にDLLにしなくてよい、が正しかったとしても、単にエラーがでないから問題ない、ではなく、それ以外の関連する事柄についてきちんと理解したうえで使用したほうがよいと思います。    

**ローレベルキーボードフック（グローバル）**  

キーボード操作のたびに「操作内容」と「仮想キーコード」を表示します。

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //フックコールバック関数
            public delegate IntPtr SetWindowsHookExCallback(int nCode, UIntPtr wParam, IntPtr lParam);

            //フックの設定
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr SetWindowsHookEx(int idHook, SetWindowsHookExCallback lpfn, IntPtr hmod, uint dwThreadId);

            //フックの解除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(IntPtr hhk);

            //フック情報を次のフックプロシージャに渡す
            [DllImport("user32.dll")]
            public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

            //ウィンドウに関する情報を取得
            //32bit
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
            //64bit
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
        }

        //キーボードメッセージの識別子
        public enum WM
        {
            WM_KEYDOWN = 0x0100,
            WM_KEYUP = 0x0101,
            WM_SYSKEYDOWN = 0x0104,
            WM_SYSKEYUP = 0x0105
        }

        //KBDLLHOOKSTRUCT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct KBDLLHOOKSTRUCT
        {
            public uint vkCode;
            public uint scanCode;
            public uint flags;
            public uint time;
            public UIntPtr dwExtraInfo;
        }

        //定数の定義
        private const int WH_KEYBOARD_LL = 13;//ローレベルキーボードフック
        private const int HC_ACTION = 0;
        private const int GWLP_HINSTANCE = -6;
        private const int GWL_HINSTANCE = -6;

        //---------- Win32API用定義終了 ----------

        //フック プロシージャ
        IntPtr HookProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode == HC_ACTION)
            {
                //wParam キーボード メッセージの識別子
                textBox.AppendText(((WM)wParam.ToUInt32()).ToString().PadRight(16, ' '));

                //lParam MOUSEHOOKSTRUCT 構造体へのポインタ
                KBDLLHOOKSTRUCT keystruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                textBox.AppendText(((Keys)keystruct.vkCode).ToString() + Environment.NewLine);
            }

            return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }

        IntPtr hookPtr = IntPtr.Zero;

        //メッセージ表示用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            WordWrap = false,
            ScrollBars = ScrollBars.Both,
            Top = 30,
            Left = 10,
            Width = 500,
            Height = 300,
            Font = new System.Drawing.Font("ＭＳ ゴシック", 9F)//等幅
        };

        public Form1()
        {
            InitializeComponent();

            //キー入力用テキストボックス
            TextBox txt = new TextBox { Top = 5, Left = 10, Width = 100 };
            Controls.Add(txt);

            //メッセージ表示用テキストボックス
            Controls.Add(textBox);

            //起動時にフック開始
            Shown += (sender, e) =>
            {
                //インスタンスハンドルの取得
                IntPtr hInstance;
                if (IntPtr.Size == 4)//32bit
                {
                    hInstance = (IntPtr)NativeMethods.GetWindowLong(Handle, GWL_HINSTANCE);
                }
                else//64bit
                {
                    hInstance = NativeMethods.GetWindowLongPtr(Handle, GWLP_HINSTANCE);
                }

                hookPtr = NativeMethods.SetWindowsHookEx(WH_KEYBOARD_LL, HookProc, hInstance, 0);
            };

            //終了時にフック解除
            FormClosing += (sender, e) =>
            {
                NativeMethods.UnhookWindowsHookEx(hookPtr);
                hookPtr = IntPtr.Zero;
            };
        }
    }
}
```
**ローレベルマウスフック（グローバル）**  

マウス操作のたびに「操作内容」と「マウスカーソルの位置」を表示します。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //フックコールバック関数
            public delegate IntPtr SetWindowsHookExCallback(int nCode, UIntPtr wParam, IntPtr lParam);

            //フックの設定
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr SetWindowsHookEx(int idHook, SetWindowsHookExCallback lpfn, IntPtr hmod, uint dwThreadId);

            //フックの解除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool UnhookWindowsHookEx(IntPtr hhk);

            //フック情報を次のフックプロシージャに渡す
            [DllImport("user32.dll")]
            public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

            //ウィンドウに関する情報を取得
            //32bit
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
            //64bit
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
        }

        //マウス メッセージの識別子
        public enum WM
        {
            LBUTTONDOWN = 0x0201,
            LBUTTONUP = 0x0202,
            RBUTTONDOWN = 0x0204,
            RBUTTONUP = 0x0205,
            MOUSEMOVE = 0x0200,
            MOUSEWHEEL = 0x020A,
        }

        //MSLLHOOKSTRUCT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public UIntPtr dwExtraInfo;
        }

        //POINT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int x;
            public int y;
        }

        //定数の定義
        private const int WH_MOUSE_LL = 14;//ローレベルマウスフック
        private const int HC_ACTION = 0;
        private const int GWLP_HINSTANCE = -6;
        private const int GWL_HINSTANCE = -6;

        //---------- Win32API用定義終了 ----------

        //フック プロシージャ
        IntPtr HookProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode == HC_ACTION)
            {
                //wParam マウス メッセージの識別子
                textBox.AppendText(((WM)wParam.ToUInt32()).ToString().PadRight(16, ' '));

                //lParam MSLLHOOKSTRUCT 構造体へのポインタ
                MSLLHOOKSTRUCT mousestruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                textBox.AppendText(mousestruct.pt.x.ToString() + "," + mousestruct.pt.y.ToString() + Environment.NewLine);
            }

            return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }

        IntPtr hookPtr = IntPtr.Zero;

        //メッセージ表示用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            WordWrap = false,
            ScrollBars = ScrollBars.Both,
            Top = 30,
            Left = 10,
            Width = 500,
            Height = 300,
            Font = new System.Drawing.Font("ＭＳ ゴシック", 9F)//等幅
        };

        public Form1()
        {
            InitializeComponent();

            //キー入力用テキストボックス
            TextBox txt = new TextBox { Top = 5, Left = 10, Width = 100 };
            Controls.Add(txt);

            //メッセージ表示用テキストボックス
            Controls.Add(textBox);

            //起動時にフック開始
            Shown += (sender, e) =>
            {
                //インスタンスハンドルの取得
                IntPtr hInstance;
                if (IntPtr.Size == 4)//32bit
                {
                    hInstance = (IntPtr)NativeMethods.GetWindowLong(Handle, GWL_HINSTANCE);
                }
                else//64bit
                {
                    hInstance = NativeMethods.GetWindowLongPtr(Handle, GWLP_HINSTANCE);
                }

                hookPtr = NativeMethods.SetWindowsHookEx(WH_MOUSE_LL, HookProc, hInstance, 0);
            };

            //終了時にフック解除
            FormClosing += (sender, e) =>
            {
                NativeMethods.UnhookWindowsHookEx(hookPtr);
                hookPtr = IntPtr.Zero;
            };
        }
    }
}
```
## ウィンドウメニューに項目を追加

[システムメニューにメニューを追加する - C#プログラミング | iPentec](https://www.ipentec.com/document/csharp-add-menu-item-in-system-menu)  

`Alt`+`Space`で出てくるウィンドウメニューに「最前面に表示」メニューを追加します。
```c
//ウィンドウメニュー（システムメニュー）
元のサイズに戻す//position:0
移動//1
サイズ変更//2
最小化//3
最大化//4
---//5
最前面に表示//追加
---//追加
閉じる//6
```

1. [GetSystemMenu](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getsystemmenu)を使用してウィンドウメニューのコピーへのハンドルを取得します。  
1. [InsertMenuItem](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-insertmenuitemw)を使用して「区切り線」を追加します。  
    1. 第1引数には[GetSystemMenu](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getsystemmenu)で取得したメニューへのハンドルを指定します。  
    1. 第3引数を`true`にすることで、第2引数でメニュー挿入位置を指定できます。第2引数で指定したメニューの位置の前に挿入されます。今回は「閉じる」の前に区切り線を入れたいので「6」を指定します。  
    1. 第4引数には[MENUITEMINFO](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-menuiteminfow)構造体へのポインタを指定します。[MENUITEMINFO](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-menuiteminfow)構造体にはメニュー項目に対する設定値が入っています。リンク先を見て必要な部分のみ設定します。今回は区切り設定をしたいのでその設定のみを実施します。    
1. [InsertMenuItem](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-insertmenuitemw)を使用して「最前面に表示」を追加します。  
    1. 第1引数は上記と同様です。  
    1. 第2,3引数も上記と同様です。「区切り線」を入れたことで「区切り線」が新たに「6」の位置になっています。この前に入れたいので「6」を指定します。  
    1. 第4引数も同様です。今回は「メニュー項目の識別子」と「メニュー項目の文字列」を設定します。[注釈](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getsystemmenu#remarks)に「定義済みのウィンドウ メニュー項目はすべて、`0xF000`より大きい識別子番号を持っています。」と書かれているので`0xF000`未満の値で識別子を設定します。今回は`MENU_ID_01(0x0001)`としています。  
1. 以上でメニュー項目は追加されますが、メニュー表示時や選択時の実装がまだです。ウィンドウプロシージャを利用してメッセージ受信時の処理を記載します。ウィンドウプロシージャはウィンドウに送信されたメッセージを処理するコールバック関数のことです。    
    * [ウィンドウプロシージャ - Windows API 入門](http://kaitei.net/winapi/window-procedures/#window-procedure)  
    * [WNDPROC - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nc-winuser-wndproc?redirectedfrom=MSDN)  
1. [WndProc](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.wndproc?view=windowsdesktop-8.0)関数は[注釈](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.wndproc?view=windowsdesktop-8.0#--)に記載の通り、[WindowProc](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nc-winuser-wndproc?redirectedfrom=MSDN)関数に正確に対応していますのでこれをオーバーライドして実装します。  
    1. メニュー表示前に[WM_INITMENU](https://learn.microsoft.com/ja-jp/windows/win32/menurc/wm-initmenu)メッセージが送信されます。このメッセージ受信時に[SetMenuItemInfo](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setmenuiteminfow)を使って、現在の[TopMost](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form.topmost?view=windowsdesktop-8.0)設定から「最前面に表示」のチェック状態を設定します。  
    1. メニュー選択時に[WM_SYSCOMMAND](https://learn.microsoft.com/ja-jp/windows/win32/menurc/wm-syscommand)メッセージが送信されます。メニュー識別子が`MENU_ID_01(0x0001)`の場合[TopMost](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form.topmost?view=windowsdesktop-8.0)設定を切り替えます。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //ウィンドウメニューへのハンドルを取得
            [DllImport("user32.dll")]
            public static extern IntPtr GetSystemMenu(IntPtr hWnd, [MarshalAs(UnmanagedType.Bool)] bool bRevert);

            //新しいメニュー項目を挿入
            [DllImport("user32.dll", ExactSpelling = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool InsertMenuItemW(IntPtr hmenu, uint item, [MarshalAs(UnmanagedType.Bool)] bool fByPosition, ref MENUITEMINFO lpmi);

            //メニュー項目の設定を変更
            [DllImport("user32.dll", ExactSpelling = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetMenuItemInfoW(IntPtr hmenu, uint item, [MarshalAs(UnmanagedType.Bool)] bool fByPositon, ref MENUITEMINFO lpmii);
        }

        //MENUITEMINFO構造体の定義
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct MENUITEMINFO
        {
            public uint cbSize;//この構造体のサイズ
            public uint fMask;//取得または設定する項目
            public uint fType;//メニュー項目の種類
            public uint fState;//メニュー項目の状態
            public uint wID;//メニュー項目の識別子
            public IntPtr hSubMenu;
            public IntPtr hbmpChecked;
            public IntPtr hbmpUnchecked;
            public UIntPtr dwItemData;
            public string dwTypeData;//メニュー項目の内容
            public uint cch;
            public IntPtr hbmpItem;
        }

        //定数の定義
        //fMask
        private const uint MIIM_FTYPE = 0x00000100;//fTypeメンバーを取得または設定
        private const uint MIIM_ID = 0x00000002;//wID メンバーを取得または設定
        private const uint MIIM_STATE = 0x00000001;//fState メンバーを取得または設定
        private const uint MIIM_STRING = 0x00000040;//dwTypeData メンバーを取得または設定

        //fType
        private const uint MFT_SEPARATOR = 0x00000800;//区切り線

        //fState
        private const uint MFS_CHECKED = 0x00000008;//チェックあり
        private const uint MFS_UNCHECKED = 0x000000000;//チェックなし

        //メッセージ
        private const int WM_SYSCOMMAND = 0x0112;//コマンド選択時、ボタン選択時
        private const int WM_INITMENU = 0x0116;//メニュー表示前

        //---------- Win32API用定義終了 ----------

        private const uint MENU_ID_01 = 0x0001;//追加するメニューの識別子
        IntPtr hSysMenu;//ウィンドウ メニューへのハンドル

        public Form1()
        {
            InitializeComponent();

            //ウィンドウ メニューへのハンドルを取得
            hSysMenu = NativeMethods.GetSystemMenu(Handle, false);

            //区切りメニューを追加
            MENUITEMINFO item_sep = new MENUITEMINFO
            {
                cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)),
                fMask = MIIM_FTYPE,
                fType = MFT_SEPARATOR
            };
            NativeMethods.InsertMenuItemW(hSysMenu, 6, true, ref item_sep);

            //最前面表示メニューを追加
            MENUITEMINFO item = new MENUITEMINFO
            {
                cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)),
                fMask = MIIM_ID | MIIM_STRING,
                wID = MENU_ID_01,
                dwTypeData = "最前面に表示"
            };
            NativeMethods.InsertMenuItemW(hSysMenu, 6, true, ref item);
        }

        //WindowProcをオーバーライド
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                //メニュー表示前にチェック状態を確定
                case WM_INITMENU:
                    MENUITEMINFO item = new MENUITEMINFO
                    {
                        cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)),
                        fMask = MIIM_STATE,
                        fState = TopMost ? MFS_CHECKED : MFS_UNCHECKED
                    };
                    NativeMethods.SetMenuItemInfoW(hSysMenu, MENU_ID_01, false, ref item);
                    break;

                //メニュー選択時
                case WM_SYSCOMMAND:
                    //メニュー項目の識別子を取得
                    uint menuid = (uint)(m.WParam.ToInt32() & 0xFFFF);
                    //「最前面に表示」メニューの場合、最前面状態を切り替え
                    if (menuid == MENU_ID_01)
                    {
                        TopMost = !TopMost;
                    }
                    break;
            }

            //通常のウィンドウプロシージャ処理
            base.WndProc(ref m);
        }
    }
}
```

**参考URL**  

* [WindowsフォームでWindowsメッセージを直接処理するには？：.NET TIPS - ＠IT](https://atmarkit.itmedia.co.jp/ait/articles/0606/30/news131.html)  
* [メニュー (メニューとその他のリソース) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/menurc/menus)  
* [メニューについて - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/menurc/about-menus)  

## クリップボードの監視

[クリップボードの使用 - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/using-the-clipboard#monitoring-clipboard-contents)  

ウィンドウハンドルをクリップボード形式リスナーに登録するとクリップボードの内容が変更されたときに[WM_CLIPBOARDUPDATE](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/wm-clipboardupdate)メッセージが投稿されます。

下記の例ではクリップボード内の文字列が変更されたときに、その文字列を表示するようにしています。終了するときはタスクトレイアイコンを右クリックして「終了」を選択します。なお、フォームを表示させないようにするために`Program.cs`も変更しています。  

1. 起動時に[AddClipboardFormatListener](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener)を使用してクリップボード形式リスナーに登録します。  
1. 動作中、ウィンドウプロシージャ（[ウィンドウメニューに項目を追加](#ウィンドウメニューに項目を追加)を参照）を利用して[WM_CLIPBOARDUPDATE](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/wm-clipboardupdate)メッセージを確認しています。メッセージが来たとき、クリップボードに含まれている内容が文字列であれば、その文字列を[ShowBalloonTip](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.notifyicon.showballoontip?view=windowsdesktop-8.0)メソッドを使って表示するようにしています。このとき、クリップボード内にあるデータ形式（下記参照）の数が0ではないことと現在のテキストとは異なることを確認したうえで表示しています。これは例えば以下のようにコピーされるとこととメッセージが送られることが必ずしも一致しないためです。  
    * Excelの場合、1回のコピー操作のときに[WM_CLIPBOARDUPDATE](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/wm-clipboardupdate)メッセージが連続して複数送られます。また、貼り付けのときにも送られます。Excelを閉じたときにも送られることがあります。  
    * その他のソフトウェアでもコピー以外の操作のときにメッセージが送られることを確認しています。  

    何がトリガーとなってメッセージが送られるのかは未確認ですが、少なくとも単に[OpenClipboard](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-openclipboard)と[CloseClipboard](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-closeclipboard)を呼び出した場合でも、[CloseClipboard](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-closeclipboard)が呼ばれた時点でメッセージが送られていました。  
1. 終了時に[RemoveClipboardFormatListener](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-removeclipboardformatlistener)を使用してクリップボード形式リスナーから登録を削除します。  

```cs
//Program.csも以下のように変更
using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    internal static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Form1 form1 = new Form1();
            Application.Run();
        }
    }
}
```

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //クリップボード形式リスナーリストに追加
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool AddClipboardFormatListener(IntPtr hwnd);

            //クリップボード形式リスナーリストから削除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

            //クリップボード内にあるデータ形式の数
            [DllImport("user32.dll")]
            public static extern int CountClipboardFormats();
        }

        //定数の定義
        private const int WM_CLIPBOARDUPDATE = 0x031D;

        //---------- Win32API用定義終了 ----------

        //トレイアイコン
        private NotifyIcon notifyIcon;

        //クリップボード形式リスナーとして登録済みかどうか
        bool monitoring = false;

        //クリップボード内の文字列
        string cliptext = "";

        public Form1()
        {
            InitializeComponent();

            //終了メニュー
            ToolStripMenuItem ExitToolStripMenuItem = new ToolStripMenuItem { Text = "終了" };
            ExitToolStripMenuItem.Click += (sender, e) =>
            {
                //トレイアイコンを非表示にする
                notifyIcon.Visible = false;

                //クリップボード形式リスナー一覧から削除
                if (monitoring)
                {
                    NativeMethods.RemoveClipboardFormatListener(Handle);
                }

                //終了
                Application.Exit();
            };

            //コンテキストメニュー
            ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
            contextMenuStrip.Items.Add(ExitToolStripMenuItem);

            //トレイアイコンの作成
            notifyIcon = new NotifyIcon(components)
            {
                ContextMenuStrip = contextMenuStrip,
                Icon = Icon,
                Text = "Clipboard Format Listener",
                Visible = true
            };

            //クリップボード形式リスナー一覧に追加
            monitoring = NativeMethods.AddClipboardFormatListener(Handle);
        }

        //WindowProcをオーバーライド
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_CLIPBOARDUPDATE:
                    //クリップボード内にあるデータ形式の数がゼロでなくかつテキストが含まれている場合
                    if (NativeMethods.CountClipboardFormats() != 0 && Clipboard.ContainsText())
                    {
                        //クリップボード内のテキスト取得
                        string ret = Clipboard.GetText();

                        //現在のテキストと異なる場合
                        if (ret != cliptext)
                        {
                            cliptext = ret;
                            if (cliptext != "")
                            {
                                notifyIcon.ShowBalloonTip(1000, "", Clipboard.GetText(), ToolTipIcon.None);
                            }
                        }
                    }
                    break;
            }

            //通常のウィンドウプロシージャ処理
            base.WndProc(ref m);
        }
    }
}
```
**クリップボードのデータ形式について**  

例えばExcelでセルのコピーをしたとき、クリップボードにはコピーされた情報が複数のデータ形式で格納されます。例えば単純なテキストデータ、CSV形式のデータ、セル範囲の画像データなどです。[クリップボードのフォーマット](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/clipboard-formats)に記載の通り、同じ情報を異なる複数の形式で表しています。この形式にはシステムで定義されている[標準クリップボード形式](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/standard-clipboard-formats) だけでなく、アプリケーションが独自で作成した[登録済みクリップボード形式](https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/clipboard-formats#registered-clipboard-formats)などもあります。  

以下はクリップボードの内容が変わるたびにクリップボードに含まれるデータ形式の一覧を表示します。今回はフォームを表示しますので、上記で`Program.cs`を変更した場合は元に戻すか、新しくプロジェクトを作成しなおします。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //クリップボード形式リスナーリストに追加
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool AddClipboardFormatListener(IntPtr hwnd);

            //クリップボード形式リスナーリストから削除
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

            //クリップボード内にあるデータ形式の数
            [DllImport("user32.dll")]
            public static extern int CountClipboardFormats();

            //クリップボードで現在使用できるデータ形式を取得
            [DllImport("user32.dll")]
            public static extern uint EnumClipboardFormats(uint format);

            //指定した登録済み形式の名前をクリップボードから取得
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern int GetClipboardFormatName(uint format, [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpszFormatName, int cchMaxCount);

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool OpenClipboard(IntPtr hWnd);

            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseClipboard();
        }

        //定数の定義
        private const int WM_CLIPBOARDUPDATE = 0x031D;

        //標準クリップボード形式
        public enum CF
        {
            CF_TEXT = 1,
            CF_BITMAP = 2,
            CF_METAFILEPICT = 3,
            CF_SYLK = 4,
            CF_DIF = 5,
            CF_TIFF = 6,
            CF_OEMTEXT = 7,
            CF_DIB = 8,
            CF_PALETTE = 9,
            CF_PENDATA = 10,
            CF_RIFF = 11,
            CF_WAVE = 12,
            CF_UNICODETEXT = 13,
            CF_ENHMETAFILE = 14,
            CF_HDROP = 15,
            CF_LOCALE = 16,
            CF_DIBV5 = 17,
            CF_OWNERDISPLAY = 0x0080,
            CF_DSPTEXT = 0x0081,
            CF_DSPBITMAP = 0x0082,
            CF_DSPMETAFILEPICT = 0x0083,
            CF_DSPENHMETAFILE = 0x008E
        }

        //---------- Win32API用定義終了 ----------

        //出力用テキストボックス
        TextBox textBox = new TextBox
        {
            Multiline = true,
            Dock = DockStyle.Fill,
            ScrollBars = ScrollBars.Both,
            WordWrap = false
        };

        //クリップボード形式リスナーとして登録済みかどうか
        bool monitoring = false;

        public Form1()
        {
            InitializeComponent();

            TopMost = true;
            Controls.Add(textBox);

            //クリップボード形式リスナー一覧に追加
            monitoring = NativeMethods.AddClipboardFormatListener(Handle);

            //終了時にクリップボード形式リスナー一覧から削除
            FormClosing += (sender, e) =>
            {
                if (monitoring)
                {
                    NativeMethods.RemoveClipboardFormatListener(Handle);
                }
            };
        }

        //WindowProcをオーバーライド
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_CLIPBOARDUPDATE:
                    StringBuilder sb = new StringBuilder();
                    sb.Append("--------------------------------------- ");
                    sb.Append(DateTime.Now.ToString("HH:mm:ss.fff "));
                    sb.Append(" / データ形式の数");
                    sb.Append(NativeMethods.CountClipboardFormats().ToString());
                    sb.Append(Environment.NewLine);

                    if (NativeMethods.OpenClipboard(IntPtr.Zero))
                    {
                        //データ形式の数
                        int counter = 0;

                        //データ形式（を数値で表したもの）
                        uint ret = 0;

                        //現在クリップボード内にあるデータ形式を取得
                        for (; ; )
                        {
                            //使用可能なデータ形式を取得（初回の引数は0）
                            ret = NativeMethods.EnumClipboardFormats(ret);

                            //失敗または全取得完了で終了
                            if (ret == 0)
                            {
                                break;
                            }

                            //データ形式の数のカウントアップと出力
                            counter++;
                            sb.Append(counter.ToString("D3"));
                            sb.Append(" : ");

                            //データ形式の名称の取得
                            var buffer = new StringBuilder(256);
                            int length = NativeMethods.GetClipboardFormatName(ret, buffer, buffer.Capacity);

                            //データ形式の名称の出力
                            if (length == 0)
                            {
                                if (Enum.IsDefined(typeof(CF), (int)ret))//標準クリップボード形式
                                {
                                    sb.Append(((CF)ret).ToString());
                                }
                                else//その他の形式
                                {
                                    sb.Append(ret.ToString());
                                }
                            }
                            else//登録済クリップボード形式
                            {
                                sb.Append(buffer.ToString());
                            }
                            sb.Append(Environment.NewLine);
                        }
                        NativeMethods.CloseClipboard();
                    }

                    //テキストボックスに表示
                    textBox.AppendText(sb.ToString());
                    break;
            }

            //通常のウィンドウプロシージャ処理
            base.WndProc(ref m);
        }
    }
}
```

## IMEをオフにする

[alt-ime-ahk/IME.ahk at master · karakaram/alt-ime-ahk · GitHub](https://github.com/karakaram/alt-ime-ahk/blob/master/IME.ahk)  

2秒ごとにフォアグラウンドウィンドウのIMEをオフにします。  

最初は[ImmGetContext](https://learn.microsoft.com/ja-jp/windows/win32/api/imm/nf-imm-immgetcontext)、[ImmSetOpenStatus](https://learn.microsoft.com/ja-jp/windows/win32/api/imm/nf-imm-immsetopenstatus)、[ImmReleaseContext](https://learn.microsoft.com/ja-jp/windows/win32/api/imm/nf-imm-immreleasecontext)を使用して実装しようと思いましたが、他のアプリケーションのIMEは操作できないようなので[ImmGetDefaultIMEWnd](https://learn.microsoft.com/ja-jp/windows/win32/api/imm/nf-imm-immgetdefaultimewnd)を使用しています。  

```cs
//Windows フォーム アプリケーション(.NET Framewrok)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        //---------- Win32API用定義開始 ----------

        //Windows APIの定義
        internal static class NativeMethods
        {
            //スレッドの情報を取得
            [DllImport("user32.dll")]
            public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO pgui);

            //IMEウィンドウ取得
            [DllImport("Imm32.dll")]
            public static extern IntPtr ImmGetDefaultIMEWnd(IntPtr unnamedParam1);

            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SendMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
        }

        //GUITHREADINFO構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct GUITHREADINFO
        {
            public uint cbSize;
            public uint flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public RECT rcCaret;
        }

        //RECT構造体の定義
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        //定数の定義
        private const uint WM_IME_CONTROL = 0x0283;
        private const uint IMC_SETOPENSTATUS = 0x0006;
        private const uint IMC_SETCONVERSIONMODE = 0x0002;
        private const uint IME_CMODE_NOCONVERSION = 0x0100;

        //---------- Win32API用定義終了 ----------

        //タイマー
        Timer timer = new Timer();

        public Form1()
        {
            InitializeComponent();

            //2秒毎に動作
            timer.Enabled = true;
            timer.Interval = 2000;
            timer.Tick += (sender, e) =>
            {
                //フォアグラウンド スレッドの情報を取得
                GUITHREADINFO guiThreadInfo = new GUITHREADINFO();
                guiThreadInfo.cbSize = (uint)Marshal.SizeOf(typeof(GUITHREADINFO));

                if (NativeMethods.GetGUIThreadInfo(0, ref guiThreadInfo))
                {
                    //取得できた場合
                    if (guiThreadInfo.hwndFocus != IntPtr.Zero)//キーボード フォーカスがあるウィンドウへのハンドルがNULLでない場合
                    {
                        // IMEウィンドウのハンドルを取得
                        IntPtr hwndIme = NativeMethods.ImmGetDefaultIMEWnd(guiThreadInfo.hwndFocus);

                        // IMEをOFFにする
                        NativeMethods.SendMessage(hwndIme, WM_IME_CONTROL, (UIntPtr)IMC_SETOPENSTATUS, (IntPtr)0);

                        // またはIME変換無効
                        //NativeMethods.SendMessage(hwndIme, WM_IME_CONTROL, (UIntPtr)IMC_SETCONVERSIONMODE, (IntPtr)IME_CMODE_NOCONVERSION);
                    }
                }
            };

            FormClosing += (sender, e) =>
            {
                timer.Stop();
                timer.Dispose();
            };
        }
    }
}
```

**参考URL**  
* [HanCra](https://atstr.web.fc2.com/?make/doc/start-06)  
* [IMC_SETOPENSTATUS (Windows CE 5.0) | Microsoft Learn](https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms905969(v=msdn.10))  
* [IMC_SETCONVERSIONMODE (Windows CE .NET 4.2) | Microsoft Learn](https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms920931(v=msdn.10))