我有一个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);
答案 0 :(得分:5)
your code中存在大量琐碎的问题,所有这些问题都在不知道为什么它不会在另一台机器上运行。浏览来源:
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上不是问题,它接受模块句柄的 null 值,但在早期版本上存在问题。
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
没关系,但是如果另一个键盘钩子首先拦截击键怎么办?你当然会失败。
if (pressedKey == Keys.F11 || pressedKey == Keys.F12)
我强烈建议你不使用低级键盘钩来捕捉两次击键。更好的捕鼠器是RegisterHotKey()。您可以轻松搜索示例代码。自动解决"另一个应用程序无论如何都能看到键击"问题
嗯,这可能就是其中之一,我猜不出哪个:)祝你好运。
答案 1 :(得分:3)
您是否尝试过将lParam设置为null?自从我完成密钥处理以来已经有一段时间了,所以不确定是否最好设置为null或IntPtr.Zero。其中一个应该工作:
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
lParam = null;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
答案 2 :(得分:1)
这可能不是您期望的解决方案,但无论如何我都会发布它。
我使用不同的方法捕获此类事件,Windows提供了注册"全局热键"无论目标应用程序是否正在运行,它都会在任何地方被截获(实际上它已经连接到操作系统,而不是单个应用程序)。
您可以尝试使用可以这样调用的代码段https://gist.github.com/bruce965/87caacfb0289d0ad1b3c:
new HotkeyHandler(false, false, false, false, Keys.F11).Register(yourForm);
new HotkeyHandler(false, false, false, false, Keys.F12).Register(yourForm);
其中yourForm
是System.Windows.Forms.Form
,代码如下:
protected override void WndProc(ref Message m) {
if(m.Msg == HotkeyHandler.WM_HOTKEY_MSG_ID) {
// Handle hotkey
}
base.WndProc(ref m);
}
您不需要将表单打开或显示,只需确保它与以下内容一起运行:
Application.Run(yourForm);
从链接的代码段中进行快速参考:
[DllImport("user32.dll")]
static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
// make sure the second parameter '1234' is unique for every registered hotkey
RegisterHotKey(yourForm.Handle, 1234, 0, (int) Keys.F11);