C # एप्लिकेशन में वैश्विक कीबोर्ड कैप्चर


81

मैं अपने एप्लिकेशन में एक कीबोर्ड शॉर्टकट कैप्चर करना चाहता हूं और यह प्रदर्शित करने के लिए एक संवाद ट्रिगर करता हूं कि क्या उपयोगकर्ता ऐप के बाहर भी कीबोर्ड कॉम्बो दबाता है। Google डेस्कटॉप खोज के Ctrl के समान, खोज संवाद लाने के लिए Ctrl।

मैंने कुछ कीबोर्ड हुक मॉड्यूल का उपयोग करने की कोशिश की है जो मूल रूप से इस प्रभाव को प्राप्त करने के लिए Win32 इंटरॉप का उपयोग करते हैं, लेकिन प्रत्येक कार्यान्वयन मैंने कीबोर्ड को कुछ हद तक नीचे करने की कोशिश की है जहां आप कुछ अजीब व्यवहार करना शुरू कर देते हैं जब एप्लिकेशन कुछ गहन कर रहा होता है। जैसे कि बड़ी मात्रा में डेटा लोड करना, यह कीबोर्ड और माउस को लॉकअप करने का कारण होगा।

मैं एक हल्के समाधान की तलाश कर रहा हूं जो कीबोर्ड और माउस को बांधने के बिना ऐसा करने की अनुमति देगा।


आप बता सकते हैं कि आपने पहले से कौन से मॉड्यूल की कोशिश की थी।
स्ट्रोमेनेट 22

जवाबों:


96

