如何解决如何停止在C#
我正在尝试创建一个将对全局热键作出反应的C#应用程序; ALT+H
,然后在我松开ALT
键时。这实际上确实可以很好地工作,但是我的问题是,当我的应用程序完成了对热键应执行的操作后,它将阻止该热键被其他应用程序处理。我从下面的StackOverflow帖子Global keyboard capture in C# application中获得了大部分代码,但是我在其他地方看到return (IntPtr)1;
应该能够停止进一步处理密钥。
但是...当我在Word或写字板,然后按ALT+H
,然后Word显示所有类型的菜单-我不想要它,因为我只希望我的应用程序执行某项操作:-)
对于这段相当长的代码,我感到很抱歉,但是我认为它很重要,因此您可以获得完整的概述。我只有1个使用return (IntPtr)1;
的地方。
我的代码:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
namespace MyNewProgram
{
static class Program
{
// Enable hotkeys
// https://stackoverflow.com/a/604417/2028935
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_SYSKEYUP = 0x0105;
private const int VK_SHIFT = 0x10;
private const int VK_MENU = 0x12;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
// Other variables
public static bool isAltpressedInThisApp = false;
private static MainWindow MyMainWindow;
// --------------------------------------------------------------------------------------
[STAThread] // STAThreadAttribute indicates that the COM threading model for the application is single-threaded apartment,https://stackoverflow.com/a/1361048/2028935
static void Main()
{
// Hook application to keyboard
_hookID = SetHook(_proc);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MyMainWindow = new MainWindow();
Application.Run(MyMainWindow);
// Unhook application from keyboard
UnhookWindowsHookEx(_hookID);
}
// --------------------------------------------------------------------------------------
// required functions for globally hooking the keyboard
[DllImport("user32.dll",CharSet = CharSet.Auto,SetLastError = true)]
public static extern short GetKeyState(int keyCode);
[DllImport("user32.dll",SetLastError = true)]
private static extern IntPtr SetwindowsHookEx(int idHook,LowLevelKeyboardProc lpfn,IntPtr hMod,uint dwThreadId);
[DllImport("user32.dll",SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll",SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk,int nCode,IntPtr wParam,IntPtr lParam);
[DllImport("kernel32.dll",SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private delegate IntPtr LowLevelKeyboardProc(int nCode,IntPtr lParam);
// --------------------------------------------------------------------------------------
// Hook the keyboard - action
private static IntPtr HookCallback(int nCode,IntPtr lParam)
{
// React on KEYDOWN
if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)WM_SYSKEYUP)))
{
int vkCode = Marshal.ReadInt32(lParam);
// H
if ((Keys)vkCode == Keys.H)
{
isAltpressedInThisApp = true;
// Is ALT pressed down
if ((GetKeyState(VK_MENU) & 0x8000) != 0)
{
Console.WriteLine("ALT + H");
return (IntPtr)1; // do not allow others to hook this key combo
}
}
}
// React on KEYUP
if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
{
int vkCode = Marshal.ReadInt32(lParam);
// Is ALT not pressed down
if ((Keys)vkCode == Keys.LMenu)
{
Console.WriteLine("ALT UP");
}
}
return CallNextHookEx(_hookID,nCode,wParam,lParam);
}
// --------------------------------------------------------------------------------------
// Hook the keyboard
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);
}
}
}
}
我不是一个扎实的C#开发人员,因为我在这里刚迈出了第一步-那么为什么我要把自己放在全球热键的深水里作为首要任务之一;-)有没有人可以提供一些提示在这里,因为我可能在某个地方犯了一个愚蠢的错误?
解决方法
背后的想法是,每当按下alt键时,它就会开始将键吞入一个本地列表中。如果已经看到我们需要查看的模式,它将不会发送,但是对于其他所有模式,它将开始按照接收的顺序再次发送密钥。
考试阶段代码:
enum WM
{
WM_KEYDOWN = 0x0100,WM_KEYUP = 0x0101,WM_SYSKEYUP = 0x0105,WM_SYSKEYDOWN = 0x0104,}
private static IntPtr HookCallback(int nCode,IntPtr wParam,IntPtr lParam)
{
if (nCode < 0)
return CallNextHookEx(_hookID,nCode,wParam,lParam);
KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam,typeof(KBDLLHOOKSTRUCT));
Enum.TryParse<Keys>($"{kbd.vkCode}",out Keys key);
Enum.TryParse<WM>($"{wParam}",out WM message);
Console.WriteLine($"{message}:{key}");
return CallNextHookEx(_hookID,lParam);
}
当按下alt + h并释放时,输出:
WM_SYSKEYDOWN:LMenu
WM_SYSKEYDOWN:H
WM_SYSKEYUP:H
WM_KEYUP:LMenu
如您所见,Windows同时发送了alt和h键。您提供的代码只能捕获H键,因此接收键盘消息的窗口会认为已按下Alt键。
没有人可以获取和过滤掉先前按下的键,因此只要看到按下键,我们就需要捕捉alt键。如果下一个键不是H,则应按收到的顺序发送已获取的键。
我已经写了下面的代码来处理这种情况,但是我不太确定它如何在真实的Windows机器上工作,因为我有一个osx操作系统,因为我在虚拟机中运行Windows。按下时也可以更改击键。
如果仍然不够,您可以等待,我也许可以尝试在真正的Windows机器中在我的办公室解决它。但我认为您可以理解并自行解决。
[StructLayout(LayoutKind.Sequential)]
public class KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public KBDLLHOOKSTRUCTFlags flags;
public uint time;
public UIntPtr dwExtraInfo;
}
[Flags]
public enum KBDLLHOOKSTRUCTFlags : uint
{
LLKHF_EXTENDED = 0x01,LLKHF_INJECTED = 0x10,LLKHF_ALTDOWN = 0x20,LLKHF_UP = 0x80,}
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk,byte bScan,uint dwFlags,UIntPtr dwExtraInfo);
enum WM
{
WM_KEYDOWN = 0x0100,}
private static void ReSendKeys(int index = -1)
{
_index = -1;
var copiedKeys = _swallowedKeys.ToArray();
for (int i = 0; i < copiedKeys.Length; ++i)
{
bool up = copiedKeys[i].Item1 == (IntPtr)WM.WM_SYSKEYUP || copiedKeys[i].Item1 == (IntPtr)WM.WM_KEYUP;
keybd_event((byte)copiedKeys[i].Item2.vkCode,(byte)copiedKeys[i].Item2.scanCode,up ? 2u : 0u,UIntPtr.Zero);
}
_index = index;
_swallowedKeys.Clear();
}
private static List<Tuple<IntPtr,KBDLLHOOKSTRUCT>> _swallowedKeys = new List<Tuple<IntPtr,KBDLLHOOKSTRUCT>>();
private static int _index = 0;
private static IntPtr HookCallback(int nCode,lParam);
KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam,out WM message);
Console.Write($"{message}:{key}");
// we know that when _index is -1,ReSendKeys function has been called
// so do not filter out first alt key
if (_index == -1)
{
_index++;
Console.WriteLine();
return CallNextHookEx(_hookID,lParam);
}
// we are at the beginning of the sequence we will catch
// if it's alt key filter it out,and increment the variable
if (_index == 0)
{
_swallowedKeys.Add(new Tuple<IntPtr,KBDLLHOOKSTRUCT>(wParam,kbd));
if (message == WM.WM_SYSKEYDOWN && key == Keys.LMenu)
{
_index++;
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
else
{
_swallowedKeys.Clear();
// do nothing
}
}
if (_index == 1)
{
_swallowedKeys.Add(new Tuple<IntPtr,kbd));
// if the next key is H,then filter it out also
if (message == WM.WM_SYSKEYDOWN && key == Keys.H)
{
_index++;
_swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
// if not,we filtered out wrong sequence,we need to resend them
else
{
Console.WriteLine();
ReSendKeys();
return (IntPtr)1;
}
}
if (_index == 2)
{
_swallowedKeys.Add(new Tuple<IntPtr,kbd));
if (message == WM.WM_SYSKEYUP && key == Keys.H)
{
_index++;
_swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);
Console.WriteLine(" filtered out");
return (IntPtr)1;
}
else
{
// if user pressed H but not released and pressed another key at the same time
// i will pass that situation,if u need to handle something like that,you got the idea,please fill that block of code
}
}
if (_index == 3)
{
_swallowedKeys.Add(new Tuple<IntPtr,kbd));
if (message == WM.WM_KEYUP && key == Keys.LMenu)
{
_index = 0;
_swallowedKeys.Clear();
Console.WriteLine(" filtered out");
Console.WriteLine("shortcut disabled");
return (IntPtr)1;
}
else
{
Console.WriteLine();
// user has been pressed Alt + H,H released but not alt,so we can expect one H again,but we need to send this pressed key with Alt prefixed
ReSendKeys(1);
return (IntPtr)1;
}
}
Console.WriteLine();
return CallNextHookEx(_hookID,lParam);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。