孙鑫老师笔记19课.docx
《孙鑫老师笔记19课.docx》由会员分享,可在线阅读,更多相关《孙鑫老师笔记19课.docx(10页珍藏版)》请在冰豆网上搜索。
孙鑫老师笔记19课
Lesson19动态链接库DLL
WindowsAPI中的所有函数都包含在DLL中。
其中有三个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。
静态库和动态库
静态库:
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。
在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件);
在使用动态库的时候,往往提供两个文件:
一个因入库和一个DLL。
因入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。
在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数。
使用动态链接库的好处
可以采用多种编程语言来编写。
增强产品的功能。
提供二次开发的平台。
简化项目管理
可以节省磁盘空间和内存。
有助于资源的共享。
有助于实现应用程序的本地化
建立DLL文件代码如下:
_declspec(dllexport)intAdd(intx,inty)
{
returnx+y;
}
_declspec(dllexport)intSubtract(intx,inty)
{
returnx-y;
}
//必须带_declspec(dllexport)文件,以生成*.lib文件
如果要查找*dll中包含信息,可在命令行下进入Debug所在目录,输入以下命令
dumpbin-exportsdll.dll
有些时候由于某种安装原因,dumpbin被认为是无效命令,接下来在
C:
\ProgramFiles\MicrosoftVisualStudio\VC98\Bin\下找到VCVARS32.bat并在命令行运行,之后就能执行dumpbin命令了。
新建MFC程序,新建两个按钮,代码如下:
voidCDllTestDlg:
:
OnBtnAdd()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
CStringstr;
str.Format("3+5=&d",Add(3,5));
MessageBox(str);
}
voidCDllTestDlg:
:
OnBtnSubtract()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
CStringstr;
str.Format("5-3=%d",Subtract(5,3));
MessageBox(str);
}
为使编译器认识Add,Subtract,必须在之前使用两个声明:
externintAdd(intx,inty);
externintSubtract(intx,inty);
可以使用标示符表示这两个函数是从动态链接库的.lib文件引用的,以生成效率更高的代码
_declspec(dllimport)intAdd(intx,inty);
_declspec(dllimport)intSubtract(intx,inty);
这两段代码我们也可以在DLL中新建一个头文件放进去,并在MFC程序中添加头文件
如#include"..\Dll\Dll.h"
从原先Dll文件下Debug目录中复制*.lib到MFC程序文件夹下,并添加库函数
在project--->setting--->link--->Object/LibraryModules写下所复制的文件名
如果要查看DllTest.exe文件信息,使用命令行dumpbin-importsdlltest.exe
修改动态链接库Dll.h
#ifdefDLL_API
#else
#defineDLL_API_declspec(dllimport)
#endif
DLL_APIintAdd(intx,inty);
DLL_APIintSubtract(intx,inty);
修改Dll.cpp文件
#defineDLL_API_declspec(dllexport)
#include"Dll.h"
intAdd(intx,inty)
{
returnx+y;
}
intSubtract(intx,inty)
{
returnx-y;
}
这样做是为了方便外部程序调用同时方便内部程序使用,因为动态链接库中只有导出的函数才可以被使用,没有导出的函数在外部是看不到的,是不能被访问的
接下来导出整个类,代码:
classDLL_APIpoint
{
public:
voidoutput(intx,inty); //如果只想导出一个函数,可把上边的DLL_API剪切 //然后放到voidoutput(intx,inty);前边,虽然累没有被 //导出,但访问仍没有区别
};仍然受制于访问权限
实现:
voidPoint:
:
output(intx,inty)
{
HWNDhwnd=GetForegroundWindow();
HDChdc=GetDC(hwnd);
charbuf[20];
memset(buf,0,20);
sprintf(buf,"x=%d,y=%d",x,y);
TextOut(hdc,x,y,buf,strlen(buf));
ReleaseDC(hwnd,hdc);
}
接下来在MFC程序新建一个按钮,调用动态链接库函数,代码如下:
Pointpt;
pt.output(100,200);
因为C++导出或导入动态链接库会发生名字的改编,如果不想发生名字改编,我们可以使用如下代码:
#defineDLL_API extern"c"_declspec(dllexport)
这样编译器就不会进行名字改编,一个用C语言编写的客户端程序就可以调用这个用C++编写的动态链接库。
其缺点是,不能导入类中的函数
如果函数使用标准调用约定_stdcall,即使使用了extern"c",此函数仍会发生改编
接下来新建一个动态链接库文件,文件名为Dll2,cpp文件代码为:
intAdd(intx,inty)
{
returnx+y;
}
intSubtract(intx,inty)
{
returnx-y;
}
为了最终解决问题,我们可以新建一个模块文件Dll.def,以使得其他语言编制的程序也能使用我们的动态链接库。
添加代码
LIBRARYDll2
EXPORTS //即使调用_stdcall约定,也不会发生改编,而只会调用这里显示的Add //字符串
Subtract
EXPORTS语句引入了一个由一个或多个definitions(导出的函数或数据)组成的节。
每个定义必须在单独一行上。
EXPORTS关键字可以在第一个定义所在的同一行上或在前一行上。
.def文件可以包含一个或多个EXPORTS语句。
导出definitions的语法为:
entryname[=internalname][@ordinal[NONAME]][PRIVATE][DATA]
entryname是要导出的函数名或变量名。
这是必选项。
如果导出的名称与DLL中的名称不同,则通过internalname指定DLL中导出的名称。
例如,如果DLL导出函数func1(),要将它用作func2(),则应指定:
EXPORTS
func2=func1
@ordinal允许指定是序号而不是函数名将进入DLL的导出表。
这有助于最小化DLL的大小。
.LIB文件将包含序号与函数之间的映射,这使您得以像通常在使用DLL的项目中那样使用函数名。
可选的NONAME关键字允许只按序号导出,并减小结果DLL中导出表的大小。
但是,如果要在DLL上使用GetProcAddress,则必须知道序号,因为名称将无效。
可选的PRIVATE关键字禁止将entryname放到由LINK生成的导入库中。
它对同样是由LINK生成的图像中的导出无效。
可选的DATA关键字指定导出的是数据,而不是代码。
例如,可以导出数据变量,如下所示:
EXPORTS
iDATA
接下来在MFC文件中改写按钮voidCDllTestDlg:
:
OnBtnAdd()代码:
HINSTANCEhInst;
hInst=LoadLibrary("Dll2.dll"); //使用动态加载
typedefint(*ADDPROC)(inta,intb);
//如果在DLL的函数中调用_stdcall,相应的应该把代码改为
//typedefint(_stdcall*ADDPROC)(inta,intb); //注意到处函数的调用约定
ADDPROCAdd=(ADDPROC)GetProcAddress(hInst,"Add");//构造一个函数指针
if(!
Add)
{
MessageBox("获取函数地址失败!
");
return;
}
CStringstr;
str.Format("3+5=%d",Add(3,5));
MessageBox(str);
因为调用LoadLibrary时动态加载动态链接库,所以不需要头文件和.lib文件
如果我们在动态链接库中使用标准调用约定_stdcall,而在可执行程序中使用动态加载DLL,会发生名字重编,如果知道DLL中函数的序号,这时可以使用宏MAKEINTRESOURCE把序号转变成名字,如:
ADDPROCAdd=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE
(1));
DllMain
TheDllMainfunctionisanoptionalentrypointintoadynamic-linklibrary(DLL).Ifthefunctionisused,itiscalledbythesystemwhenprocessesandthreadsareinitializedandterminated,oruponcallstotheLoadLibraryandFreeLibraryfunctions.
DllMainisaplaceholderforthelibrary-definedfunctionname.YoumustspecifytheactualnameyouusewhenyoubuildyourDLL.Formoreinformation,seethedocumentationincludedwithyourdevelopmenttools.
BOOLWINAPIDllMain(
HINSTANCEhinstDLL,
DWORDfdwReason,
LPVOIDlpvReserved
);
当我们的动态链接库不再使用时可以调用FreeLibrary使动态链接库使用计数减1,当使用计数为零时,系统会收回模块资源
FreeLibrary
TheFreeLibraryfunctiondecrementsthereferencecountoftheloadeddynamic-linklibrary(DLL).Whenthereferencecountreacheszero,themoduleisunmappedfromtheaddressspaceofthecallingprocessandthehandleisnolongervalid.
BOOLFreeLibrary(
HMODULEhModule
);