DLL初步和钩子入门


1
首先来个静态链接库的。

//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y); //声明为C编译、连接方式的外部函数
#endif
//文件:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}

这个静态链接库就一个加法操作,以后的例子都一样。extern "C" int add(int x,int y);这一句表示这个函数要被外部调用,且编译的时候以C的方式进行编译。这样外部就可以直接调用add了,如果不声明“C”,并且这个文件为CPP的,则编译的时候add函数会被编译器加上其他东西。这样调用可能出问题。

下面是如何调用静态链接库:

#include ".."lib.h"
#pragma comment( lib, "..""debug""libTest.lib" )
 //指定与静态库一起连接,这句可以用包含库文件取代
int main(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ) );
}

看到了吧,包括头文件,然后加入静态链接库。So easy

 

 

2OK下面来看看,动态链接库,首先是非MFC的,就是VC6建立的时候选动态链接库,别选MFCdll

库文件:

extern "C" int __declspec(dllexport)add(int x, int y); 注意这个就行了,说明这是个导出函数,其他和上面一样。

接下来是使用:

typedef int(*lpAddFun)(int, int); //宏定义函数指针类型,lpaddfun 就代表了函数地址,不陌生吧

int main(int argc, char *argv[])

{
HINSTANCE hDll;
//DLL句柄 句柄的意思就是指向当前实例。代表了当前实例
lpAddFun addFun; //
函数指针
hDll = LoadLibrary("..""Debug""dllTest.dll");
 //接下来要用dll,我们就手动加载。(即动态加载)
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
 //根据句柄取到函数名。addFun的类型为lpAddFun
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("%d", result);
}
FreeLibrary(hDll);      
 //
用完之后就释放。这就是动态加载DLL 明白了吧?
}
return 0;}

加载dll-à-使用à 释放dll 3步都在程序中自己调用就是动态加载dll

这个程序我试验了一下,如果把“C”去掉,则连接出错,找不到add,原因就是DLL连接的时候默认吧addc++连接了。连接成了形如add@fff的形式,所以GetProcAddress(hDll, "add");就找不到add了。

还有一个很重要的问题,首先我们应该知道,一个动态链接库其实所有的实现函数都在DLL里面,同时生成的lib文件不过是指明了DLL里面有哪些可以调用的函数而已。调用DLL的工程没有加DLL的头文件和lib,他就加载了DLL,这个DLL里面已经包含了要调用的函数的实现,而我们之后在知道这个DLLADD函数的情况下直接找他,当然就不需要LIB文件了! 下面那个例子就需要加入lib,因为我们直接使用了add,就需要Libdll中找add的函数原型

3下面来看静态调用,静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则WindowsDLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。

只看使用就行了。

#pragma comment(lib,"dllTest.lib")
//.lib
文件中仅仅是关于其对应DLL文件中函数的重定位信息
extern "C" __declspec(dllimport) add(int x,int y); //这句一般在.h文件
int main(int argc, char* argv[])
{
int result = add(2,3);
printf("%d",result);
return 0;
}

就这么简单,注意要手动把dll放到该程序目录中。

如果是导入类的话,要用class __declspec(dllimport) point

{

Public add();

………………

}; //即类声明

总结一下前面2个DLL,我不得不把省略的地方拿出来说一下:

1 我省略了 DLLmain 其实每个DLL里面都应该有他,(事实上我只写了实现函数add)

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)

{                                                                                           switch (ul_reason_for_call)                                                                    {
 case DLL_PROCESS_ATTACH:break; //
进程加载这个dll时运行这里
 case DLL_THREAD_ATTACH:break;
 case DLL_THREAD_DETACH:break;
 case DLL_PROCESS_DETACH:break;
}
return TRUE;
}
这个函数就是DLL的入口点,当外面有进程或者线程调用这个DLL的时候就会执行DLLMain

还有一个就是一般是写一个def文件来代替__declspec(dllimport)声明的导出函数。他们作用一样

3 DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据,我们来看看在应用工程中引用DLL中变量的例子

MFC DLL我暂时不讲,先来看看钩子!很牛的东西。钩子分为系统钩子和线程钩子。先看线程钩子,很简单,在一个程序里就可以搞定(普通main或者单文档或者对话框程序)

1:初始化钩子hHook=SetWindowsHookEx(WH_MOUSE,MouseProc,0,GetCurrentThreadId());           参数说明 1 为要钩的消息类别,2为消息处理函数(回调函数) 3 后面再说 4 当前线程id 就是我们的主线程Id。。。。。。。。。。。这个钩子可以钩到该线程的所有消息!

