しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

.NET Compact Framework で Win32 メッセージを処理する

.NET Compact Framework に用意されている Form クラスには WndProc メソッドが存在しないので、ウィンドウプロシージャの処理をオーバーライドできません。

Win32 メッセージを処理するためには Microsoft.WindowsCE.Form.EventWindow を使えば、WndProc をオーバーライドできるのでこれを使えばいいやと思ってたんですが、こいつは Load など標準的なイベント用意されてないし Application.Run に指定することもできません。

まあ、System.Windows.Forms.Form を継承してメインのウィンドウを作って、EventWindow を継承したクラスをメンバとして持てばいい話なんですけど、わざわざメッセージ処理だけのためにクラス作ったりするのは結構めんどくさいです。

なので、Form のウィンドウプロシージャを入れ替えてしまう方法をとることにしました。いろいろ端折っていますが、一般的な Win32 API のみ使っているので検索してみてください。

private delegate IntPtr WndProc(IntPtr hwnd, int msg, uint wParam, int lParam);

private void Form_HandleCreated(object sender, EventArgs e)
{
    Form form = sender as Form;

    oldWndProc = NativeMethods.SetWindowLong(form.Handle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(new WndProc(WindowProc)));
}

HandleCreated イベントは名前のままで、ウィンドウハンドルが作成された時に呼び出されるので、このタイミングで SetWindowLong を使ってあらかじめ用意しておいた、WndProc デリゲートでウィンドウプロシージャを入れ替えてやります。

その時の SetWindowLong の戻り値は元のプロシージャのポインタなので、保存しておく必要があります。

private void Form_HandleDestroyed(object sender, EventArgs e)
{
    NativeMethods.SetWindowLong(form.Handle, GWL_WNDPROC, oldWndProc);
}

そして忘れないように HandleDestroyed で元のウィンドウプロシージャに戻してあげます。

private int WindowProc(IntPtr hwnd, int msg, int wParam, int lParam)
{
    return NativeMethods.CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
}

そして置き換えたプロシージャ内では、メッセージを処理しなかったときには CallWindowProc を使って元々のプロシージャを呼び出さないと駄目です。

この辺は Win32 API のリファレンスを参考にしてみてください。