简单的WIN32动态调用DLL多项目实现

VS2005,单解决方案,2个项目:DLLTEST,MAIN2。

【文件/新建/项目】-> Visual C++ -> Win32控制台应用程序,输入项目名称,在“应用程序设置”中,"应用程序类型"选择“DLL",“附加选项”:“空项目”。

 

创建一个DLL

 

vs2005自动生成一个解决方案,此时右击【解决方案管理器】中的解决方案,再添加一个WIN32控制台应用程序:MAIN2。

 

动态调用DLL,只需要DLL文件,(静态链接还必须要有.lib文件才能编译,并且要设置MAIN1依赖于DllTest),当编译生成可执行文件后,只需要DLL文件。)

 

静态链接DLL时,若没有.lib文件或没有设置MAIN1依赖于DllTest时,会有链接错误:

1>正在编译...
1>MAIN1.cpp
1>正在链接...
1>MAIN1.obj : error LNK2001: 无法解析的外部符号 "int __cdecl TheFunction(void)" (?TheFunction@@YAHXZ)

 

动态链接DLL可以不依赖于任何DLL项目,只需要DLL文件即可。

 

动态调用DLL,可以对DLL文件无法打开时做出额外响应(静态调用不行),

动态调用DLL,首先需要调用LoadLibrary(),获得DLL的句柄,然后再调用GetProcAddress()函数获得输出函数的地址。   

 

在《简单的WIN32静态调用DLL多项目实现》中提到的在DLL中对于输出函数的声明:

_declspec ( dllexport ) BOOL TheFunction();

此时编译能够通过,程序也能够执行,但是GetProcAddress()函数却找不到输出函数的地址,也就是无法调用DLL中的函数。

对此,请看下面摘取《Windows核心编程(第五版)》P516中的一段话:

 

“读者还是会注意到MYLIBAPI符号包含了extern "C"修饰符。只有在编写C++代码的时候,才应该使用这个修饰符,在编写C代码的时候不应该使用该修饰符。C++编译器通常会对函数名和变量名进行改编(mangle),这在链接的时候会导致严重的问题。 ... 当连接器试图连接可执行文件的时候,会发现可执行文件引用了一个不存在的符号并报错。extern "C"用来告诉编译器不要对变量名或函数名进行改编,这样用C、C++或任何编程语言编写的可执行模块都可以访问该变量或函数。”

 

C++编译器通常会对函数名和变量名进行改编(mangle),这里的原因主要是针对C++所支持的函数重载。

 

 此时在DLL项目文件中把声明改为:

extern "C" _declspec ( dllexport ) BOOL TheFunction();

程序编译通过且执行成功。

在动态调用DLL中添加extern "C"的原因是调用程序找不到DLL中的函数地址(因为在编译时进行了改编),改编成了什么?可以在相应的.lib文件中看到,用记事本打开.lib文件,会找到类似一下的代码:

__NULL_IMPORT_DESCRIPTOR DllTest_NULL_THUNK_DATA ?TheFunction@@YAHXZ __imp_?TheFunction@@YAHXZ

其中的?TheFunction@@YAHXZ既是改编后的真实输出函数TheFunction的名字。若不使用extern "C"修饰符,直接在GetProcAddress()函数中使用"?TheFunction@@YAHXZ"也可以。不过还是使用extern "C"方便一些。这也可以说明在静态调用DLL中,程序在连接.lib文件时自动匹配调用程序调用的DLL中的函数,所以可以不使用extern "C"在静态调用DLL的程序中。

 

对于静态调用DLL,在调用程序中可以不使用extern "C" _declspec(dllimport)修饰符,“但是,如果编译器能够提前知道我们所引用的符号是从一个DLL的.lib文件中导入的,那么它将能够产生略微高效的代码。有鉴于此,我建议读者在导入函数和数据符号的时候使用__declspec(dllimport)关键字。如果调用的是标准的Windows函数,那么Microsoft已经替我们准备好了。”(《Windows核心编程(第五版)》P520)

对于动态调用DLL,在调用程序中可以不使用extern "C" _declspec(dllimport)修饰符。“Because the program uses run-time dynamic linking, it is not necessary to link the module with an import library for the DLL.”(MSDN中在解释GetProcAddress()函数时的例子“Using Run-Time Dynamic Linking”)。

 

在调用程序中若不使用extern "C" _declspec(dllimport)修饰符(此时不清楚是哪里的函数了),用户定义了和DLL输出完全相同(返回值,函数名,参数及参数类型)的函数,此时编译时会出错:

 

 1>正在编译...
1>MAIN1.cpp
1>./MAIN1.cpp(60) : error C2556: “DWORD TheFunction(void)”: 重载函数与“BOOL TheFunction(void)”只是在返回类型上不同
1>        ./MAIN1.cpp(16) : 参见“TheFunction”的声明
1>./MAIN1.cpp(60) : error C2371: “TheFunction”: 重定义;不同的基类型
1>        ./MAIN1.cpp(16) : 参见“TheFunction”的声明
1>./MAIN1.cpp(67) : error C3861: “TheFunction”: 找不到标识符

 

 

看来还是效仿《Windows核心编程(第五版)》P514的做法,将extern "C" _declspec(dllimport)修饰符添加到调用程序中去。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值