C#检测特定进程时禁用指定按键失败:钩子设置问题排查
Fixing Low-Level Keyboard Hook Issues for Disabling Keys in Notepad.exe
Let's break down what's going wrong with your low-level keyboard hook implementation and fix it step by step. Your goal is to disable G, H, S keys only when Notepad is active, but several issues in your code are preventing this from working.
Key Issues in Your Code
- Incorrect Module Handle for Hook: For
WH_KEYBOARD_LL(low-level keyboard hook), you don't need to pass the target process (Notepad)'s module handle. This type of hook runs in the context of your own process, so you should use your application's module handle (orGetModuleHandle(null)which gets the current process's executable module). - Uninitialized
ptrHook: You never assign the return value ofSetWindowsHookExtoptrHookin yourControlmethod. This meansCallNextHookExandUnhookWindowsHookExare using an uninitialized pointer, breaking the hook chain. - Thread Start Typo:
cont.start()should becont.Start()(C# is case-sensitive), which would cause a compile error. - No Check for Running Notepad:
Process.GetProcessesByName("notepad")[0]will throw an exception if Notepad isn't running when your app starts. - Global Key Blocking: Your current code blocks the keys everywhere, not just in Notepad. You need to check if the active window belongs to a Notepad process before blocking keys.
Fixed Implementation
Here's the corrected code that addresses all these issues:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; namespace NotepadKeyBlocker { public partial class Form1 : Form { [StructLayout(LayoutKind.Sequential)] private struct KBDLLHOOKSTRUCT { public Keys key; public int scanCode; public int flags; public int time; public IntPtr extra; } // Windows API imports private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr 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)] 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); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); // Global hook variables private static IntPtr _ptrHook = IntPtr.Zero; private static LowLevelKeyboardProc _keyboardProc; private const int WH_KEYBOARD_LL = 13; public Form1() { InitializeComponent(); // Start the control thread with proper case and background flag Thread controlThread = new Thread(ControlNotepadKeys); controlThread.IsBackground = true; // Ensures thread exits when app closes controlThread.Start(); } private void ControlNotepadKeys() { MessageBox.Show("Control thread started"); // Wait for Notepad to launch (adjust polling interval as needed) Process notepadProcess = null; while (notepadProcess == null) { Process[] processes = Process.GetProcessesByName("notepad"); if (processes.Length > 0) { notepadProcess = processes[0]; } Thread.Sleep(1000); // Check every second } // Initialize hook with current process's module handle _keyboardProc = new LowLevelKeyboardProc(CaptureKey); using (Process currentProcess = Process.GetCurrentProcess()) using (ProcessModule currentModule = currentProcess.MainModule) { _ptrHook = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardProc, GetModuleHandle(currentModule.ModuleName), 0); } // Keep the thread alive to maintain the hook Application.Run(); } private static IntPtr CaptureKey(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT keyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); // Verify active window belongs to Notepad IntPtr foregroundWindow = GetForegroundWindow(); GetWindowThreadProcessId(foregroundWindow, out uint processId); Process foregroundProcess = Process.GetProcessById((int)processId); if (foregroundProcess.ProcessName.Equals("notepad", StringComparison.OrdinalIgnoreCase)) { // Block specified keys if (keyInfo.key == Keys.G || keyInfo.key == Keys.H || keyInfo.key == Keys.S) { return (IntPtr)1; // Return 1 to block the key event } } } // Pass event to next hook in the chain if not blocked return CallNextHookEx(_ptrHook, nCode, wParam, lParam); } protected override void OnFormClosing(FormClosingEventArgs e) { base.OnFormClosing(e); // Clean up hook to prevent memory leaks if (_ptrHook != IntPtr.Zero) { UnhookWindowsHookEx(_ptrHook); _ptrHook = IntPtr.Zero; } } } }
Key Fixes Explained
- Correct Hook Initialization: We now use the current process's module handle when setting the hook, which is required for
WH_KEYBOARD_LLhooks. - Saved Hook Handle: The return value from
SetWindowsHookExis stored in_ptrHook, ensuringCallNextHookExandUnhookWindowsHookExwork as intended. - Notepad Process Safety: We add a loop to wait for Notepad to launch, avoiding index-out-of-range exceptions if Notepad isn't running at app startup.
- Active Window Check: Keys are only blocked when the foreground window belongs to a Notepad process, so your regular system functionality remains unaffected.
- Proper Thread Management: The control thread is marked as background, and we explicitly unhook the keyboard when the form closes to clean up resources.
- Fixed Thread Start:
controlThread.Start()uses correct C# casing to resolve compile errors.
内容的提问来源于stack exchange,提问作者Edson Smith
相关产品推荐
相关产品推荐