स्टीफन Toub ने C # में वैश्विक कीबोर्ड हुक को लागू करने पर एक शानदार लेख लिखा :

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class InterceptKeys
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
    }

    private static IntPtr SetHook(LowLevelKeyboardProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private delegate IntPtr LowLevelKeyboardProc(
        int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Console.WriteLine((Keys)vkCode);
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
        LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

16
मैं इस वर्ग का उपयोग कैसे कर सकता हूं?
VAAA

1
किसी भी कारण से यह अजीब चरित्र लौट रहा होगा?
ब्लू आइड बेहेमोथ

@VAAA अपने समाधान के लिए एक वर्ग जोड़ें। यदि आपके पास पहले से ही Program.cs में एक मुख्य () है, तो इस श्रेणी में मुख्य () का नाम बदलकर InitializeComponent () करें और फिर इसे अपने प्रोजेक्ट App () विधि में कॉल करें। SysTrayApp ()। स्टीफन द्वारा उत्तर दिया गया अधिक प्रश्नोत्तर के लिए लिंक की गई लेख टिप्पणियाँ देखें।
जेई कार्टर II

यह आपको "एस" की तरह दबाया गया कच्ची कुंजी देता है, लेकिन यह निर्धारित करने का एक तरीका है कि किस कुंजी को डाला जाएगा, उदाहरण के लिए यदि कैप्स लॉक बंद है, तो "एस", लेकिन अगर यह चालू है, तो "एस" ?
Jez

46

यहाँ मेरा कोड काम करता है:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace SnagFree.TrayApp.Core
{
    class GlobalKeyboardHookEventArgs : HandledEventArgs
    {
        public GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; }
        public GlobalKeyboardHook.LowLevelKeyboardInputEvent KeyboardData { get; private set; }

        public GlobalKeyboardHookEventArgs(
            GlobalKeyboardHook.LowLevelKeyboardInputEvent keyboardData,
            GlobalKeyboardHook.KeyboardState keyboardState)
        {
            KeyboardData = keyboardData;
            KeyboardState = keyboardState;
        }
    }

    //Based on https://gist.github.com/Stasonix
    class GlobalKeyboardHook : IDisposable
    {
        public event EventHandler<GlobalKeyboardHookEventArgs> KeyboardPressed;

        public GlobalKeyboardHook()
        {
            _windowsHookHandle = IntPtr.Zero;
            _user32LibraryHandle = IntPtr.Zero;
            _hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.

            _user32LibraryHandle = LoadLibrary("User32");
            if (_user32LibraryHandle == IntPtr.Zero)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }



            _windowsHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
            if (_windowsHookHandle == IntPtr.Zero)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // because we can unhook only in the same thread, not in garbage collector thread
                if (_windowsHookHandle != IntPtr.Zero)
                {
                    if (!UnhookWindowsHookEx(_windowsHookHandle))
                    {
                        int errorCode = Marshal.GetLastWin32Error();
                        throw new Win32Exception(errorCode, $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
                    }
                    _windowsHookHandle = IntPtr.Zero;

                    // ReSharper disable once DelegateSubtraction
                    _hookProc -= LowLevelKeyboardProc;
                }
            }

            if (_user32LibraryHandle != IntPtr.Zero)
            {
                if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    throw new Win32Exception(errorCode, $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
                }
                _user32LibraryHandle = IntPtr.Zero;
            }
        }

        ~GlobalKeyboardHook()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private IntPtr _windowsHookHandle;
        private IntPtr _user32LibraryHandle;
        private HookProc _hookProc;

        delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

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

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool FreeLibrary(IntPtr hModule);

        /// <summary>
        /// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
        /// You would install a hook procedure to monitor the system for certain types of events. These events are
        /// associated either with a specific thread or with all threads in the same desktop as the calling thread.
        /// </summary>
        /// <param name="idHook">hook type</param>
        /// <param name="lpfn">hook procedure</param>
        /// <param name="hMod">handle to application instance</param>
        /// <param name="dwThreadId">thread identifier</param>
        /// <returns>If the function succeeds, the return value is the handle to the hook procedure.</returns>
        [DllImport("USER32", SetLastError = true)]
        static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

        /// <summary>
        /// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
        /// </summary>
        /// <param name="hhk">handle to hook procedure</param>
        /// <returns>If the function succeeds, the return value is true.</returns>
        [DllImport("USER32", SetLastError = true)]
        public static extern bool UnhookWindowsHookEx(IntPtr hHook);

        /// <summary>
        /// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
        /// A hook procedure can call this function either before or after processing the hook information.
        /// </summary>
        /// <param name="hHook">handle to current hook</param>
        /// <param name="code">hook code passed to hook procedure</param>
        /// <param name="wParam">value passed to hook procedure</param>
        /// <param name="lParam">value passed to hook procedure</param>
        /// <returns>If the function succeeds, the return value is true.</returns>
        [DllImport("USER32", SetLastError = true)]
        static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);

        [StructLayout(LayoutKind.Sequential)]
        public struct LowLevelKeyboardInputEvent
        {
            /// <summary>
            /// A virtual-key code. The code must be a value in the range 1 to 254.
            /// </summary>
            public int VirtualCode;

            /// <summary>
            /// A hardware scan code for the key. 
            /// </summary>
            public int HardwareScanCode;

            /// <summary>
            /// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
            /// </summary>
            public int Flags;

            /// <summary>
            /// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
            /// </summary>
            public int TimeStamp;

            /// <summary>
            /// Additional information associated with the message. 
            /// </summary>
            public IntPtr AdditionalInformation;
        }

        public const int WH_KEYBOARD_LL = 13;
        //const int HC_ACTION = 0;

        public enum KeyboardState
        {
            KeyDown = 0x0100,
            KeyUp = 0x0101,
            SysKeyDown = 0x0104,
            SysKeyUp = 0x0105
        }

        public const int VkSnapshot = 0x2c;
        //const int VkLwin = 0x5b;
        //const int VkRwin = 0x5c;
        //const int VkTab = 0x09;
        //const int VkEscape = 0x18;
        //const int VkControl = 0x11;
        const int KfAltdown = 0x2000;
        public const int LlkhfAltdown = (KfAltdown >> 8);

        public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            bool fEatKeyStroke = false;

            var wparamTyped = wParam.ToInt32();
            if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
            {
                object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));
                LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o;

                var eventArguments = new GlobalKeyboardHookEventArgs(p, (KeyboardState)wparamTyped);

                EventHandler<GlobalKeyboardHookEventArgs> handler = KeyboardPressed;
                handler?.Invoke(this, eventArguments);

                fEatKeyStroke = eventArguments.Handled;
            }

            return fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }
    }
}

