DLL(三)显示链接
隐式链接虽然实现较为简单,但除了必须的.dll文件之外还需要DLL的.h文件(虽然不一定必须)和.lib文件。这在那些只提供.dll文件的场合就无法使用,而这种情况下我们只能用显示链接了。
显示链接:
是有应用程序在运行过程,由其空间中的线程决定是否调用DLL中的输出函数。
{
// TODO: 在此添加控件通知处理程序代码
HINSTANCE hInst;
hInst=LoadLibrary(L"dll1.dll");
typedef int (*ADDPTR)(int a,int b);//定义一个函数指针
ADDPTR add; //add函数指针变量
if(hInst!=NULL){//载入DLL成功
add=(ADDPTR)GetProcAddress(hInst,"add");//去除dll中函数名为add的这个函数的地址
//如果发生了函数的名字改编这里是不是就会失败啊!
if(NULL!=add){//找到DLL中的add函数
CString str;
/*
注意:因为这里编译器默认定义了unicode了吧,解决办法如下
Format ( L"%c",s ) ;
或者Format ( _T("%c"),s ) ;
*/
str.Format(L"5+3=%d",add(5,3));
MessageBox(str);
}else{
MessageBox(L"获取函数地址失败");
}
}else{
MessageBox(L"加载DLL失败");
}
}
把dll1.dll拷贝到当前目录下发现加载DLL没有问题,但是提示获取函数地址失败,我想到难道是dll1.dll中的add函数名被改编了,非常有可能。
我看了一下dll1的工程的代码发现果然如此就一个dll1.cpp,而且代码是下面这样的
{
return a+b;
}
_declspec(dllexport) int subtract(int a,int b)
{
return a-b;
}
dumpbin了一下,当然会像下面这样了!
这样当然会名字改变,我又不想用extern"C"的方式来做,因为考虑到通用性dll中的函数调用应该做出_stdcall而不是用默认的_cdecal
那样的话即使用extern"C"也没法阻止名字改编了,所以我们就应该用.def文件。
于是定义了dll1.def文件加到工程中,并在项目属性的连接器那加上了.def(方法看DLL(一)),dll1.cpp改成下面这样
{
return a+b;
}
/*_declspec(dllexport)*/ int subtract(int a,int b)
{
return a-b;
}
然后再dumpbin,这下输出就是
没有发生名字改编这下dll_test程序应该可以正常运行了吧,果然不出所料,成功的输出了
为什么用显示连接呢?
1)隐式链接需要.dll .lib .h而显示连接原则上只需要.dll
2) 我们看到的一些程序通常都不是一个.dll而是多个.dll,那如果做成隐式链接的话那在.exe启动的时候会把所有的.dll都加载到进程的地址空间这样的做的话会造成启动的缓慢,而且可能在程序运行过程中可能.dll中的很多函数我们都不需要去访问。可能是在某个条件发生的时候才会访问某个dll中的某个输出函数。如果隐式链接的话所有点函数都已经被加载进了,这样浪费资源(所以的DLL和所有DLL中的函数)。这种情况下我们可以动态加载。在某个条件发生的时候再loadlibary(*.dll)
然后GetProcAddress找到某个函数,用了之后再freeLibary(*.dll)这种显示连接其实是一种动态链接。
有一个地方我们要注意一下:
如果我们为了把.dll做的更加通用,改成下面这样
dll1.cpp:改变调用约定
*/
_stdcall int add(int a,int b)
{
return a+b;
}
_stdcall int subtract(int a,int b)
{
return a-b;
}
这样的调用约定的改变并不会改变函数的名字,就是不会发生名字改编,因为我们有dll1.def已经定死了
但是我们在dll1_test中如果还是按原先的方式进行调用就会出错。我们将改之后的dll1.dll替换掉原先dll1_test中的dll,但是调用函数的代码不改变。
那会怎么样,也许我们认为尽管改变了调用约定,但是函数没有发生名字改编,所以应该无需该代码啊。但是实际情况是让我们失望的,必须如下改动
typedef int (_stdcall *ADDPTR)(int a,int b);
也就是说使用dll的用户要遵照dll的创建者规定的调用约定进行调用,不然就无法正确调用,只是需要我们注意到。
我们还要注意到一点就是这种显示链接dll库的做法,用dumpbin -imports dll_test.exe命令时是无法看出加载了哪个DLL的信息的。