ネイティブコードを呼び出す際の警告
マネージコードから、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);
整数を文字列に変換する
整数を文字列に変換するには、stringstreamを用いる。
16進数で文字列に変換するには、マニピュレータstd::hexを用いる。
#include
std::stringstream ss;
// マニピュレータを用いて16進数に変換
ss << std::hex << number;
タスクトレイにアイコンを表示する
タスクトレイにアイコンを表示するには、ツールバーの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);
}