उपयोग:

using System;
using System.Windows.Forms;

namespace SnagFree.TrayApp.Core
{
    internal class Controller : IDisposable
    {
        private GlobalKeyboardHook _globalKeyboardHook;

        public void SetupKeyboardHooks()
        {
            _globalKeyboardHook = new GlobalKeyboardHook();
            _globalKeyboardHook.KeyboardPressed += OnKeyPressed;
        }

        private void OnKeyPressed(object sender, GlobalKeyboardHookEventArgs e)
        {
            //Debug.WriteLine(e.KeyboardData.VirtualCode);

            if (e.KeyboardData.VirtualCode != GlobalKeyboardHook.VkSnapshot)
                return;

            // seems, not needed in the life.
            //if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.SysKeyDown &&
            //    e.KeyboardData.Flags == GlobalKeyboardHook.LlkhfAltdown)
            //{
            //    MessageBox.Show("Alt + Print Screen");
            //    e.Handled = true;
            //}
            //else

            if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown)
            {
                MessageBox.Show("Print Screen");
                e.Handled = true;
            }
        }

        public void Dispose()
        {
            _globalKeyboardHook?.Dispose();
        }
    }
}

4
वाह, यह कमाल है! आप Alt-F4 पर भी कब्जा कर सकते हैं और रोक सकते हैं कि एप्लिकेशन बंद हो जाएगा। आपने अपने उदाहरण में C # 6.0 का भी उपयोग किया :)
बिगजिम

1
धन्यवाद! मैंने इस कोड का उपयोग किया है और यह काम करता है। लेकिन अगर मैं थोड़ी देर के बाद कुंजी दबाता हूं, तो एक अपवाद है जिसमें कहा गया है कि प्रतिनिधि कचरा एकत्र कर रहा है, और प्रबंधित कोड को इसे जीवित रखना चाहिए। फिर एक शून्य संदर्भ अपवाद है। क्या आप इसमें मेरी सहायता कर सकते है ?
नब्जी

2
मैं जोड़ने की सिफारिश करेंगे public Keys Key { get { return (Keys)VirtualCode; } }करने के लिए LowLevelKeyboardInputEvent। यह उन वर्चुअल कोड्स से निपटने की आवश्यकता है, जो सभी को Google के पास हैं। इसके अलावा: फिर आप बदल सकते हैं VkSnapshotएक होने से intकरने के लिए Keysऔर easely सिर्फ एक कुंजी रखें। मैं एक कदम आगे गया और एक रखा public static Keys[] RegisteredKeys। ऑनप्रेस्ड आता है if (!GlobalKeyboardHook.RegisteredKeys.Contains(e.KeyboardData.Key)) return;। आप सुनिश्चित कर सकते हैं कि बस इवेंट को निकाल दिया जाए;)।
C4d

1
@ डब 10 साल बाद? मुझे यकीन नहीं है। लेकिन मुझे बात समझ में आ गई। एक साल बीत जाने के बाद मेरी टिप्पणी को देखने के बाद इसका पालन करना थोड़ा कठिन हो गया है। मैं आज शाम के लिए एक अनुस्मारक सेट करूँगा। शायद मेरे पास इसे फिर से बनाने के लिए कुछ समय होगा।
C4d

1
@ दुबे हे, यह सवाल 10 साल पुराना है। मैंने अपना संशोधित संस्करण नीचे पोस्ट किया है। इसके साथ मजे करो!
C4d


8

Dube द्वारा अनुरोध के रूप में मैं अपने संशोधित संस्करण Siarhei Kuchuk का उत्तर पोस्ट कर रहा हूँ ।
यदि आप मेरे परिवर्तनों की खोज करना चाहते हैं // EDT। मैंने इसमें से अधिकांश पर टिप्पणी की है।

