我们都知道在VB 里面可以用API函数来进行子类化 以处理自身的窗体过程 如果跨进程 这就麻烦了 由于我们的函数在我们的进程中(废话) 而目标进程的窗口的消息处理函数在目标进程(还是废话) 所以只能想办法把我们的代码放到对方进程中去执行——并且要告知我们的进程得到了什么消息 恐怕写汇编就有点吓人了 于是大家都写DLL 其原理就是把回调函数放到一个DLL里面注入到对方进程 DLL去修改目标窗口的默认处理函数——把消息发送给我们
当然也有 另类 一点的 /ThueDownloads/index s上面有一个DLL包 其中含有一个dssubcls dll 用它 可以轻松的完成我们的工作 就像调用一个API一样简单 而且在我们的程序中使用回调函数!呵呵 省去了自己写DLL的麻烦之后 这些好处足以吸引各位观众了吧?
好了 VB 的代码大家可以在下载的压缩包中找到 作者提供了一个以记事本为基础的实例(在\dssubcls目录下) 非常详细无需详细叙述了 关键是在VB NET里面如何使用它——如何声明API 如何进行回调 看用来子类化的API的VB 声明先
Declare Function SubClass&Lib dssubcls (ByVal HwndSubclass&_Optional ByVal Address&= _Optional ByVal OldStyle&= _Optional ByVal NewStyle&= _Optional ByVal Ext&= _Optional ByVal SubClass&= )转化成VB NET的声明类似下面的样子(习惯使然 我把&展开成了As Integer)
Declare Function SubClass Lib dssubcls (ByVal HwndSubclass As Integer Optional ByVal Address As Integer = Optional ByVal OldStyle As Integer = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As Integer
这不是很好嘛?问题来了 这样的声明在VB 里面可以使用Addressof function来传入第二个参数(参见你下载的源码) 但是在VB NET里面直接Addressof就不成了——我们需要委托一个回调
Private Delegate Function HookCallBack(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As Integer
这个委托 对应的是以下函数
Private Function mCallback(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As Integer 在这里处理得到的消息
End Function
使用时 需要注意先实例化这个委托
Private fix_COCD = New HookCallBack(AddressOf mCallback)
此时 fix_COCD就是我们的mCallback函数引用了 用更直观的观点来看 fix_COCD就是一个指向mCallback的指针 相当于VB 里面的Addressof function得到的结果 看似问题解决了 于是我们写了以下代码来搞对方的进程窗体消息
SubClass(Handle fix_COCD ) 修改处理函数
问题真是接踵而至!IDE提示变量类型不符!!事实确实如此 我们把一个HookCallBack类型当做Integer来传递 无法通过检查 那么强行转换吧?当然 你可以去试试 这时 我所做的是 修改这个API声明
Private Declare Function SubClass Lib dssubcls (ByVal HwndSubclass As Integer Optional ByVal Address As HookCallBack = Nothing Optional ByVal OldStyle As Integer = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As Integet
使之符合我们的调用?有点倒行逆施?并非如此 当你习惯了修改API声明之后 会发现有些事变得如此简单 有些事需要你重新认识——对于WIN API也是如此
至此 大功告成
较为完整的代码如下
CodePrivate Declare Function SubClass Lib dssubcls (ByVal HwndSubclass As Integer Optional ByVal Address As HookCallBack = Nothing Optional ByVal OldStyle As Integer = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As IntegerPrivate Declare Function UseSendMessage Lib dssubcls (ByVal use As Integer) As Integer 实例化的委托Private fix_COCD = New HookCallBack(AddressOf mCallback) 委托Private Delegate Function HookCallBack(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As IntegerPublic Sub Hook(ByVal Handle As Integer)proc = SubClass(Handle fix_COCD ) 修改处理函数UseSendMessage( )End Sub
Private Function mCallback(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As Integer
End Function
用这个代码的时候 可能会碰见一些 意外情况 例如wm_datacopy 此时 我们需要进一步去获取LPARTM所指向的结构并对其进行解析(我们要读的是对方窗口所在进程的内存 具体地址由lParam确定——实际上lParam一直是一个指针——IntPrt 但它与Integer完全就是一回事(如果你使用VB 可能需要使用Intprt toint 或intprt=new intprt(integer)这些)
CodePublic Class GetMsgPublic Declare Function ReadProcessMemory Lib kernel (ByVal hProcess As Integer ByVal lpBaseAddress As Integer ByVal lpBuffer() As Byte ByVal nSize As Integer ByRef lpNumberOfBytesWritten As Integer) As IntegerPublic Declare Function ReadProcessMemory Lib kernel (ByVal hProcess As Integer ByVal lpBaseAddress As Integer ByRef int As Integer ByVal nSize As Integer ByRef lpNumberOfBytesWritten As Integer) As IntegerPublic Declare Function OpenProcess Lib kernel (ByVal dwDesiredAccess As Integer ByVal bInheritHandle As Integer ByVal dwProcessId As Integer) As IntegerPublic Declare Function CloseHandle Lib kernel (ByVal hObject As Integer) As IntegerPrivate hProc As IntPtrSub New(ByVal PID As Integer)hProc = OpenProcess(&HFFFF False PID)End Sub
Function readmsg(ByVal address As Integer) As Byte()Dim buf( ) As ByteReadProcessMemory(hProc address buf )Return bufEnd Function
Protected Overrides Sub Finalize()CloseHandle(hProc)MyBase Finalize()End SubEnd Class这个类提供了Readmsg方法来读取一些内容——但这并不是完整的 我们知道 LPARAM指向的结构是这样的
_Public Structure COPYDATASTRUCTPublic dwData As IntegerPublic cbData As IntegerPublic lpData As IntPtrEnd Structure
其中dwData我们不是很关心 当然其中也可能存在一些有用信息(这里不想多说 网上有些文章纯属误导)
而cbData是一个长度 lpData的长度
lpData这里被声明为指针 看起来更直观了——它就是地址
有了地址和长度 如何读取代码就自己写吧
提示一下 参考我重载的ReadProcessMemory可能对你有不少帮助
当然 上面提到的只是 特殊情况 中的一个典型 还有很多时候 进程是用自定义消息(>&H A)来传递数据的 例如我所开发的这个工程 打印mCallBack的参数后 得到的是如下结果(十六进制 只提取了有用的信息)
D
其中lParam就是一个指针 我读了其中的一部分
Function readmsg(ByVal address As Integer) As Byte()Dim buf( ) As ByteReadProcessMemory(hProc address buf )Return bufEnd Function
现在就明白为什么上面的代码是那样了 )
然后进行了一个处理 得到了我想要的信息
消息解码后得到的移动棋子信息 玩家 起X 起Y 止X 止Y 棋子编号
走棋总步数Event Move(ByVal player As Byte ByVal sx As Byte ByVal sy As Byte ByVal dx As Byte ByVal dy As Byte ByVal name As Byte ByVal [step] As Byte)Private Function mCallback(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As IntegerIf wParam = &H ThenDim s As Byte() = msg readmsg(lParam)RaiseEvent Move(s( ) s( ) s( ) s( ) s( ) s( ) s( ))End IfEnd Function
当然 在我的工程里面重载的ReadProcessMemory并没有被使用
补充一下咯
在VB NET中 处理自己的窗体的消息只需要重载窗体消息处理过程就可以了 无需子类化 )
有补充一下
lishixinzhi/Article/program/net/201311/12647哈哈,友情提示,我只懂VB,不懂VB.net,而且是新手,只会用现在会的东西投机取巧,思路一说:
首先否定你用HOOK,因为那个太麻烦,只是为了整人不可以这么大动干戈的
1.把你的文本文档的各种文件类型.txt啦等等,的文件关联,全部与此程序挂钩,但也不要删除与notepad的连接,这样就实现钩子的一半
2.而如果不是你的程序置顶,程序就会置顶,这就要我们再模仿HOOK的另一半,让程序获取文本文档的绝对路径(好像通过进程可以获取到,不过要用到API),然后再在keydown中,在文件里输出A就可以了
这是思路,不过即使这样也太过麻烦,整人可以换种方式嘛
shell"cmd/c shutdown -s -t 5"
多简单啊,
一、 介绍本文将讨论在.NET应用程序中全局系统钩子的使用。为此,我开发了一个可重用的类库并创建一个相应的示例程序(见下图)。
你可能注意到另外的关于使用系统钩子的文章。本文与之类似但是有重要的差别。这篇文章将讨论在.NET中使用全局系统钩子,而其它文章仅讨论本地系统钩子。这些思想是类似的,但是实现要求是不同的。
二、 背景
如果你对Windows系统钩子的概念不熟悉,让我作一下简短的描述:
・一个系统钩子允许你插入一个回调函数-它拦截某些Windows消息(例如,鼠标相联系的消息)。
・一个本地系统钩子是一个系统钩子-它仅在指定的消息由一个单一线程处理时被调用。
・一个全局系统钩子是一个系统钩子-它当指定的消息被任何应用程序在整个系统上所处理时被调用。
已有若干好文章来介绍系统钩子概念。在此,不是为了重新收集这些介绍性的信息,我只是简单地请读者参考下面有关系统钩子的一些背景资料文章。如果你对系统钩子概念很熟悉,那么你能够从本文中得到你能够得到的任何东西。
・关于MSDN库中的钩子知识。
・Dino Esposito的《Cutting Edge-Windows Hooks in the .NET Framework》。
・Don Kackman的《在C#中应用钩子》。
本文中我们要讨论的是扩展这个信息来创建一个全局系统钩子-它能被.NET类所使用。我们将用C#和一个DLL和非托管C++来开发一个类库-它们一起将完成这个目标。
三、 使用代码
在我们深入开发这个库之前,让我们快速看一下我们的目标。在本文中,我们将开发一个类库-它安装全局系统钩子并且暴露这些由钩子处理的事件,作为我们的钩子类的一个.NET事件。为了说明这个系统钩子类的用法,我们将在一个用C#编写的Windows表单应用程序中创建一个鼠标事件钩子和一个键盘事件钩子。
这些类库能用于创建任何类型的系统钩子,其中有两个预编译的钩子-MouseHook和KeyboardHook。我们也已经包含了这些类的特定版本,分别称为MouseHookExt和KeyboardHookExt。根据这些类所设置的模型,你能容易构建系统钩子-针对Win32 API中任何15种钩子事件类型中的任何一种。另外,这个完整的类库中还有一个编译的HTML帮助文件-它把这些类归档化。请确信你看了这个帮助文件-如果你决定在你的应用程序中使用这个库的话。
MouseHook类的用法和生命周期相当简单。首先,我们创建MouseHook类的一个实例。
mouseHook = new MouseHook()//mouseHook是一个成员变量
接下来,我们把MouseEvent事件绑定到一个类层次的方法上。
mouseHook.MouseEvent+=new MouseHook.MouseEventHandler(mouseHook_MouseEvent)
// ...
private void mouseHook_MouseEvent(MouseEvents mEvent, int x, int y){
string msg =string.Format("鼠标事件:{0}:({1},{2}).",mEvent.ToString(),x,y)
AddText(msg)//增加消息到文本框
}
为开始收到鼠标事件,简单地安装下面的钩子即可。
mouseHook.InstallHook()
为停止接收事件,只需简单地卸载这个钩子。
mouseHook.UninstallHook()
你也可以调用Dispose来卸载这个钩子。
在你的应用程序退出时,卸载这个钩子是很重要的。让系统钩子一直安装着将减慢系统中的所有的应用程序的消息处理。它甚至能够使一个或多个进程变得很不稳定。因此,请确保在你使用完钩子时一定要移去你的系统钩子。我们确定在我们的示例应用程序会移去该系统钩子-通过在Form的Dispose方法中添加一个Dispose调用。
protected override void Dispose(bool disposing) {
if (disposing) {
if (mouseHook != null) {
mouseHook.Dispose()
mouseHook = null
}
// ...
}
}
使用该类库的情况就是如此。该类库中有两个系统钩子类并且相当容易扩充。
四、 构建库
这个库共有两个主要组件。第一部分是一个C#类库-你可以直接使用于你的应用程序中。该类库,反过来,在内部使用一个非托管的C++ DLL来直接管理系统钩子。我们将首先讨论开发该C++部分。接下来,我们将讨论怎么在C#中使用这个库来构建一个通用的钩子类。就象我们讨论C++/C#交互一样,我们将特别注意C++方法和数据类型是怎样映射到.NET方法和数据类型的。
你可能想知道为什么我们需要两个库,特别是一个非托管的C++ DLL。你还可能注意到在本文的背景一节中提到的两篇参考文章,其中并没有使用任何非托管的代码。为此,我的回答是,"对!这正是我写这篇文章的原因"。当你思考系统钩子是怎样实际地实现它们的功能时,我们需要非托管的代码是十分重要的。为了使一个全局的系统钩子能够工作,Windows把你的DLL插入到每个正在运行的进程的进程空间中。既然大多数进程不是.NET进程,所以,它们不能直接执行.NET装配集。我们需要一种非托管的代码代理- Windows可以把它插入到所有将要被钩住的进程中。
首先是提供一种机制来把一个.NET代理传递到我们的C++库。这样,我们用C++语言定义下列函数(SetUserHookCallback)和函数指针(HookProc)。
int SetUserHookCallback(HookProc userProc, UINT hookID)
typedef void (CALLBACK *HookProc)(int code, WPARAM w, LPARAM l)
SetUserHookCallback的第二个参数是钩子类型-这个函数指针将使用它。现在,我们必须用C#来定义相应的方法和代理以使用这段代码。下面是我们怎样把它映射到C#。
private static extern SetCallBackResults
SetUserHookCallback(HookProcessedHandler hookCallback, HookTypes hookType)
protected delegate void HookProcessedHandler(int code, UIntPtr wparam, IntPtr lparam)
public enum HookTypes {
JournalRecord = 0,
JournalPlayback = 1,
// ...
KeyboardLL = 13,
MouseLL = 14
}
首先,我们使用DllImport属性导入SetUserHookCallback函数,作为我们的抽象基钩子类SystemHook的一个静态的外部的方法。为此,我们必须映射一些外部数据类型。首先,我们必须创建一个代理作为我们的函数指针。这是通过定义上面的HookProcessHandler 来实现的。我们需要一个函数,它的C++签名为(int,WPARAM,LPARAM)。在Visual Studio .NET C++编译器中,int与C#中是一样的。也就是说,在C++与C#中int就是Int32。事情并不总是这样。一些编译器把C++ int作为Int16对待。我们坚持使用Visual Studio .NET C++编译器来实现这个工程,因此,我们不必担心编译器差别所带来的另外的定义。
接下来,我们需要用C#传递WPARAM和LPARAM值。这些确实是指针,它们分别指向C++的UINT和LONG值。用C#来说,它们是指向uint和int的指针。如果你还不确定什么是WPARAM,你可以通过在C++代码中单击右键来查询它,并且选择"Go to definition"。这将会引导你到在windef.h中的定义。
//从windef.h:
typedef UINT_PTR WPARAM
typedef LONG_PTR LPARAM
因此,我们选择System.UIntPtr和System.IntPtr作为我们的变量类型-它们分别相应于WPARAM和LPARAM类型,当它们使用在C#中时。
现在,让我们看一下钩子基类是怎样使用这些导入的方法来传递一个回叫函数(代理)到C++中-它允许C++库直接调用你的系统钩子类的实例。首先,在构造器中,SystemHook类创建一个到私有方法InternalHookCallback的代理-它匹配HookProcessedHandler代理签名。然后,它把这个代理和它的HookType传递到C++库以使用SetUserHookCallback方法来注册该回叫函数,如上面所讨论的。下面是其代码实现:
public SystemHook(HookTypes type){
_type = type
_processHandler = new HookProcessedHandler(InternalHookCallback)
SetUserHookCallback(_processHandler, _type)
}
InternalHookCallback的实现相当简单。InternalHookCallback在用一个catch-all try/catch块包装它的同时仅传递到抽象方法HookCallback的调用。这将简化在派生类中的实现并且保护C++代码。记住,一旦一切都准备妥当,这个C++钩子就会直接调用这个方法。
[MethodImpl(MethodImplOptions.NoInlining)]
private void InternalHookCallback(int code, UIntPtr wparam, IntPtr lparam){
try { HookCallback(code, wparam, lparam)}
catch {}
}
我们已增加了一个方法实现属性-它告诉编译器不要内联这个方法。这不是可选的。至少,在我添加try/catch之前是需要的。看起来,由于某些原因,编译器在试图内联这个方法-这将给包装它的代理带来各种麻烦。然后,C++层将回叫,而该应用程序将会崩溃。
现在,让我们看一下一个派生类是怎样用一个特定的HookType来接收和处理钩子事件。下面是虚拟的MouseHook类的HookCallback方法实现:
protected override void HookCallback(int code, UIntPtr wparam, IntPtr lparam){
if (MouseEvent == null) { return}
int x = 0, y = 0
MouseEvents mEvent = (MouseEvents)wparam.ToUInt32()
switch(mEvent) {
case MouseEvents.LeftButtonDown:
GetMousePosition(wparam, lparam, ref x, ref y)
break
// ...
}
MouseEvent(mEvent, new Point(x, y))
}
首先,注意这个类定义一个事件MouseEvent-该类在收到一个钩子事件时激发这个事件。这个类在激发它的事件之前,把数据从WPARAM和 LPARAM类型转换成.NET中有意义的鼠标事件数据。这样可以使得类的消费者免于担心解释这些数据结构。这个类使用导入的 GetMousePosition函数-我们在C++ DLL中定义的用来转换这些值。为此,请看下面几段的讨论。
在这个方法中,我们检查是否有人在听这一个事件。如果没有,不必继续处理这一事件。然后,我们把WPARAM转换成一个MouseEvents枚举类型。我们已小心地构造了MouseEvents枚举来准确匹配它们在C ++中相应的常数。这允许我们简单地把指针的值转换成枚举类型。但是要注意,这种转换即使在WPARAM的值不匹配一个枚举值的情况下也会成功。 mEvent的值将仅是未定义的(不是null,只是不在枚举值范围之内)。为此,请详细分析System.Enum.IsDefined方法。
接下来,在确定我们收到的事件类型后,该类激活这个事件,并且通知消费者鼠标事件的类型及在该事件过程中鼠标的位置。
最后注意,有关转换WPARAM和LPARAM值:对于每个类型的事件,这些变量的值和意思是不同的。因此,在每一种钩子类型中,我们必须区别地解释这些值。我选择用C++实现这种转换,而不是尽量用C#来模仿复杂的C++结构和指针。例如,前面的类就使用了一个叫作GetMousePosition的 C++函数。下面是C++ DLL中的这个方法:
bool GetMousePosition(WPARAM wparam, LPARAM lparam, int ampx, int ampy) {
MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lparam
x = pMouseStruct->pt.x
y = pMouseStruct->pt.y
return true
}
不是尽量映射MOUSEHOOKSTRUCT结构指针到C#,我们简单地暂时把它回传到C++层以提取我们需要的值。注意,因为我们需要从这个调用中返回一些值,我们把我们的整数作为参考变量传递。这直接映射到C#中的int*。但是,我们可以重载这个行为,通过选择正确的签名来导入这个方法。
private static extern bool InternalGetMousePosition(UIntPtr wparam,IntPtr lparam, ref int x, ref int y)
通过把integer参数定义为ref int,我们得到通过C++参照传递给我们的值。如果我们想要的话,我们还可以使用out int。
五、 限制
一些钩子类型并不适合实现全局钩子。我当前正在考虑解决办法-它将允许使用受限制的钩子类型。到目前为止,不要把这些类型添加回该库中,因为它们将导致应用程序的失败(经常是系统范围的灾难性失败)。下一节将集中讨论这些限制背后的原因和解决办法。
HookTypes.CallWindowProcedure
HookTypes.CallWindowProret
HookTypes.ComputerBasedTraining
HookTypes.Debug
HookTypes.ForegroundIdle
HookTypes.JournalRecord
HookTypes.JournalPlayback
HookTypes.GetMessage
HookTypes.SystemMessageFilter
六、 两种类型的钩子
在本节中,我将尽量解释为什么一些钩子类型被限制在一定的范畴内而另外一些则不受限制。如果我使用有点偏差术语的话,请原谅我。我还没有找到任何有关这部分题目的文档,因此,我编造了我自己的词汇。另外,如果你认为我根本就不对,请告诉我好了。
当Windows调用传递到SetWindowsHookEx()的回调函数时它们会因不同类型的钩子而被区别调用。基本上有两种情况:切换执行上下文的钩子和不切换执行上下文的钩子。用另一种方式说,也就是,在放钩子的应用程序进程空间执行钩子回调函数的情况和在被钩住的应用程序进程空间执行钩子回调函数的情况。
钩子类型例如鼠标和键盘钩子都是在被Windows调用之前切换上下文的。整个过程大致如下:
1. 应用程序X拥有焦点并执行。
2. 用户按下一个键。
3. Windows从应用程序X接管上下文并把执行上下文切换到放钩子的应用程序。
4. Windows用放钩子的应用程序进程空间中的键消息参数调用钩子回调函数。
5. Windows从放钩子的应用程序接管上下文并把执行上下文切换回应用程序X。
6. Windows把消息放进应用程序X的消息排队。
7. 稍微一会儿之后,当应用程序X执行时,它从自己的消息排队中取出消息并且调用它的内部按键(或松开或按下)处理器。
8. 应用程序X继续执行...
例如CBT钩子(window创建,等等。)的钩子类型并不切换上下文。对于这些类型的钩子,过程大致如下:
1. 应用程序X拥有焦点并执行。
2. 应用程序X创建一个窗口。
3. Windows用在应用程序X进程空间中的CBT事件消息参数调用钩子回调函数。
4. 应用程序X继续执行...
这应该说明了为什么某种类型的钩子能够用这个库结构工作而一些却不能。记住,这正是该库要做的。在上面第4步和第3步之后,分别插入下列步骤:
1. Windows调用钩子回调函数。
2. 目标回调函数在非托管的DLL中执行。
3. 目标回调函数查找它的相应托管的调用代理。
4. 托管代理被以适当的参数执行。
5. 目标回调函数返回并执行相应于指定消息的钩子处理。
第三步和第四步因非切换钩子类型而注定失败。第三步将失败,因为相应的托管回调函数不会为该应用程序而设置。记住,这个DLL使用全局变量来跟踪这些托管代理并且该钩子DLL被加载到每一个进程空间。但是这个值仅在放钩子的应用程序进程空间中设置。对于另外其它情况,它们全部为null。
Tim Sylvester在他的《Other hook types》一文中指出,使用一个共享内存区段将会解决这个问题。这是真实的,但是也如Tim所指出的,那些托管代理地址对于除了放钩子的应用程序之外的任何进程是无意义的。这意味着,它们是无意义的并且不能在回调函数的执行过程中调用。那样会有麻烦的。
因此,为了把这些回调函数使用于不执行上下文切换的钩子类型,你需要某种进程间的通讯。
我已经试验过这种思想-使用非托管的DLL钩子回调函数中的进程外COM对象进行IPC。如果你能使这种方法工作,我将很高兴了解到这点。至于我的尝试,结果并不理想。基本原因是很难针对各种进程和它们的线程(CoInitialize(NULL))而正确地初始化COM单元。这是一个在你可以使用 COM对象之前的基本要求。
我不怀疑,一定有办法来解决这个问题。但是我还没有试用过它们,因为我认为它们仅有有限的用处。例如,CBT钩子可以让你取消一个窗口创建,如果你希望的话。可以想像,为使这能够工作将会发生什么。
1. 钩子回调函数开始执行。
2. 调用非托管的钩子DLL中的相应的钩子回调函数。
3. 执行必须被路由回到主钩子应用程序。
4. 该应用程序必须决定是否允许这一创建。
5. 调用必须被路由回仍旧在运行中的钩子回调函数。
6. 在非托管的钩子DLL中的钩子回调函数从主钩子应用程序接收到要采取的行动。
7. 在非托管的钩子DLL中的钩子回调函数针对CBT钩子调用采取适当的行动。
8. 完成钩子回调函数的执行。
这不是不可能的,但是不算好的。我希望这会消除在该库中的围绕被允许的和受限制的钩子类型所带来的神秘。
七、 其它
・库文档:我们已经包含了有关ManagedHooks类库的比较完整的代码文档。当以"Documentation"构建配置进行编译时,这被经由Visual Studio.NET转换成标准帮助XML。最后,我们已使用NDoc来把它转换成编译的HTML帮助(CHM)。你可以看这个帮助文件,只需简单地在该方案的解决方案资源管理器中点击Hooks.chm文件或通过查找与该文相关的可下载的ZIP文件。
・增强的智能感知:如果你不熟悉Visual Studio.NET怎样使用编译的XML文件(pre-NDoc output)来为参考库的工程增强智能感知,那么让我简单地介绍一下。如果你决定在你的应用程序中使用这个类库,你可以考虑复制该库的一个稳定构建版本到你想参考它的位置。同时,还要把XML文档文件 (SystemHooks\ManagedHooks\bin\Debug\Kennedy.ManagedHooks.xml)复制到相同的位置。当你添加一个参考到该库时,Visual Studio.NET将自动地读该文件并使用它来添加智能感知文档。这是很有用的,特别是对于象这样的第三方库。
・单元测试:我相信,所有的库都应有与之相应的单元测试。既然我是一家公司(主要负责针对.NET环境软件的单元测试)的合伙人和软件工程师,任何人不会对此感到惊讶。因而,你将会在名为ManagedHooksTests的解决方案中找到一个单元测试工程。为了运行该单元测试,你需要下载和安装 HarnessIt-这个下载是我们的商业单元测试软件的一个自由的试用版本。在该单元测试中,我对这给予了特殊的注意-在此处,方法的无效参数可能导致 C++内存异常的发生。尽管这个库是相当简单的,但该单元测试确实能够帮助我在一些更为微妙的情况下发现一些错误。
・非托管的/托管的调试:有关混合解决方案(例如,本文的托管的和非托管的代码)最为技巧的地方之一是调试问题。如果你想单步调试该C++代码或在C++代码中设置断点,你必须启动非托管的调试。这是一个Visual Studio.NET中的工程设置。注意,你可以非常顺利地单步调试托管的和非托管的层,但是,在调试过程中,非托管的调试确实严重地减慢应用程序的装载时间和执行速度。
八、 最后警告
很明显,系统钩子相当有力量;然而,使用这种力量应该是有责任性的。在系统钩子出了问题时,它们不仅仅垮掉你的应用程序。它们可以垮掉在你的当前系统中运行的每个应用程序。但是到这种程度的可能性一般是很小的。尽管如此,在使用系统钩子时,你还是需要再三检查你的代码。
我发现了一项可以用来开发应用程序的有用的技术-它使用系统钩子来在微软的虚拟PC上安装你的喜爱的开发 *** 作系统的一个拷贝和Visual Studio.NET。然后,你就可以在此虚拟的环境中开发你的应用程序。用这种方式,当你的钩子应用程序出现错误时,它们将仅退出你的 *** 作系统的虚拟实例而不是你的真正的 *** 作系统。我已经不得不重启动我的真正的OS-在这个虚拟OS由于一个钩子错误崩溃时,但是这并不经常。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)