我有一个C#控制台应用程序,旨在在后台运行并捕获按键事件,如果它与我的热键匹配,我想做一些操作,而不是将该键传递给活动的应用程序.
在我的开发机器上,我可以在没有visual studio的情况下运行build exe文件,我的程序按预期工作.当我在任何应用程序中的任何地方键入热键(f11或f12)时,捕获该键事件并且不将其传递给活动应用程序.当我将exe部署到另一台机器,相同的操作系统(Windows 8.1 Pro)时,检测到按键,我可以"做某事",(参见代码),然后将其传递给活动应用程序.这不是我想要的操作,也不是我在开发机器上遇到的操作.我的具体问题是,为了将这个应用程序部署到其他机器,我还需要做些什么,以便它们不仅可以捕获按键事件,还不会传递给活动应用程序?
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); public 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); } } public static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) { Keys pressedKey = (Keys)Marshal.ReadInt32(lParam); if (pressedKey == Keys.F11 || pressedKey == Keys.F12) { // Do something... // Don't pass the key press on to the system return (System.IntPtr)1; } } return CallNextHookEx(_hookID, nCode, wParam, lParam); } [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName);
Hans Passant.. 5
你的代码中存在大量的琐碎问题,所有这些问题都不知道为什么它不能在另一台机器上运行.浏览来源:
static bool debug = false;
您没有机会诊断该变量设置为false的任何内容.您捕获异常并且不显示异常消息和堆栈跟踪.你像蝙蝠一样盲目.这应该是一个配置项.
static string _com = "COM3";
永远不要硬编码串口号码.它与其他USB仿真器和另一个驱动程序在另一台机器上仍然是COM3的可能性非常低.你必须使它成为一个配置项.
setConsoleWindowVisibility(false);
你失去了看诊断的唯一机会.这需要if(!debug)在它前面.
_hookID = SetHook(_proc);
根本没有错误检查.如果SetWindowsHookEx()失败,那么你永远不会知道.Pinvoking winapi函数总是需要进行错误检查,您不再需要友好的.NET异常来避免麻烦.当winapi调用失败时抛出Win32Exception.
scaleSerialPort.Handshake = Handshake.None;
串行端口设备始终使用握手.他们会注意你的DTR和RTS信号,并且在关闭时不会发送任何信号.在调试代码时很容易看不到这一点,您将使用终端仿真器程序来打开这些信号.不会在另一台机器上工作.您必须显式将DtrEnable和RtsEnable属性设置为true.
if (scaleSerialPort.IsOpen) scaleSerialPort.Close(); try { scaleSerialPort.Open(); open = true; }
这是永远不正确的,SerialPort.Close()的MSDN文章特别警告这一点.SerialPort使用工作线程来引发其事件,Close()方法不会等到该线程完成.尝试立即再次打开它将始终失败,该工作线程仍然打开该端口.永远不要这样做,在应用程序启动时打开端口,直到它结束才关闭它.
scaleSerialPort.ReadTimeout = 200;
强烈避免使用超时,这会导致程序变得紧张,因为机器会忙碌或者您的进程的重要部分被分页.您完全不应该使用超时,因为您依赖于DataReceived.如果你想要使用它们,那么总是比最坏的情况大10倍.不要低于5000.
catch (Exception e) { if (debug) Console.WriteLine(e); Console.WriteLine("Could not connect to scale."); SendKeys.SendWait("^a"); SendKeys.SendWait("Error-C23"); }
永远不要这样做.如果您无法打开端口,那么保持程序运行绝对没有意义.一定要大声崩溃,为AppDomain.CurrentDomain.UnhandledException写一个事件处理程序来讲述这个故事. 永远不要戳那样的击键,你不知道他们去哪里.
Double.TryParse(...);
此方法的返回值不是可选的.继续前进并忽略失败的转换只会产生无法识别的错误行为.
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
.NET 4.0不再模拟托管代码的模块句柄.您应该使用LoadLibrary("user32")来获取有效的句柄.在Windows 8上不是问题,它接受模块句柄的空值,但在早期版本上存在问题.
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
这没关系,但是如果另一个键盘钩子首先拦截击键怎么办?你当然会失败.
if (pressedKey == Keys.F11 || pressedKey == Keys.F12)
我会强烈建议你不使用低级别的键盘钩子正好赶上两次击键.更好的捕鼠器是RegisterHotKey().您可以轻松搜索示例代码.自动解决"另一个应用程序无论如何看到击键"的问题.
嗯,这可能是其中之一,我猜不出哪个:)祝你好运.