स्थापित करना

class GlobalKeyboardHookEventArgs : HandledEventArgs
{
    public GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; }
    public GlobalKeyboardHook.LowLevelKeyboardInputEvent KeyboardData { get; private set; }

    public GlobalKeyboardHookEventArgs(
        GlobalKeyboardHook.LowLevelKeyboardInputEvent keyboardData,
        GlobalKeyboardHook.KeyboardState keyboardState)
    {
        KeyboardData = keyboardData;
        KeyboardState = keyboardState;
    }
}

//Based on https://gist.github.com/Stasonix
class GlobalKeyboardHook : IDisposable
{
    public event EventHandler<GlobalKeyboardHookEventArgs> KeyboardPressed;

    // EDT: Added an optional parameter (registeredKeys) that accepts keys to restict
    // the logging mechanism.
    /// <summary>
    /// 
    /// </summary>
    /// <param name="registeredKeys">Keys that should trigger logging. Pass null for full logging.</param>
    public GlobalKeyboardHook(Keys[] registeredKeys = null)
    {
        RegisteredKeys = registeredKeys;
        _windowsHookHandle = IntPtr.Zero;
        _user32LibraryHandle = IntPtr.Zero;
        _hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.

        _user32LibraryHandle = LoadLibrary("User32");
        if (_user32LibraryHandle == IntPtr.Zero)
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
        }



        _windowsHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
        if (_windowsHookHandle == IntPtr.Zero)
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new Win32Exception(errorCode, $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // because we can unhook only in the same thread, not in garbage collector thread
            if (_windowsHookHandle != IntPtr.Zero)
            {
                if (!UnhookWindowsHookEx(_windowsHookHandle))
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    throw new Win32Exception(errorCode, $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
                }
                _windowsHookHandle = IntPtr.Zero;

                // ReSharper disable once DelegateSubtraction
                _hookProc -= LowLevelKeyboardProc;
            }
        }

        if (_user32LibraryHandle != IntPtr.Zero)
        {
            if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }
            _user32LibraryHandle = IntPtr.Zero;
        }
    }

    ~GlobalKeyboardHook()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private IntPtr _windowsHookHandle;
    private IntPtr _user32LibraryHandle;
    private HookProc _hookProc;

    delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

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

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    private static extern bool FreeLibrary(IntPtr hModule);

    /// <summary>
    /// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
    /// You would install a hook procedure to monitor the system for certain types of events. These events are
    /// associated either with a specific thread or with all threads in the same desktop as the calling thread.
    /// </summary>
    /// <param name="idHook">hook type</param>
    /// <param name="lpfn">hook procedure</param>
    /// <param name="hMod">handle to application instance</param>
    /// <param name="dwThreadId">thread identifier</param>
    /// <returns>If the function succeeds, the return value is the handle to the hook procedure.</returns>
    [DllImport("USER32", SetLastError = true)]
    static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

    /// <summary>
    /// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
    /// </summary>
    /// <param name="hhk">handle to hook procedure</param>
    /// <returns>If the function succeeds, the return value is true.</returns>
    [DllImport("USER32", SetLastError = true)]
    public static extern bool UnhookWindowsHookEx(IntPtr hHook);

    /// <summary>
    /// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
    /// A hook procedure can call this function either before or after processing the hook information.
    /// </summary>
    /// <param name="hHook">handle to current hook</param>
    /// <param name="code">hook code passed to hook procedure</param>
    /// <param name="wParam">value passed to hook procedure</param>
    /// <param name="lParam">value passed to hook procedure</param>
    /// <returns>If the function succeeds, the return value is true.</returns>
    [DllImport("USER32", SetLastError = true)]
    static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential)]
    public struct LowLevelKeyboardInputEvent
    {
        /// <summary>
        /// A virtual-key code. The code must be a value in the range 1 to 254.
        /// </summary>
        public int VirtualCode;

        // EDT: added a conversion from VirtualCode to Keys.
        /// <summary>
        /// The VirtualCode converted to typeof(Keys) for higher usability.
        /// </summary>
        public Keys Key { get { return (Keys)VirtualCode; } }

        /// <summary>
        /// A hardware scan code for the key. 
        /// </summary>
        public int HardwareScanCode;

        /// <summary>
        /// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
        /// </summary>
        public int Flags;

        /// <summary>
        /// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
        /// </summary>
        public int TimeStamp;

        /// <summary>
        /// Additional information associated with the message. 
        /// </summary>
        public IntPtr AdditionalInformation;
    }

    public const int WH_KEYBOARD_LL = 13;
    //const int HC_ACTION = 0;

    public enum KeyboardState
    {
        KeyDown = 0x0100,
        KeyUp = 0x0101,
        SysKeyDown = 0x0104,
        SysKeyUp = 0x0105
    }

    // EDT: Replaced VkSnapshot(int) with RegisteredKeys(Keys[])
    public static Keys[] RegisteredKeys;
    const int KfAltdown = 0x2000;
    public const int LlkhfAltdown = (KfAltdown >> 8);

    public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        bool fEatKeyStroke = false;

        var wparamTyped = wParam.ToInt32();
        if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
        {
            object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));
            LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o;

            var eventArguments = new GlobalKeyboardHookEventArgs(p, (KeyboardState)wparamTyped);

            // EDT: Removed the comparison-logic from the usage-area so the user does not need to mess around with it.
            // Either the incoming key has to be part of RegisteredKeys (see constructor on top) or RegisterdKeys
            // has to be null for the event to get fired.
            var key = (Keys)p.VirtualCode;
            if (RegisteredKeys == null || RegisteredKeys.Contains(key))
            {
                EventHandler<GlobalKeyboardHookEventArgs> handler = KeyboardPressed;
                handler?.Invoke(this, eventArguments);

                fEatKeyStroke = eventArguments.Handled;
            }
        }

        return fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
    }
}

