您的位置:首页 > 编程语言 > C#

C#强化系列文章二:在C#中使用钩子

2008-09-17 22:25 369 查看
相信以前用过VB、Delphi,特别是VC的程序员应该对钩子程序都不陌生。在C#中我们同样可以使用钩子程序来实现特殊效果,比如当用户按下某个特殊键时提示,比如关闭应用程序前提示等。
当然使用方法相对VC来说要稍微复杂一点,有的地方还不太方便,下面的例子中实现两个基本功能:
1、按下Alt+F4时使窗口最小化
2、关闭应用程序前提示

不过目前只能捕获消息,不能屏蔽消息,我正在实验,也希望知道的高手能多多指教

一、加入winuser.h中的定义
因为钩子程序一般情况下都是在vc下使用的,在c#里面并没有对应的方法、结构等的定义,我们首先需要把winuser.h中的相关定义加入自己的类




钩子类型的枚举


public enum HookType : int






{


WH_JOURNALRECORD = 0,


WH_JOURNALPLAYBACK = 1,


WH_KEYBOARD = 2,


WH_GETMESSAGE = 3,


WH_CALLWNDPROC = 4,


WH_CBT = 5,


WH_SYSMSGFILTER = 6,


WH_MOUSE = 7,


WH_HARDWARE = 8,


WH_DEBUG = 9,


WH_SHELL = 10,


WH_FOREGROUNDIDLE = 11,


WH_CALLWNDPROCRET = 12,


WH_KEYBOARD_LL = 13,


WH_MOUSE_LL = 14


}具体的说明在msdn中都可以查到,主要的比如WH_KEYBOARD是监控按键事件,WH_CALLWNDPROC是在消息触发时执行




虚键值的定义


public enum VirtualKeys






{


VK_SHIFT = 0x10,


VK_CONTROL = 0x11,


VK_MENU = 0x12, //ALT


VK_PAUSE = 0x13,


VK_CAPITAL = 0x14


}这个不用说明了,对应ALT、CTRL等键




消息结构体


public struct CWPSTRUCT






{


public IntPtr lparam;


public IntPtr wparam;


public int message;


public IntPtr hwnd;


}这个是windows内部传递过来的消息的结构

二、加入自己定义的委托和事件参数




钩子委托


public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);


public delegate void HookEventHandler(object sender, HookEventArgs e);


HokkProc是SetWindowsHookEx调用时的委托事件,HookEventHandler是自己的委托事件




钩子事件参数


public class HookEventArgs : EventArgs






{


public int HookCode;


public IntPtr wParam;


public IntPtr lParam;


public Keys key;


public bool bAltKey;


public bool bCtrlKey;


}是自己的委托事件中接受的事件参数

三、实现自己的钩子类
这一步是最重要的,要使用钩子,我们需要引用user32.dll中的相应方法:


[DllImport("user32.dll")]


static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);




[DllImport("user32.dll")]


static extern bool UnhookWindowsHookEx(IntPtr hhk);




[DllImport("user32.dll")]


static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);




[DllImport("user32.dll")]


static extern short GetKeyState(VirtualKeys nVirtKey);


SetWindowsHookEx是注册一个钩子程序,UnhookWindowsHookEx是释放钩子程序,CallNextHookEx调用钩子的后续事件处理,GetKeyState得到所按的虚键

然后就可以调用这些方法来实现钩子程序,比如注册一个钩子可以调用:


m_hook = SetWindowsHookEx(m_hooktype, m_hookproc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());


其中m_hooktype就是HookType中定义的类型,m_hookproc就是实际的钩子处理程序:


m_hookproc = new HookProc(KeyHookProcedure);
最关键的就是KeyHookProcedure等钩子处理程序:


protected int KeyHookProcedure(int code, IntPtr wParam, IntPtr lParam)






{


if (code != 0)






{


return CallNextHookEx(m_hook, code, wParam, lParam);


}




if (HookInvoked != null)






{


Keys key = (Keys)wParam.ToInt32();


HookEventArgs eventArgs = new HookEventArgs();


eventArgs.key = key;


eventArgs.lParam = lParam;


eventArgs.wParam = wParam;


eventArgs.HookCode = code;


eventArgs.bAltKey = GetKeyState(VirtualKeys.VK_MENU) <= -127;


eventArgs.bCtrlKey = GetKeyState(VirtualKeys.VK_CONTROL) <= -127;


HookInvoked(this, eventArgs);


}




return CallNextHookEx(m_hook, code, wParam, lParam);


}在这个事件中可以取得消息的参数,特别是按键的值,然后通过HookInvoked委托调用事件实际的处理程序

