C#设置全局钩子后按键会执行两次,何解?
public Form1()
{
InitializeComponent();
}
#region Other
public delegate int HookProc(int nCode, int wParam, IntPtr lParam);//委托
static int hHook = 0;//获取钩子静态变量,初始化0
public const int WH_KEYBOARD_LL = 13;//底层钩子
//LowLevel键盘截获,如果是WH_KEYBOARD=2,并不能对系统键盘截取,Acrobat Reader会在你截取之前获得键盘。
HookProc KeyBoardHookProcedure;
//键盘结构 原型
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode; //定一个虚拟键码。该代码必须有一个价值的范围1至254 。
public int scanCode; // 指定的硬件扫描码的关键。
public int flags; // 键标志
public int time; // 指定的时间戳记的这个讯息。
public int dwExtraInfo; // 指定额外信息相关的信息。
}
SinnerCode.SinnerCode Sn = new SinnerCode.SinnerCode();
private string WindowsFromTitle = null;//获取拳皇窗口标题
#endregion
#region API
//安装钩子 最好设置非托管约定
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
//卸载钩子 最好设置非托管约定
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//调用下一个钩子
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
//用作与安装时THREADID
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
//进程模版
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
//寻找目标进程窗口
[DllImport("USER32.DLL")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
//设置进程窗口到最前
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
#endregion
#region 自定义事件
public void Hook_Start()
{
// 安装键盘钩子
if (hHook == 0)
{
KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
hHook = SetWindowsHookEx(WH_KEYBOARD_LL,
KeyBoardHookProcedure,
GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
//Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0])是不行的,获取到的是0,会失败,这就是为什么需要定义GetModuleHandle的原因,注意的是IntPtr的特定句柄类型。
//如果设置钩子失败.
if (hHook == 0)
{
Hook_Clear();
throw new Exception("设置Hook失败!");
}
}
}
//卸载键盘钩子
public void Hook_Clear()
{
bool retKeyboard = true;
if (hHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hHook);
hHook = 0;
}
//如果去掉钩子失败.
if (!retKeyboard) throw new Exception("卸载钩子失败!");
}
//这里可以添加自己想要的信息处理
public static int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
{
if (nCode >= 0)
{
//问题①
WinIoSys Sw_Key = new WinIoSys();//这局代码应该做全局变量,但做了之后无法调用..?因为Sw_Key.InitSuperKeys()用了之后要关闭,暂时不知道如何解决.
Sw_Key.InitSuperKeys();
KeyboardHookStruct kbh = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (kbh.vkCode == (int)Keys.S && (int)Control.ModifierKeys == (int)Keys.Control) // 截获Ctrl+S
{
return 1;
}
if (kbh.vkCode == (int)Keys.Y
&& (int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt) //截获Ctrl+Alt+Y
{
//MessageBox.Show("不能全部保存!");
return 1;
}
//以下是测试部分
if (kbh.vkCode == (int)Keys.L)
{
Sw_Key.KeyDown(WinIoSys.Key.VK_S);
Thread.Sleep(100);
Sw_Key.KeyDown(WinIoSys.Key.VK_D);
Thread.Sleep(100);
Sw_Key.KeyUp(WinIoSys.Key.VK_S);
Thread.Sleep(100);
Sw_Key.KeyUp(WinIoSys.Key.VK_D);
Thread.Sleep(10);
Sw_Key.KeyPress(WinIoSys.Key.VK_J, 200);
return 0;
}
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
//问题②
//程序运行后sdj三个键会输入两次,把button1代码注释,留下Hook_Start()可进任意窗口行测试.
//return CallNextHookEx(hHook, nCode, wParam, lParam);这句改为:
//return 1,会只按下L,而return0的话会执行lsdjsdj
}
#endregion
private void button1_Click(object sender, EventArgs e)
{
Sn.GetProcessTitle();
for (int i = 0; i < Sn.IsProcess.Length; i++)
{
大侠们。。来吧。最后34分了。。。解决问题2就可以了。。 --------------------编程问答-------------------- 是否有冒泡判断?子控件实行一次后父控件又实行一次 --------------------编程问答--------------------
不是这个问题吧,我听说是由于按下和抬起两个动作要做个判断。。不知道怎么判断。。 --------------------编程问答--------------------
public static int asd = 1;
if (asd % 2 == 1)//加了这个判断之后,输出结果就是一次了,但是只能在记事本输出,在其他窗口就没用了。。
{
asd += 1;
if (kbh.vkCode == (int)Keys.L)
{
Sw_Key.KeyDown(WinIoSys.Key.VK_S);
Thread.Sleep(100);
Sw_Key.KeyDown(WinIoSys.Key.VK_D);
Thread.Sleep(100);
Sw_Key.KeyUp(WinIoSys.Key.VK_S);
Thread.Sleep(100);
Sw_Key.KeyUp(WinIoSys.Key.VK_D);
Thread.Sleep(10);
Sw_Key.KeyPress(WinIoSys.Key.VK_J, 200);
return 1;
}
}
else
{
asd += 1;
}
我改成了这样。。。在记事本输出是不会出现两次了,但是在其他窗口,比如KOF拳皇。。就不会按下sdj这3个键了。。。只有在记事本上能输出sdj。。。。来人吧。。。这问题我已经问了两次了。。。 --------------------编程问答-------------------- 帮顶! --------------------编程问答--------------------
谢谢。。~ --------------------编程问答--------------------
keydown和keyup,找到它们的确切定义,然后分别判断 --------------------编程问答-------------------- “KOF拳皇。。就不会按下sdj这3个键了。。。只有在记事本上能输出sdj”
很多程序,特别是游戏,为了准求对键盘鼠标输入的快速反应,使用了IO的DX技术(就是说KOF拳皇直接到键盘的中断端口去读取输入,参见相关资料),而不是一般的程序那样(例如记事本)从操作系统的消息队列里获取。可能这个是输出不了的原因。 --------------------编程问答--------------------
我单独使用的话就可以,全局钩子后就不可以。
--------------------编程问答--------------------
问题是在于我用的判断方法是不对的。屏蔽的上一个钩子应该就是按下sdj的,而后面的那个是跟复制粘贴一样。。感觉是这样。。 --------------------编程问答-------------------- 来人好吗。。。。哎。。两天了也没解决。。。
补充:.NET技术 , C#