उपयोग अंतर यहाँ देखा जा सकता है

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private GlobalKeyboardHook _globalKeyboardHook;

    private void buttonHook_Click(object sender, EventArgs e)
    {
        // Hooks only into specified Keys (here "A" and "B").
        _globalKeyboardHook = new GlobalKeyboardHook(new Keys[] { Keys.A, Keys.B });

        // Hooks into all keys.
        _globalKeyboardHook = new GlobalKeyboardHook();
        _globalKeyboardHook.KeyboardPressed += OnKeyPressed;
    }

    private void OnKeyPressed(object sender, GlobalKeyboardHookEventArgs e)
    {
        // EDT: No need to filter for VkSnapshot anymore. This now gets handled
        // through the constructor of GlobalKeyboardHook(...).
        if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown)
        {
            // Now you can access both, the key and virtual code
            Keys loggedKey = e.KeyboardData.Key;
            int loggedVkCode = e.KeyboardData.VirtualCode;
        }
    }
}

उनके पोस्ट के लिए Siarhei Kuchuk को धन्यवाद । यहां तक ​​कि मैंने इस उपयोग को सरल बनाया है, यह प्रारंभिक कोड मेरे लिए बहुत उपयोगी था।


कोड के साथ कुछ समस्याएं थीं। सबसे पहले, क्योंकि यह एक XNA रैपर होने का अंतिम संस्करण है और मेरे पास कुछ पुराने 3D सॉफ़्टवेयर हैं, जिन्हें मैंने लिखा है, मैं अभी भी इस सहित कुछ चीजों के लिए VS2012 का उपयोग करता हूं। C # 6 ऑपरेटरों की जगह मैंने इसे आजमाने के लिए बैठाया।
२०:

