WndProc を Hook する

前述のように,Clipboard が更新されたことを知るためには,対象となるフォームのウィンドウプロシージャを override する必要があります.でも,何となく Clipboard のために override した WndProc を,Form のコードに書きたくないなぁ,とか思ったので,ウィンドウプロシージャを Hook することにしました.調べてみると,NativeWindow クラス (System.Windows.Forms) という,便利なクラスがあります.


これを使ってみることにしました.

public class ClipboardHelper
{
    #region イベント定義

    /// <summary>
    /// クリップボードの内容が更新されたときに発生するイベント
    /// </summary>
    public event EventHandler DrawClipboard = null;

    protected void OnDrawClipboard()
    {
        if (this.DrawClipboard != null)
        {
            this.DrawClipboard(this, new EventArgs());
        }
    }

    #endregion

    /// <summary>
    /// ウィンドウプロシージャをHookするクラス
    /// </summary>
    private class Hook : NativeWindow
    {
        private ClipboardHelper _helper = null;

        private IntPtr nextHandle = IntPtr.Zero;
        private const int WM_DRAWCLIPBOARD = 0x0308;
        private const int WM_CHANGECBCHAIN = 0x030D;

        [DllImport("user32.dll")]
        private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
        [DllImport("user32.dll")]
        private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private extern static int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);


        public Hook(Form target, ClipboardHelper helper)
        {
            target.Load += new EventHandler(target_Load);
            target.FormClosed += new FormClosedEventHandler(target_FormClosed);
            target.HandleCreated += new EventHandler(target_HandleCreated);
            target.HandleDestroyed += new EventHandler(target_HandleDestroyed);

            this._helper = helper;
        }

        void target_Load(object sender, EventArgs e)
        {
            this.nextHandle = SetClipboardViewer(((Form)sender).Handle);
        }

        void target_FormClosed(object sender, FormClosedEventArgs e)
        {
            ChangeClipboardChain(((Form)sender).Handle, this.nextHandle);
        }

        void target_HandleCreated(object sender, EventArgs e)
        {
            AssignHandle(((Form)sender).Handle);
        }

        void target_HandleDestroyed(object sender, EventArgs e)
        {
            ReleaseHandle();
        }


        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    
                    this._helper.OnDrawClipboard();

                    if (this.nextHandle != IntPtr.Zero)
                    {
                        SendMessage(this.nextHandle, m.Msg, m.WParam, m.LParam);
                    }
                    break;

                case WM_CHANGECBCHAIN:
                    if ((IntPtr)m.WParam == this.nextHandle)
                    {
                        this.nextHandle = (IntPtr)m.LParam;
                    }
                    else if (this.nextHandle != IntPtr.Zero)
                    {
                        SendMessage(this.nextHandle, m.Msg, m.WParam, m.LParam);
                    }
                    break;
            }
            base.WndProc(ref m);
        }
    }
    private Hook hook = null;

    public ClipboardHelper(Form wnd)
    {
        this.hook = new Hook(wnd, this);
    }
}