2:钩子消息处理函数

LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
{
if(wParam==WM_MOUSEMOVE||wParam ==WM_NCMOUSEMOVE)  //
是鼠标移动消息
 { /*
随便做点什么,比如*/ }
 return CallNextHookEx(hHook,nCode,wParam,lParam); //
传递钩子信息
}

3:释放钩子

if(hHook)
UnhookWindowsHookEx(hHook);

下来是系统钩子,难一点,必须在一个DLL中创建系统钩子,然后其他线程调用这个DLL的钩子来截获自身的消息。所以要2个程序来实现

  1.建立钩子Mousehook.DLL 

(1)     选择MFC AppWizard(DLL) MFC Extension DLL(共享MFC拷贝)类型

(2)     新建一个钩子类,完成初始化钩子和停止钩子  

类中有初始化钩子:glhHook =SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);   // glhInstance为钩子函数所在的DLL句柄dwThreadId 最后一个参数指定钩子所监视的线程的线程号。对于全局钩子,该参数为NULL

 BOOL stophook(); //卸载钩子函数 (注意定义类前面要加导出还是导入,用宏做开关) 注:导入 __declspec(dllimport)

(3)     这段代码就定义了2个共享变量,即所有调用这个DLL的线程都访问时同一个变量,而不是将变量给每一个线程拷贝一份!    #pragma data_seg("mydata")    //说明底下的是共享变量
  HHOOK glhHook=NULL;
  //安装的鼠标钩子句柄
  HINSTANCE glhInstance=NULL;
  //DLL实例句柄
  #pragma data_seg()
  在DEF文件中定义段属性:
  SECTIONS
  mydata READ WRITE SHARED  //说明mydata是共享变量

  2.建立调用系统钩子的程序

(1)    CWnd * pwnd=GetDlgItem(IDC_EDIT1); //取得编辑框的类指针

(2)    m_hook.starthook(pwnd->GetSafeHwnd()); //取得编辑框的窗口句柄并安装钩子 ,之后上面的钩子DLL根据这个传入的句柄来发送消息给该应用程序。                                                                                    就这么简单,超乎想象。。首先取得编辑框的指针,然后通过指针获取编辑框的句柄,当参数传给hook 即钩子。这个在dll中的类函数完成了所有要做的事情。Starthook中调用MouseProc我们就来认真看下他。

LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)

{

LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam;

if (nCode>=0)

 {

       HWND glhTargetWnd=pMouseHook->hwnd; //调用这个DLL的进程的窗口的句柄!我的理解。不能肯定

        HWND ParentWnd=glhTargetWnd;

        while (ParentWnd !=NULL)

        {

        glhTargetWnd=ParentWnd;

        ParentWnd=GetParent(glhTargetWnd); //取应用程序主窗口句柄

        }

       if(glhTargetWnd!=glhPrevTarWnd)   //如果不是新窗口就什么都不做

       {

        char szCaption[100];

       GetWindowText(glhTargetWnd,szCaption,100);     //取目标窗口标题

   if(IsWindow(glhDisplayWnd))   // glhDisplayWnd就是调用这个钩子DLL的窗口句柄。就是上面传的参数

       SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption); //

       glhPrevTarWnd=glhTargetWnd; //保存目标窗口

       }

       }

return CallNextHookEx(glhHook,nCode,wparam,lparam); //继续传递消息

}

总结一下:进程启动DLL中的设置HOOK(钩子),根据hook的设置,它就开始等待鼠标消息的到来,当线程的鼠标消息一来,hook就先于线程截获该消息,然后根据lparam结构的hwnd参数判断鼠标现在是否在一个新的窗口,如果是在一个新的窗口就将该窗口的标题发送给线程,作为文本输出。

    大家应该看到,这个钩子被设置为了全局钩子(SetWindowsHookEx的最后一个参数为0),即这个钩子是可以监视该电脑的所有鼠标消息的。只要有鼠标消息,不管是来自哪个线程,都回被我们这个可爱的钩子截获进行处理!

具体见:http://dev.csdn.net/author/js333/75a96924e6904cd68a0a527bebe5f78c.html     钩子的类型和实现 csdn

http://hi.baidu.com/zhanglei_186/blog/item/09e6490704a666c97a89474a.html     VC++动态链接库(DLL)编程深入浅出

原文地址:https://www.cnblogs.com/SuperXJ/p/1575259.html