1
यह काम नहीं किया लेकिन मुझे वीएस २०१ में इसकी आवश्यकता थी ताकि यह एक मुद्दा हो। मुझे लगता है कि मैंने जो समस्या पाई है वह सी # 6 में भी सच है। GlobalKeyboardHook कंस्ट्रक्टर (ऊपर) इसके साथ शुरू होता है: RegisteredKeys = registerKeys; दुर्भाग्य से पंजीकृतकेयर्स जारी नहीं रहता है और इसलिए इसके लिए सूचक रिटर्न पर अमान्य हो जाता है और उसके बाद सभी कॉल पर शून्य है। मेरा फिक्स यह था कि रास्ते में अशक्त के लिए इसकी जाँच करें, कंस्ट्रक्टर (स्थिर और लगातार) में एक समान आकार का सरणी बनाएं और इसमें पास किए गए सरणी डेटा को कॉपी करें)। बहुत बहुत धन्यवाद। विधिवत सराहना!

यह कई कीपेस के बाद फेल होता दिख रहा है? प्रबंधित डिबगिंग सहायक 'कॉलबैकऑनऑक्टेडडेलगेट' संदेश = प्रबंधित डिबगिंग असिस्टेंट 'कॉलबैकऑनकोलेक्टेडडेलेगेट': 'एक कीबोर्ड पर एकत्रित किए गए एक प्रकार का कॉलबैक टाइप किया गया था' कीबोर्ड! कीबोर्ड .GlobalKeyboardHook + HookProc :: Invoke '! इससे एप्लिकेशन क्रैश, भ्रष्टाचार और डेटा हानि हो सकती है। जब प्रतिनिधियों को अप्रबंधित कोड पारित किया जाता है, तो उन्हें प्रबंधित एप्लिकेशन द्वारा जीवित रखा जाना चाहिए जब तक कि यह गारंटी नहीं दी जाती है कि उन्हें कभी नहीं बुलाया जाएगा। '
GDutton

@ C4d हॉटकी पर कब्जा कर लिया गया है, लेकिन जब मैं MessageBox(हैंडलर सक्रिय और काम करने के लिए मान्य है) को प्रदर्शित करने के बाद मैं निम्नलिखित त्रुटि immedaitely प्राप्त कर रहा हूं : Managed Debugging Assistant 'CallbackOnCollectedDelegate' : 'A callback was made on a garbage collected delegate of type 'GlobalKeyboardHook+HookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.'मैं इस अपवाद को कैसे हल कर सकता हूं?
चाड

^ उपरोक्त अपवाद निम्न पंक्ति में निम्न पर होता है LowLeveKeyboardProc():return fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
चाड

0

मेरे प्रतिनिधि टिप्पणी के लिए बहुत कम है, लेकिन विषय में CallbackOnCollectedDelegateअपवाद है, मैं संशोधित public void SetupKeyboardHooks()में C4d के इस तरह नज़र का जवाब:

public void SetupKeyboardHooks(out object hookProc)
{
  _globalKeyboardHook = new GlobalKeyboardHook();
  _globalKeyboardHook.KeyboardPressed += OnKeyPressed;


  hookProc = _globalKeyboardHook.GcSafeHookProc;
}

ओपी में GcSafeHookProcजनता के लिए बस कहां है_hookProc

_hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.

और hookProcकक्षा में एक निजी क्षेत्र के रूप में कॉल किया जाता है SetupKeyboardHooks(...), इसलिए संदर्भ को जीवित रखते हुए, कचरा संग्रह से बचाएं, कोई और CallbackOnCollectedDelegateअपवाद नहीं। ऐसा लगता है कि GlobalKeyboardHookकक्षा में यह अतिरिक्त संदर्भ पर्याप्त नहीं है। शायद यह सुनिश्चित करें कि यह संदर्भ आपके ऐप को बंद करते समय भी निपटाया जाता है।


-3
private void buttonHook_Click(object sender, EventArgs e)
{
    // Hooks only into specified Keys (here "A" and "B").
    // (***) Use this constructor

    _globalKeyboardHook = new GlobalKeyboardHook(new Keys[] { Keys.A, Keys.B });

    // Hooks into all keys.
    // (***) Or this - not both

    _globalKeyboardHook = new GlobalKeyboardHook();
    _globalKeyboardHook.KeyboardPressed += OnKeyPressed;
}

और फिर ठीक काम कर रहा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.