钩子函数的用法和作用?
http://topic.csdn.net/t/20020625/10/827891.html
WINDOW的消息处理机制为了能在应用程序中监控系统的各种事件消息,提供了挂接各种反调函数(HOOK)的功能。这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一个挂接函数链。系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。WINDOW系统的这种反调函数挂接方法虽然会略加影响到系统的运行效率,但在很多场合下是非常有用的,通过合理有效地利用键盘事件的挂钩函数监控机制可以达到预想不到的良好效果。
一、在WINDOWS键盘事件上挂接监控函数的方法
WINDOW下可进行挂接的过滤函数包括11种:
WH_CALLWNDPROC 窗口函数的过滤函数
WH_CBT 计算机培训过滤函数
WH_DEBUG 调试过滤函数
WH_GETMESSAGE 获取消息过滤函数
WH_HARDWARE 硬件消息过滤函数
WH_JOURNALPLAYBACK 消息重放过滤函数
WH_JOURNALRECORD 消息记录过滤函数
WH_MOUSE 鼠标过滤函数
WH_MSGFILTER 消息过滤函数
WH_SYSMSGFILTER 系统消息过滤函数
WH_KEYBOARD 键盘过滤函数
其中键盘过滤函数是最常用最有用的过滤函数类型,不管是哪一种类型的过滤函数,其挂接的基本方法都是相同的。WINDOW调用挂接的反调函数时总是先调用挂接链首的那个函数,因此必须将键盘挂钩函数利用函数SetWindowsHookEx()将其挂接在函数链首。至于消息是否传递给函数链的下一个函数是由每个具体函数功能确定的,如果消息需要传统给下一个函数,可调用API函数的CallNextHookEx()来实现,如果不传递直接返回即可。
挂接函数可以是用来监控所有线程消息的全局性函数,也可以是单独监控某一线程的局部性函数。如果挂接函数是局部函数,可以将它放到一个.DLL动态链接库中,也可以放在一个局部模块中;如果挂接函数是全局的,那么必须将其放在一个.DLL动态链接库中。挂接函数必须严格按照下述格式进行声明,以键盘挂钩函数为例:
int FAR PASCAL KeyboardProc(
int nCode,WORD wParam,DWORD lParam)
其中KeyboardProc为定义挂接函数名,该函数必须在模块定义文件中利用EXPORTS命令进行说明;nCode决定挂接函数是否对当前消息进行处理;wParam和lParam为具体的消息内容。
二、键盘事件挂接函数的安装与下载
在程序中可以利用函数SetWindowsHookEx()来挂接过滤函数,在挂接函数时必须指出该挂接函数的类型、函数的入口地址以及函数是全局性的还是局部性的,挂接函数的具体调用格式如下:
SetWindowsHookEx(iType,iProc,hInst,iCode)
其中iType为挂接函数类型,键盘类型为WH_KEYBOARD,iProc为挂接函数地址,hInst为挂接函数链接库实例句柄,iCode为监控代码-0表示全局性函数。如果挂接函数需要将消息传递给下一个过滤函数,则在该挂接函数返回前还需要调用一次CallNextHookEx()函数,当需要下载挂接函数时,只要调用一次UnhookWindowsHookEx(iProc)函数即可实现。如果函数是全局性的,那么它必须放在一个.DLL动态链接库中,这时该函数调用方法可以和其它普通.DLL函数一样有三种:
1.在DEF定义文件中直接用函数名或序号说明:
EXPORTS
WEP @1 RESIDENTNAME
InitHooksDll @2
InstallFilter @3
KeyboardProc @4
用序号说明格式为:链接库名.函数名(如本例中说明方法为KEYDLL.KeyboardProc)。
2.在应用程序中利用函数直接调用:
首先在应用程序中利用LoadLibrary(LPSTR "链接库名 ")将动态链接库装入,并取得装载库模块句柄hInst,然后直接利用GetProcAddress(HINSTANCE hInst,LPSTR "函数过程名 ")获取函数地址,然后直接调用该地址即可,程序结束前利用函数FreeLibrary( )释放装入的动态链接库即可。
3.利用输入库.LIB方法
利用IMPLIB.EXE程序在建立动态链接库的同时建立相应的输入库.LIB,然后直接在项目文件中增加该输入库。
三、WINDOWS挂钩监控函数的实现步骤
WINDOWS挂钩函数只有放在动态链接库DLL中才能实现所有事件的监控功能。在.DLL中形成挂钩监控函数基本方法及其基本结构如下:
1、首先声明DLL中的变量和过程;
2、然后编制DLL主模块LibMain(),建立模块实例;
3、建立系统退出DLL机制WEP()函数;
4、完成DLL初始化函数InitHooksDll(),传递主窗口程序句柄;
5、编制挂钩安装和下载函数InstallFilter();
6、编制挂钩函数KeyboardProc(),在其中设置监控功能,并确定继续调下一个钩子函数还是直接返回WINDOWS应用程序。
7、在WINDOWS主程序中需要初始化DLL并安装相应挂钩函数,由挂接的钩子函数负责与主程序通信;
8、在不需要监控时由下载功能卸掉挂接函数。
四、WINDOWS下键盘挂钩监控函数的应用技术
目前标准的104 键盘上都有两个特殊的按键,其上分别用WINDOW程序徽标和鼠标下拉列表标识,本文暂且分别称为Micro左键和Micro右键,前者用来模拟鼠标左键激活开始菜单,后者用来模拟鼠标右键激活属性菜单。这两个特殊按键只有在按下后立即抬起即完成 CLICK过程才能实现其功能,并且没有和其它按键进行组合使用。
由于WINDOWS 系统中将按键划分得更加详细,使应用程序中很难灵活定义自己的专用快捷键,比如在开发.IME等应用程序时很难找到不与WORD8.0等其它应用程序冲突的功能按键。如果将标准104键盘中的这两个特殊按键作为模拟CTRL和ALT 等专用按键,使其和其它按键组合,就可以在自己的应用程序中自由地设置专用功能键,为应用程序实现各种功能快捷键提供灵活性。正常情况下WINDOWS 键盘事件驱动程序
并不将这两个按键的消息进行正常解释,这就必须利用键盘事件的挂钩监控函数来实现其特定的功能。其方法如下:
1、首先编制如下一个简单动态链接库程序,并编译成DLL文件。
#include "windows.h "
int FAR PASCAL LibMain(HANDLE hModule,UINT wDataSeg,
UINT cbHeapSize,LPSTR lpszCmdLine);
int WINAPI WEP(int bSystemExit);
int WINAPI InitHooksDll(HWND hwndMainWindow);
int WINAPI InstallFilter(BOOL nCode);
LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam);
static HANDLE hInstance; // 全局句柄
static HWND hWndMain; // 主窗口句柄
static int InitCalled=0; // 初始化标志
static HHOOK hKeyHook;
FARPROC lpfnKeyHook=(FARPROC)KeyHook;
BOOL HookStates=FALSE;
int FAR PASCAL LibMain(
HANDLE hModule,
UINT wDataSeg,
UINT cbHeapSize,
LPSTR lpszCmdLine)
{
if (cbHeapSize!=0) UnlockData(0);
hInstance = hModule;
return 1;
}
int WINAPI WEP (int bSystemExit)
{ return 1;}
int WINAPI InitHooksDll(HWND hwndMainWindow)
{ hWndMain = hwndMainWindow;
InitCalled = 1;
return (0);
}
int WINAPI InstallFilter(BOOL nCode)
{ if (InitCalled==0) return (-1);
if (nCode==TRUE) {
hKeyHook=SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)lpfnKeyHook,hInstance,0);
HookStates=TRUE;
} else {
UnhookWindowsHookEx(hKeyHook);
HookStates=FALSE;
}
return(0);
}
LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam)
{
static BOOL msflag=FALSE;
if(nCode> =0) {
if(HookStates==TRUE){
if((wParam==0xff)|| //WIN3.X下按键值
(wParam==0x5b)||(wParam==0x5c)){//WIN95下按键值
if((i==0x15b)||(i==0x15c)){ //按键按下处理
msflag=TRUE;
PostMessage(hWndMain,0x7fff,0x1,0x3L);
} else if((i==0xc15b)||(i==0xc15c)){//按键抬起处理
msflag=FALSE;
PostMessage(hWndMain,0x7fff,0x2,0x3L);
}
}
}
}
return((int)CallNextHookEx(hKeyHook,nCode,wParam,lParam));
}
该程序的主要功能是监控键盘按键消息,将两个特殊按键Micro按下和抬起消息转换成自定义类型的消息,并将自定义消息发送给应用程序主窗口函数。
2、在应用程序主函数中建立窗口后,调用InitHooksDll()函数来初始化动态链接库,并将应用程序主窗口句柄传递给链接库,然后调用InstallFilter()函数挂接键盘事件监控回调函数。
InitHooksDll(hIMEWnd); //初始化DLL
InstallFilter(TRUE); //安装键盘回调函数
3、在应用程序主窗口函数处理自定义消息时,保存Micro按键的状态,供组合按键处理时判断使用。
switch (iMessage) {
case 0x7fff: //自定义消息类型
if(lParam==0x3L){//设置Micro键的状态
if(wParam==0x1) MicroFlag=TRUE;
else if(wParam==0x2) MicroFlag=FALSE;
}
break;
4、在进行按键组合处理时,首先判断Micro键是否按下,然后再进行其它按键的判断处理。
case WM_KEYDOWN: // 按键按下处理
if(MicroFlag==TRUE){//Micro键按下
if((BYTE)HIBYTE(wParam)==0x5b){
//Micro+ "[ "组合键
......//按键功能处理
} else if((BYTE)HIBYTE(wParam)==0x5d){
//Micro+ "] "组合键
......//按键功能处理
}
}
break;
5、当应用程序退出时应注意下载键盘监控函数,即调用InstallFilter(FALSE)函数一次。
6、利用本文提供的方法设置自己的应用程序功能按键,在保证程序功能按键不会与其它系统发生冲突的同时,有效地利用了系统中现有资源,而且在实现应用程序功能的同时灵活应用了系统中提供的各种功能调用。
http://hi.baidu.com/anwyo/blog/item/882c858276107bb96d811925.html
HOOK的认识[转载]
2008年09月26日星期五下午 3:40
2 实例句柄
http://zhidao.baidu.com/question/83275743.html
3 DLL入门浅析(1)——如何建立DLL
http://www.cppblog.com/suiaiguo/archive/2009/07/20/90619.html
初学DLL,结合教程,总结一下自己的所得,希望对DLL初学者们有所帮助。
动态链接库(DLL)是从C语言函数库和Pascal库单元的概念发展而来的。所有的C语言标准库函数都存放在某一函数库中。在链接应用程序的过程中,链接器从库文件中拷贝程序调用的函数代码,并把这些函数代码添加到可执行文件中。这种方法同只把函数储存在已编译的OBJ文件中相比更有利于代码的重用。但随着Windows这样的多任务环境的出现,函数库的方法显得过于累赘。如果为了完成屏幕输出、消息处理、内存管理、对话框等操作,每个程序都不得不拥有自己的函数,那么Windows程序将变得非常庞大。Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝。动态链接库就是在这种情况下出现的。动态链接库不用重复编译或链接,一旦装入内存,DLL函数可以被系统中的任何正在运行的应用程序软件所使用,而不必再将DLL函数的另一拷贝装入内存。
下面我们一步一步来建立一个DLL。
一、建立一个DLL工程
新建一个工程,选择Win32控制台项目(Win32 Console Application),并且在应用程序设置标签(the advanced tab)上,选择DLL和空项目选项。
二、声明导出函数
这里有两种方法声明导出函数:一种是通过使用__declspec(dllexport),添加到需要导出的函数前,进行声明;另外一种就是通过模块定义文件(Module-Definition File即.DEF)来进行声明。
第一种方法,建立头文件DLLSample.h,在头文件中,对需要导出的函数进行声明。
#ifndef _DLL_SAMPLE_H
#define _DLL_SAMPLE_H
// 如果定义了C++编译器,那么声明为C链接方式
#ifdef __cplusplus
extern "C" {
#endif
// 通过宏来控制是导入还是导出
#ifdef _DLL_SAMPLE
#define DLL_SAMPLE_API __declspec(dllexport)
#else
#define DLL_SAMPLE_API __declspec(dllimport)
#endif
// 导出/导入函数声明
DLL_SAMPLE_API void TestDLL(int);
#undef DLL_SAMPLE_API
#ifdef __cplusplus
}
#endif
#endif
这个头文件会分别被DLL和调用DLL的应用程序引入,当被DLL引入时,在DLL中定义_DLL_SAMPLE宏,这样就会在DLL模块中声明函数为导出函数;当被调用DLL的应用程序引入时,就没有定义_DLL_SAMPLE,这样就会声明头文件中的函数为从DLL中的导入函数。
第二种方法:模块定义文件是一个有着.def文件扩展名的文本文件。它被用于导出一个DLL的函数,和__declspec(dllexport)很相似,但是.def文件并不是Microsoft定义的。一个.def文件中只有两个必需的部分:LIBRARY和 EXPORTS。
LIBRARY DLLSample
DESCRIPTION "my simple DLL"
EXPORTS
TestDLL @1 ;@1表示这是第一个导出函数
第一行,''LIBRARY''是一个必需的部分。它告诉链接器(linker)如何命名你的DLL。下面被标识为''DESCRIPTION''的部分并不是必需的。该语句将字符串写入 .rdata 节,它告诉人们谁可能使用这个DLL,这个DLL做什么或它为了什么(存在)。再下面的部分标识为''EXPORTS''是另一个必需的部分;这个部分使得该函数可以被其它应用程序访问到并且它创建一个导入库。当你生成这个项目时,不仅是一个.dll文件被创建,而且一个文件扩展名为.lib的导出库也被创建了。除了前面的部分以外,这里还有其它四个部分标识为:NAME, STACKSIZE, SECTIONS, 和VERSION。另外,一个分号(;)开始一个注解,如同''//''在C++中一样。定义了这个文件之后,头文件中的__declspec(dllexport)就不需要声明了。
三、编写DllMain函数和导出函数
DllMain函数是DLL模块的默认入口点。当Windows加载DLL模块时调用这一函数。系统首先调用全局对象的构造函数,然后调用全局函数DLLMain。DLLMain函数不仅在将DLL链接加载到进程时被调用,在DLL模块与进程分离时(以及其它时候)也被调用。
#include "stdafx.h"
#define _DLL_SAMPLE
#ifndef _DLL_SAMPLE_H
#include "DLLSample.h"
#endif
#include "stdio.h"
//APIENTRY声明DLL函数入口点
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
void TestDLL(int arg)
{
printf("DLL output arg %d\n", arg);
}
如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。
然后,F7编译,就得到一个DLL了。
4 基本概念
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
1、钩子链表和钩子子程:
每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。
LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);
HookProc是回调函数名。
nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。
wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。
5 HHOOK 类型
类型: HHOOK句柄函数参数的类型
如:
BOOL WINAPIUnhookWindowsHookEx( __in HHOOK hhk);
Appliesto: desktopapps only:适用于:桌面应用程序
Removesa hook procedure installed in a hook chain by the SetWindowsHookEx function.
删除一个钩子过程安装在一个钩子链的SetWindowsHookEx函数。
HHOOK WINAPI SetWindowsHookEx(
__inint idHook,
__inHOOKPROC lpfn,
__inHINSTANCE hMod,
__inDWORD dwThreadId);
BOOL WINAPI UnhookWindowsHookEx(
__in HHOOK hhk
);
参数:
hhk [in]
类型: HHOOK
要删除的钩子的句柄。这个参数是上一个函数SetWindowsHookEx的返回值.
类型: BOOL
如果函数成功,返回值为非零值。
如果函数失败,返回值为零。要获得更多的错误信息,调用GetLastError函数.
6 KeyboardProc
键盘钩子处理函数:
首先键盘钩子处理函数的函数名是可以自定义的,例如:MyKeyboardProc()
函数原型:
LRESULTCALLBACK KeyboardProc( int code,
WPARAMwParam,
LPARAMlParam
);
code:
根据这个数值决定怎样处理消息
如果code 小于0,则必须让KeyboardProc()函数返回CallNextHookEx()
code可以是下列值:
HC_ACTION:wParam和lParam包含按键消息
HC_NOREMOVE:wParam和lParam包含按键消息,并且按键消息不能从消息队列中移除(一个被PeekMessage函数调用的请求,指定 PM_NOREMOVE标志)
wParam:
按键的虚拟键值消息,例如:VK_F1
lParam:
32位内存,内容描述包括:指定扩展键值,扫描码,上下文,重复次数。
7 CallNextHookEx()
Appliesto: desktopapps only
Passesthe hook information to the next hook procedure in the current hook chain. Ahook procedure can call this function either before or after processing thehook information.
适用于桌面应用程序:
钩子信息传递给下一个钩子程序,在当前的钩链。一个钩子程序可以调用这个函数之前或钩子信息处理后。
Winapi:调用下一个钩子
CallNextHookEx(
hhk:HHOOK; {当前钩子的句柄}
nCode:Integer; {钩子代码; 就是给下一个钩子要交待的}
wParam:WPARAM; {要传递的参数; 由钩子类型决定是什么参数}
lParam:LPARAM {要传递的参数; 由钩子类型决定是什么参数}
):LRESULT; {会返回下一个钩子执行后的返回值; 0 表示失败}
//参数nCode 的可选值:
HC_ACTION= 0; {}
HC_GETNEXT= 1; {}
HC_SKIP= 2; {}
HC_NOREMOVE= 3; {}
HC_NOREM= HC_NOREMOVE; {}
HC_SYSMODALON= 4; {}
HC_SYSMODALOFF = 5; {}
8 SetWindowsHookEx()
Unicode and ANSI names | SetWindowsHookExW (Unicode) and SetWindowsHookExA (ANSI) |
#ifdef UNICODE
#defineSetWindowsHookEx SetWindowsHookExW
#else
#defineSetWindowsHookEx SetWindowsHookExA
#endif// !UNICODE
http://edu.qudong.com/program/C/shilijiexi/20080512/11946.html
http://blog.csdn.net/djyangmaowei/article/details/4536667
HHOOK WINAPI SetWindowsHookEx(
__inint idHook,
__inHOOKPROC lpfn,
__inHINSTANCE hMod,
__inDWORD dwThreadId);
HHOOKSetWindowsHookEx( //装载一个钩子
int idHook, //描述被装载的钩子的类型,参数见下面
HOOKPROC lpfn, //钩子回调函数的地址,如果 dwThreadId 为 0 那么这个回调函数会在不同的进程中创建,它必须写在在DLL中
HINSTANCE hMod, //DLL的句柄
DWORD dwThreadId //描述要钩住的线程ID,如果这个参数为0,则会钩住桌面上的所有线程
);
idhook:装入钩子的类型.
lpfn: 钩子进程的入口地址
hMod: 应用程序的事件句柄
dwThreadId: 装入钩子的线程
参数:
idHook:
这个参数可以是以下值:
WH_CALLWNDPROC、WH_CALLWNDPROCRET、WH_CBT、WH_DEBUG、WH_FOREGROUNDIDLE、WH_GETMESSAGE、WH_JOURNALPLAYBACK、WH_JOURNALRECORD、WH_KEYBOARD、WH_KEYBOARD_LL、WH_MOUSE、WH_MOUSE_LL、WH_MSGFILTER、WH_SHELL、WH_SYSMSGFILTER。
对于这些参数,我不想一一加以解释,因为MSDN中有关于他们的详细注解。我只挑选其中的几个加以中文说明。
9 clearAllHook ()清除钩子
10 HINSTANCE,HWND和ID值的用法及意义和区别
HINSTANCE;应用程序实例句柄,它是一个唯一用来标识程序进程的32位的变量,
HWND:是窗口句柄,有很多种句柄,还有HBRUSH,HFONT,HMENU等等
ID:是一些控件或是选项的标识符,在程序中用到对他们的操作时可以用这个ID来访问这个控件或是其他什么
HINSTANCE和HMODULE这两种类型的句柄,用法不同么?
hmodule是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址,比如exe, dll等模块等。
hinstance在win32下与hmodule是相同的东西,在win32下还存在主要是因为win16程序使用hinstance来区别task
HINSTANCE是应用程序的实例句柄
获取方法 HINSTANCEAfxGetInstanceHandle( );
或者AfxGetApp( );
得到一个CWINAPP类的指针
11 使用DEF 文件从 DLL 导出
模块定义(.def) 文件是包含一个或多个描述DLL 各种属性的Module 语句的文本文件。如果不使用 __declspec(dllexport) 关键字导出DLL 的函数,则DLL 需要.def 文件。
.def 文件必须至少包含下列模块定义语句:
文件中的第一个语句必须是LIBRARY语句。此语句将.def 文件标识为属于DLL。LIBRARY语句的后面是DLL 的名称。链接器将此名称放到DLL 的导入库中。
EXPORTS语句列出名称,可能的话还会列出DLL 导出函数的序号值。通过在函数名的后面加上@ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从1 到N,其中N 是DLL 导出函数的个数。如果希望按序号导出函数,请参见按序号而不是按名称从 DLL 导出函数以及本主题。
例如,包含实现二进制搜索树的代码的DLL 看上去可能像下面这样:
12 DLL 中 .DEF文件的使用
DLL中导出函数的声明有两种方式:一种为在函数声明中加上__declspec(dllexport),这里不再举例说明;另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
首先创建一个DLL程序,.cpp中
int__stdcall Add(int numa, int numb)
{
return(numa + numb);
}
int__stdcall Sub(int numa, int numb)
{
return(numa - numb);
}
然后创建一个.def的文件,在里面加上
;DllTestDef.lib: 导出DLL函数
;作者:----
LIBRARYDllTestDef
EXPORTS
Add@ 1
Sub@ 2
最后创建一个测试程序:.cpp文件如下:
#include<iostream>
#include<windows.h>
usingnamespace std;
typedefint (__stdcall *FUN)(int, int);
HINSTANCEhInstance;
FUN fun;
intmain()
{
hInstance= LoadLibrary("DLLTestDef.dll");
if(!hInstance)
cout<<"Not Find this Dll" << endl;
fun= (FUN)GetProcAddress(hInstance, MAKEINTRESOURCE(1));
if(!fun)
{
cout<<"not find this fun" << endl;
}
cout<<fun(1, 2) << endl;
FreeLibrary(hInstance);
return0;
}
说明:
.def文件的规则为:
(1)LIBRARY语句说明.def文件相应的DLL;
(2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
(3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
13 关于什么是句柄(经典)
句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言中的文件句柄。
从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个WINDOWS函数来获得一个句柄,之后其他的WINDOWS函数就可以使用该句柄,以引用相应的对象。
如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?
为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。
句柄地址(稳定)→记载着对象在内存中的地址────→对象在内存中的地址(不稳定)→实际对象
本质:WINDOWS程序中并不是用物理地址来标识一个内存块,文件,任务或动态装入模块的,相反的,WINDOWSAPI给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。
但是必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的一个座位是一样的道理。
受M$的帮助文档以及很多Windows编程书籍的影响,大家对局柄比较普遍的认识是:句柄是一个整数,用以标识Windows对象,句柄不是一个指针……
而实际上,这些不过是M$进行数据封装的幌子而已,下面我们一起来分析一下HANDLE到底是什么。
请先到Windef.h找绝大多数句柄的定义:
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HHOOK);
……
DECLARE_HANDLE(HGDIOBJ);
DECLARE_HANDLE(HBITMAP);
DECLARE_HANDLE(HBRUSH);
……
typedef HANDLE HGLOBAL;
typedefHANDLE HLOCAL;
……
OK, 现在大家跟我一起翻到Winnt.h,看看DECLARE_HANDLE和HANDLE到底是什么:
#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) structname##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedefHANDLE name
#endif
typedef HANDLE *PHANDLE;
哈哈,现在知道了吧,HANDLE就是PVOID,也就是无类型指针,
而DECLARE_HANDLE(HWND);就是:
struct HWND__ {
int unused;};
typedef struct HWND__ *HWND;
现在实际上都清楚啦,这些Handles都不过是指向struct的指针,至于这个struct的用处,连M$都说unused了,^o^
现在解释下M$这么做的意义,这就是所谓数据封装,你可以在你的程序中把M$的内部结构指针传来传去,可是你却不知道它到底指向的内容是什么,而且可以编个句柄的瞎话防止大家的质疑:)。而M$的程序大可以这么写:
#include <windows.h> //这个和大家用的一样
#include "windows_in.h" //这个是M$自用的,外人别想看到^o^
HSOMETHINGELSE DoSomething(HSOMETHINGhSomething) {
struct RealSomething* p =(struct RealSomething*)hSomething; //先强制类型转换成内部结构指针
……do something……
return(HSOMETHINGELSE)pRealSomethingElse;//强制类型逆转换
14 WH_GETMESSAGE和WH_CALLWNDPROC这两个钩子的区别
首先
1.WH_GETMESSAGE专门勾的是通过postmessage发送到目标程序的消息。
2.WH_CALLWNDPROC则是勾的sendmessage发送到目标程序的消息。
3.postmessage 是将消息寄送到消息队列而不管其是否返回,sendmessage则是将消息发送到目标程序并等待返回。
4.最重要一点也是我犯错的地方,WH_GETMESSAGE中的lparam指向的是一个MSG结构体。
而WH_CALLWNDPROC中的lparam指向的则是CWPSTRUCT结构体!如果再写回调函数的时候结构体弄错了那可就什么消息都截获不到了因为你要截获的消息根本就没有存在在这个结构体中。
15 HC_ACTION 0
ThelParam parameter is apointer to an EVENTMSG structure containing information about a messageremoved from the system queue. The hook procedure must record the contents ofthe structure by copying them to a buffer or file.
lParam参数是一个指针一个EVENTMSG结构包含从系统队列中删除消息的信息。钩子过程必须记录复制到一个缓冲区或文件的内容结构。
16 回调函数
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。
17 VC++加载动态库和静态库
一. 静态库包括.lib和.h文件,在工程中使用静态库分为3步:
<1>在工程中加入静态库,有两种方法:
**方法一:项目设置中引用.lib,project-> setting->link-> object/library modules中添加.lib;(需要在tools/options/Directories设置正确的引用路径)
**方法二:在项目中直接加入lib,project-> add to project->files,选择正确的.lib。
<2>在工程中包括.h文件;(可能需要在tools/options设置正确的引用路径)
<3>在工程中使用静态库中的函数;
二. 动态链接库一般包括.lib(导出函数),.h,.dll文件,使用动态库有两种情况:
a.隐式链接,同使用静态库相似,分为三步:引用.lib(#pragmacomment(lib, "##/##/mine.lib")或者项目设置中引用.lib,project-> setting->link-> object/library modules中添加.lib),包含头文件,使用导出函数;
b.动态加载,直接使用LoadLibrary 加载所需的动态库,然后指定所需的导出函数,效率最高!
三.关于lib文件:
开发DLL的时候,就会生成两个文件:LIB静态库文件(接口文件)、DLL动态库文件(程序文件).
LIB是提供DLL接口的文件,供编译器使用.
#pragmacomment(lib, "##/##/mine.lib")
是使本工程加入mine.dll动态链表库的接口库文件,让本工程可以使用mine.dll中的函数
Windows用来定位 DLL 的搜索路径
通过隐式和显式链接,Windows 首先搜索“已知 DLL”,如 Kernel32.dll 和 User32.dll。Windows 然后按下列顺序搜索 DLL:
1. 当前进程的可执行模块所在的目录(包含 EXE 文件的目录)。
2. 当前目录(进程的当前工作目录)。
3. Windows 系统目录。GetSystemDirectory 函数检索此目录的路径。
4. Windows 目录。GetWindowsDirectory 函数检索此目录的路径。
5. PATH 环境变量中列出的目录。
18 Win32 多线程程序设计(线程完全手册)
Threads(线程)是比processes进程更小的执行单元,CPU的调度与时间分配皆以threads为对象。
19 消息钩子
钩子是WINDOWS中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之前处理它们。钩子的种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,鼠标钩子可以截获鼠标消息,外壳钩子可以截获启动和关闭应用程序的消息,日志钩子可以监视和记录输入事件。
若在dll中使用SetWindowsHookEx设置一全局钩子,系统会将其加载入使用user32的进程中,因而它也可被利用为无进程木马的进程注入手段。
20 wParam和lParam用法总结:
使用LPARAM传递地址,而WPARAM传递其他参数。
lp一般指指针
wparam 的定义:
typedef unsigned int UINT;
typedef UINT WPARAM;
lparam 的定义
typedef long LONG;
typedef LONG LPARAM;
1. WM_PAINT消息,LOWORD(lParam)是客户区的宽,HIWORD(lParam)是客户区的高
2. 滚动条WM_VSCROLL或WM_HSCROLL消息,LOWORD(wParam)指出了鼠标对滚动条的操作。比如上、下、左、右、翻页、移动等。
3. 击键消息,有WM_SYSKEYDOWN、WM_SYSKEYUP、WM_KEYUP、WM_KEYDOWN,其中wParam是虚拟键代码,lParam是包含属于击键的其他信息。lParam消息参数分为6个域,有重复计数、环境代码、键的先前状态等。
4. 字符消息WM_CHAR、WM_DEADCHAR、WM_SYSCHAR、WM_SYSDEADCHAR,lParam消息参数跟击键消息的lParam 消息参数内容相同,wParam参数是ANSI或Unicode字符代码
5. 客户区鼠标消息WM_LBUTTONDOWN、WM_LBUTTONUP、WM_RBUTTONDOWN、WM_RBUTTONUP、 WM_MBUTTONDOWN、WM_MBUTTONUP,lParam参数的低位是鼠标的客户区x坐标,高位是客户区y坐标。wParam参数是指示鼠标键及Shift和Ctrl键的状态。wParam&MK_SHIFT或MK_CTRL,如果返回TRUE就意味着有按下Shift或Ctrl 键。
6. 非客户区消息,wParam参数指明移动或者单击鼠标键的非客户区位置,以HT开头,lParam参数低位指出了鼠标所在屏幕坐标的x坐标,高位指出了鼠标所在屏幕坐标的y坐标。
7. 鼠标轮滚动消息,WM_MOUSEWHEEL消息,lParam将获得鼠标的屏幕位置(坐标),wParam参数的低位表明鼠标键和Shift与Ctrl 键的状态。wParam高位有一个“delta”值,该值可正可负,指出了滚轮导致屏幕滚动几行,120表示向上3行。
8. 计时器消息WM_TIMER,wParam参数等于计时器的ID值,lParam为0
9. 按钮子窗口的WM_COMMAND消息,wParam参数的低位是子窗口ID,高位是通知码, lParam参数是接收消息的子窗口的句柄。
10. 焦点消息,对于正在失去焦点的窗口,会收到WM_KILLFOCUS消息,其wParam参数是即将接收输入焦点的窗口的句柄。对于即将获取焦点的窗口,会收到WM_SETFOCUS消息,其wParam参数是正在失去焦点的窗口的句柄。
11. 编辑控制的WM_COMMAND消息,wParam参数的低位是子窗口ID,高位是通知码, lParam参数是子窗口句柄。
12. 列表框的WM_COMMAND消息,wParam参数的低位是子窗口ID,高位是通知码, lParam参数是子窗口句柄。
13. 菜单消息1,WM_INITMENU,wParam是主菜单句柄,lParam是0.
14. 菜单消息2,WM_MENUSELECT,菜单跟踪消息,指针移到菜单的某一些,就会发送这个消息给窗口过程,其wParam参数的低位是选中项菜单的 ID或者弹出式菜单的句柄,高位是选择标识,lParam参数是包含选中项的菜单句柄。
15. 菜单消息3,WM_INITMENUPOPUP,准备显示一个弹出式菜单时产生的消息,wParam参数是弹出式菜单的句柄,lParam的低位是弹出式菜单的索引,如果该菜单是系统菜单,那么高位是1,否则为0.
16. 菜单消息4,WM_COMMAND,选中菜单后产生,wParam低位是击中菜单的ID,高位是0,lParam参数也是0.
17. 菜单消息5,WM_SYSCOMMAND,表示用户从系统菜单中选择一个启用的菜单项,其wParam参数是菜单的ID, lParam为0.如果该消息是由按鼠标产生的,那么lParam参数是鼠标的屏幕坐标。
18. 加速键消息,WM_COMMAND消息,wParam低位是加速键ID,高位是1, lParam是0.
19.控制项着色消息,WM_CTLCOLORBTN消息,wParam是按钮的设备描述表句柄,lParam是按钮的窗口句柄.
22 GetModuleFileName
得到全路径
TCHAR exeFullPath[MAX_PATH]; // MAX_PATH
GetModuleFileName(NULL,exeFullPath,MAX_PATH);//得到程序模块名称,全路径
也就是当前运行程序的全路径
利用方法一的解析路径的方法,即可得到程序所在路径。
GetModuleFileName函数原型
DWORD GetModuleFileName(
HMODULE hModule, // handle to module。将要得到的模块的句柄。如果是当前模块,NULL
LPTSTR lpFilename, // path buffer 得到的文件名。
DWORD nSize // size of buffer 一般MAX_PATH就可以了