文字コードについてのまとめ


Unicode
すべての文字を16ビットで表し、1つの文字コードで多国語処理が可能。

■UTF8
Unicodeを8ビット単位で符号化する符号化方式。

EUC
主にUnixで用いられ、複数バイト文字を扱う。

■マルチバイト文字
1文字あたり1バイト以上の可変バイト列として表したもの。
Shift_JISはマルチバイト文字に含まれる。型はchar

■ワイド文字
全ての文字を等しいサイズのデータで表したもの。
Unicodeはワイド文字に含まれる。型はwchar_t

WindowsAPIのMultiByteToWideCharは、マルチバイトをワイド文字列(Unicode
に変換することができる。

ネイティブコードを呼び出す際の警告


マネージコードから、DLLのネイティブコードを呼び出すことを、P/Invoke(Platform Invoke)という。

stringやIntPtrなどはマネージコードとネイティブコードで表現方法が異なるため、CLIによって変換処理が行われる。
このように、異なるシステム間でデータを変換する処理をマーシャリングという。


[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowName);
上記のような実装だと、静的解析でいくつか警告が出る。

  • CA1060: P/Invoke を NativeMethods クラスに移動します

DllImport属性を用いて定義したメソッドは、NativeMethodsというクラスに含めるという規則になっているが、含まれていませんという警告。
下記のようにクラス内にNativeMethodsを作り、その中でメソッドを宣言すれば解消する。


class TestClass {

internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowName);
}

// 実行時はこうする
IntPtr hwnd = NativeMethods.FindWindow(null, "Test Form");
}

  • CA2101: P/Invoke 文字列引数に対してマーシャリングを指定します

FindWindowの場合、stringがマーシャリングされるが、それが意図した形に変換されないかもしれないという警告。
マーシャリング対象に MarshalAs を明示的に指定して、属性にBestFitMapping=false と ThrowOnUnmappableChar=true を設定すれば解消する。


[DllImport("user32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern IntPtr FindWindow(
[MarshalAs(UnmanagedType.LPStr)] string className,
[MarshalAs(UnmanagedType.LPStr)] string windowName);

タスクトレイにアイコンを表示する


タスクトレイにアイコンを表示するには、ツールバーのNotifyIconを用いる

■手順
ツールバーのNotifyIconを、フォームにドラッグする
NotifyIconのプロパティのIconに、適当なアイコンを設定する
NotifyIconのイベントのMouseClickをダブルクリックする
自動生成されたnotifyIcon_MouseClickに、アイコンがクリックされた際の処理
を書く


private void notifyIconPpds_MouseClick(object sender, MouseEventArgs e)
{
// アイコンがクリックされたらフォームを表示する
this.Show();
}


閉じるボタン等でのアプリ終了を無効化


閉じるボタン等でアプリを終了するのを禁止するには、WndProcをオーバーライドしてWM_SYSCOMMANDのSC_CLOSEを無視すればよい。
この方法では、閉じるボタンは押せる状態だが、押してもアプリは終了しなくなる。
また、アプリ左上のアイコンをクリックして表示されるメニューからの終了と、Alt+F4による終了も禁止できる。

タイトルバーの左上をダブルクリックは、パラメータ0xf063が飛んでくる。0xf063が何パラメータなのか調べてもわからなかったので、適当にSC_CLOSEDOUBLECLICKとする。


private const int SC_CLOSE = 0xf060;
private const int SC_CLOSEDOUBLECLICK = 0xf063;
private const int WM_SYSCOMMAND = 0x112;

bool isCloseEnable = false;

protected override void WndProc(ref Message m)
{
// WM_SYSCOMMANDのSC_CLOSEを無視する
if (m.Msg == WM_SYSCOMMAND
&& (m.WParam.ToInt64() == SC_CLOSE || m.WParam.ToInt64() == SC_CLOSEDOUBLECLICK)
&& !isCloseEnable)
{
return;
}
}

特定のフォームにメッセージを送信する


メッセージを送信したい相手のウインドウハンドルがわからない場合、FindWindowを用いれば特定の相手のウインドウハンドルを取得できる。
この取得したウインドウハンドルを指定してSendMessageを呼ぶことで、特定のウインドウにメッセージを送信する。

■送信側


[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string className, string windowName);

// 送信したい相手のTextに指定してある値を指定してウインドウハンドルを取得
IntPtr hWnd = FindWindow(null, "Test");

// 取得したウインドウにメッセージを送信
SendMessage(hWnd, 0xAAAA, 0, 0);

■受信側


protected override void WndProc(ref Message m)
{
if (m.Msg == 0xAAAA)
{
// メッセージ受信時の処理
}

base.WndProc(ref m);
}

クライアント領域をドラッグしてフォームを移動する


マウスイベントが発生するとWM_NCHITTESTメッセージがWindowsからフォームに投げられる。
このメッセージに対してフォームは、クリックされた場所がタイトルバーであればHTCAPTION、クライアント領域であればHTCLIENTを返す。
WndProcをオーバーライドすると、クライアント領域をクリックしたときにWM_NCHITTESTメッセージを補足することができる。
そこで、WndProcをオーバーライドし、WM_NCHITTESTを補足した際にHTCAPTIONを返すことで、クライアント領域をクリックしてフォームを移動することができる。

また、このままではクライアント領域をダブルクリックしたときにフォームが最大化してしまう。
これを防ぐには、ダブルクリックのイベントを拾い、その時何もせずにreturnすればよい。


private const int WM_NCHITTEST = 0x84;
private const int HTCLIENT = 0x1;
private const int HTCAPTION = 0x2;

protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
// クライアント領域をクリックされた場合、HTCAPTIONを返す
case WM_NCHITTEST:
base.WndProc(ref m);
if ((int)m.Result == HTCLIENT)
{
m.Result = (IntPtr)HTCAPTION;
}
return;

// フォームがダブルクリックされた場合最大化しない
case WM_NCLBUTTONDBLCLK:
return;

}
base.WndProc(ref m);
}