0

キーボードフックコードは次のとおりです。

   class globalKeyboardHook
        {
            #region Constant, Structure and Delegate Definitions

            public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
            public static keyboardHookProc callbackDelegate;

            public struct keyboardHookStruct
            {
                public int vkCode;
                public int scanCode;
                public int flags;
                public int time;
                public int dwExtraInfo;
            }

            const int WH_KEYBOARD_LL = 13;
            const int WM_KEYDOWN = 0x100;
            const int WM_KEYUP = 0x101;
            const int WM_SYSKEYDOWN = 0x104;
            const int WM_SYSKEYUP = 0x105;
            #endregion

            #region Instance Variables

            public List<Keys> HookedKeys = new List<Keys>();
            private static IntPtr hhook = IntPtr.Zero;

            #endregion

            #region Events

            public event KeyEventHandler KeyDown;
            public event KeyEventHandler KeyUp;

            #endregion

            #region Constructors and Destructors

            public globalKeyboardHook()
            {
                hook();
                GC.KeepAlive(callbackDelegate);
            }

            ~globalKeyboardHook()
            {
                unhook();
            }

            #endregion

            #region Public Methods

            public void hook()
            {
                IntPtr hInstance = LoadLibrary("User32");
                callbackDelegate = new keyboardHookProc(hookProc);
                hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
                if (hhook == IntPtr.Zero) throw new Win32Exception();
            }

            public void unhook()
            {
                bool ok = UnhookWindowsHookEx(hhook);
                if (!ok) throw new Win32Exception();
                callbackDelegate = null;
            }

            public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
            {
                if (code >= 0)
                {
                    Keys key = (Keys)lParam.vkCode;
                    if (HookedKeys.Contains(key))
                    {
                        KeyEventArgs kea = new KeyEventArgs(key);
                        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
                        {
                            KeyDown(this, kea);
                        }
                        else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
                        {
                            KeyUp(this, kea);
                        }
                        if (kea.Handled)
                            return 1;
                    }
                }
                return CallNextHookEx(hhook, code, wParam, ref lParam); // Exception on this line
            }
            #endregion

            #region DLL imports

            [DllImport("user32.dll")]
            static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);

            [DllImport("user32.dll")]
            static extern bool UnhookWindowsHookEx(IntPtr hInstance);

            [DllImport("user32.dll")]
            static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);

            [DllImport("kernel32.dll")]
            static extern IntPtr LoadLibrary(string lpFileName);
            #endregion
        }

子フォームコンストラクターで最初に初期化すると、何も起こらず、必要なだけ押すことができますが、イベントには何も到達しません:

private globalKeyboardHook kh = new globalKeyboardHook();

public FrmMainForm(FrmVideo owner)
        {
            InitializeComponent();
            _videoForm = owner;

            try
            {
                _videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

                if (_videoDevices.Count == 0)
                {
                    throw new ApplicationException();
                }

                foreach (FilterInfo device in _videoDevices)
                {
                    camerasCombo.Items.Add(device.Name);
                }

                camerasCombo.SelectedIndex = 1;
            }
            catch (ApplicationException)
            {
                camerasCombo.Items.Add("No local capture devices");
                _videoDevices = null;
            }

            kh.KeyUp += new KeyEventHandler(kh_KeyUp);
            kh.hook();
        }


    void kh_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.F12)
            _videoForm.TakeOver = !_videoForm.TakeOver;

        if (e.KeyCode == Keys.Escape)
            Application.Exit();
    }

ウェブカメラからキャプチャするために Aforge コードを呼び出すまで、これは親フォームでストリーミングを開始します。

  var videoSource = new VideoCaptureDevice(_videoDevices[camerasCombo.SelectedIndex].MonikerString)
        {
            DesiredFrameSize = new Size(Globals.FrameWidth, Globals.FrameHeight),
            DesiredFrameRate = 12
        };

        if (videoSource != null)
        {
            _videoForm.Device = videoSource;
            _videoForm.Start();
            button1.Enabled = false;
        }

その後、キーを押すと、次のフックが返されたときに CallbackOnCollectedDelegate が検出されました。

ああ、私はフォームの破棄でフックを外しています:

   protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            kh.unhook();
            base.Dispose(disposing);
        }
4

1 に答える 1

2

これには精神的なデバッグが必要であり、文書化されていないものを推測します。奇妙なことに、CallNextHookEx() を呼び出すと MDA がトリガーされます。これはやや奇妙ですが、フック プロシージャへのコールバックは実際に機能しました。そのデリゲート オブジェクトは収集されませんでした。失敗したのは次のフック プロシージャ コールです。

これには簡単な説明があります: SetWindowsHookEx() を複数回呼び出しました。デリゲート オブジェクトを格納するために静的変数を使用すると、深刻な問題が発生します。静的変数が通常行うように、格納できるデリゲート オブジェクトは1 つだけです。hook() を 2 回目に呼び出すと、最初のフックのデリゲートが上書きされるため、ガベージ コレクションが妨げられなくなります。これは実際に CallNextHookEx() で MDA をトリガーします。これは、最初のフックのフック プロシージャを呼び出すためです。

したがって、hook() メソッドを次のように改善する必要があります。

public void hook()
{
    if (callbackDelegate != null) 
        throw new InvalidOperationException("Cannot hook more than once");
    // etc..
}

実際には、複数回フックすることは違法ではなく、Windows は気にしません。変数 static を宣言しないでください。

于 2012-12-16T16:32:32.773 に答える