分类: C/C++
2010-04-23 13:00:49
//文件: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
2:OK下面来看看,动态链接库,首先是非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连接的时候默认吧add当c++连接了。连接成了形如add@fff的形式,所以GetProcAddress(hDll, "add");就找不到add了。
还有一个很重要的问题,首先我们应该知道,一个 动态链接库其实所有的实现函数都在DLL里面,同时生成的lib文件不过是指明了DLL里面有哪些可以调用的函数而已。调用DLL的工程没有加DLL的头文件和lib,他就加载了DLL,这个DLL里面已经包含了要调用的函数的实现,而我们之后在知道这个DLL有ADD函数的情况下直接找他,当然就不需要LIB文件了! 下面那个例子就需要加入lib,因为我们直接使用了add,就需要Lib去dll中找add的函数原型
3:下面来看静态调用,静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记 录减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)编程深入浅出