四、在应用程序中调用钩子类
我们可以在自己的form中声明两个钩子对象


private MyHook callProcHook = new MyHook(HookType.WH_CALLWNDPROC);


private MyHook keyHook = new MyHook(HookType.WH_KEYBOARD);


然后在初始化时注册钩子:


private void Form1_Load(object sender, EventArgs e)






{


keyHook.HookInvoked += new HookEventHandler(keyHook_HookInvoked);


keyHook.Install();




callProcHook.HookInvoked += new HookEventHandler(callProcHook_HookInvoked);


callProcHook.Install();


}
然后就是实际的钩子事件:


private void keyHook_HookInvoked(object sender, HookEventArgs e)






{


if (e.key == Keys.F4 && e.bAltKey) //Alt + F4






{


this.WindowState = FormWindowState.Minimized;


}


}




private void callProcHook_HookInvoked(object sender, HookEventArgs e)






{


unsafe






{


CWPSTRUCT* message = (CWPSTRUCT*)e.lParam;


if (message != null)






{


if (message->message == WM_CLOSE)






{


(sender as MyHook).CallNextProc = false;


MessageBox.Show("程序即将关闭!");


}


}


}


}
这样我们就可以通过钩子实现一些相对底层的应用。

代码说的有点乱,我就把最主要的代码直接列在下面供大家参考:




例子代码
1

using System;
2

using System.Collections.Generic;
3

using System.ComponentModel;
4

using System.Data;
5

using System.Drawing;
6

using System.Text;
7

using System.Windows.Forms;
8

using System.Runtime.InteropServices;
9


10

namespace HookTest
11





{
12

public partial class Form1 : Form
13





{
14



消息定义(WinUser.h中定义)#region 消息定义(WinUser.h中定义)
15

private const int WM_PAINT = 0x000F;
16

private const int WM_CLOSE = 0x0010;
17

private const int WM_QUIT = 0x0012;
18

private const int WM_DESTROY = 0x0002;
19

#endregion
20


21

private MyHook callProcHook = new MyHook(HookType.WH_CALLWNDPROC);
22

private MyHook keyHook = new MyHook(HookType.WH_KEYBOARD);
23


24

public Form1()
25





{
26

InitializeComponent();
27

}
28


29

private void Form1_Load(object sender, EventArgs e)
30





{
31

keyHook.HookInvoked += new HookEventHandler(keyHook_HookInvoked);
32

keyHook.Install();
33


34

callProcHook.HookInvoked += new HookEventHandler(callProcHook_HookInvoked);
35

callProcHook.Install();
36

}
37


38

private void keyHook_HookInvoked(object sender, HookEventArgs e)
39





{
40

if (e.key == Keys.F4 && e.bAltKey) //Alt + F4
41





{
42

this.WindowState = FormWindowState.Minimized;
43

}
44

}
45


46

private void callProcHook_HookInvoked(object sender, HookEventArgs e)
47





{
48

unsafe
49





{
50

CWPSTRUCT* message = (CWPSTRUCT*)e.lParam;
51

if (message != null)
52





{
53

if (message->message == WM_CLOSE)
54





{
55

(sender as MyHook).CallNextProc = false;
56

MessageBox.Show("程序即将关闭!");
57

}
58

}
59

}
60

}
61


62

}
63


64



消息结构体(参照WinUser.h中定义)#region 消息结构体(参照WinUser.h中定义)
65

public struct CWPSTRUCT
66





{
67

public IntPtr lparam;
68

public IntPtr wparam;
69

public int message;
70

public IntPtr hwnd;
71

}
72

#endregion
73


74



钩子类型的枚举#region 钩子类型的枚举
75

public enum HookType : int
76





{
77

WH_JOURNALRECORD = 0,
78

WH_JOURNALPLAYBACK = 1,
79

WH_KEYBOARD = 2,
80

WH_GETMESSAGE = 3,
81

WH_CALLWNDPROC = 4,
82

WH_CBT = 5,
83

WH_SYSMSGFILTER = 6,
84

WH_MOUSE = 7,
85

WH_HARDWARE = 8,
86

WH_DEBUG = 9,
87

WH_SHELL = 10,
88

WH_FOREGROUNDIDLE = 11,
89

WH_CALLWNDPROCRET = 12,
90

WH_KEYBOARD_LL = 13,
91

WH_MOUSE_LL = 14
92

}
93

#endregion
94


95



虚键值的定义(参照WinUser.h中定义)#region 虚键值的定义(参照WinUser.h中定义)
96

public enum VirtualKeys
97





{
98

VK_SHIFT = 0x10,
99

VK_CONTROL = 0x11,
100

VK_MENU = 0x12, //ALT
101

VK_PAUSE = 0x13,
102

VK_CAPITAL = 0x14
103

}
104

#endregion
105


106



钩子委托#region 钩子委托
107

public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
108

public delegate void HookEventHandler(object sender, HookEventArgs e);
109

#endregion
110


111



钩子事件参数#region 钩子事件参数
112

public class HookEventArgs : EventArgs
113





{
114

public int HookCode;
115

public IntPtr wParam;
116

public IntPtr lParam;
117

public Keys key;
118

public bool bAltKey;
119

public bool bCtrlKey;
120

}
121

#endregion
122


123



钩子类#region 钩子类
124

public class MyHook
125





{
126



调用Windows API#region 调用Windows API
127

[DllImport("user32.dll")]
128

static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);
129


130

[DllImport("user32.dll")]
131

static extern bool UnhookWindowsHookEx(IntPtr hhk);
132


133

[DllImport("user32.dll")]
134

static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
135


136

[DllImport("user32.dll")]
137

static extern short GetKeyState(VirtualKeys nVirtKey);
138

#endregion
139


140



局部变量#region 局部变量
141

private IntPtr m_hook;
142

private HookType m_hooktype;
143

private HookProc m_hookproc;
144


145

private bool _bCallNext;
146


147

public bool CallNextProc
148





{
149



get

{ return _bCallNext; }
150



set

{ _bCallNext = value; }
151

}
152


153

#endregion
154


155

public event HookEventHandler HookInvoked;
156


157

public void Install()
158





{
159

m_hook = SetWindowsHookEx(m_hooktype, m_hookproc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
160

}
161


162

public void Uninstall()
163





{
164

if (m_hook != IntPtr.Zero)
165





{
166

UnhookWindowsHookEx(m_hook);
167

}
168

}
169


170

public MyHook(HookType HookType)
171





{
172

m_hooktype = HookType;
173

if (m_hooktype == HookType.WH_KEYBOARD)
174





{
175

m_hookproc = new HookProc(KeyHookProcedure);
176

}
177

else if (m_hooktype == HookType.WH_CALLWNDPROC)
178





{
179

m_hookproc = new HookProc(CallProcHookProcedure);
180

}
181

}
182


183

protected int KeyHookProcedure(int code, IntPtr wParam, IntPtr lParam)
184





{
185

if (code != 0)
186





{
187

return CallNextHookEx(m_hook, code, wParam, lParam);
188

}
189


190

if (HookInvoked != null)
191





{
192

Keys key = (Keys)wParam.ToInt32();
193

HookEventArgs eventArgs = new HookEventArgs();
194

eventArgs.key = key;
195

eventArgs.lParam = lParam;
196

eventArgs.wParam = wParam;
197

eventArgs.HookCode = code;
198

eventArgs.bAltKey = GetKeyState(VirtualKeys.VK_MENU) <= -127;
199

eventArgs.bCtrlKey = GetKeyState(VirtualKeys.VK_CONTROL) <= -127;
200

HookInvoked(this, eventArgs);
201

}
202


203

return CallNextHookEx(m_hook, code, wParam, lParam);
204

}
205


206

protected int CallProcHookProcedure(int code, IntPtr wParam, IntPtr lParam)
207





{
208

try
209





{
210

CallNextProc = true;
211

if (HookInvoked != null)
212





{
213

HookEventArgs eventArgs = new HookEventArgs();
214

eventArgs.lParam = lParam;
215

eventArgs.wParam = wParam;
216

eventArgs.HookCode = code;
217

HookInvoked(this, eventArgs);
218

}
219


220

if (CallNextProc)
221





{
222

return CallNextHookEx(m_hook, code, wParam, lParam);
223

}
224

else
225





{
226

//return 1;
227

return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
228

}
229

}
230

catch (Exception ex)
231





{
232

MessageBox.Show(ex.Message);
233

return 0;
234

}
235

}
236

}
237

#endregion
238

}

以上的钩子只对当前应用程序起作用,如果想控制其他的所有程序,需要使用全局钩子。原则上全局钩子在C#中是不支持的,在http://www.codeproject.com/csharp/globalhook.asp 中的代码可以参照来实现全局钩子
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: