EXPORTS
K9RtSysInterrupt
K9RtSysExprm
; Explicit exports can go here
//delphi 调用文件
unit InterfaceFunc;
interface
uses
UnitData,Types;
function K9RtSysExprm(nModuleTag:integer ;
uTimeout:integer;
pTestPar:Pointer;
pResultPar:Pointer) : Integer; stdcall;
function K9RtSysInterrupt(nInterruptType:Integer) : Integer; stdcall;
implementation
const DLLPATH = 'RtBsExpr.dll';
function K9RtSysExprm; external DLLPATH name 'ExperimentRelayTest';
function K9RtSysInterrupt; external DLLPATH name 'InterruptRelayTest';
end.
extern "c"与.def文件的作用
首先,我们需要知道C和C++编译器对函数名字的处理方式是不一样的;其次,就是同为C编译器的两个不同产品,在编译时对函数名字的处理方式也是有区别的,比如microsoft vc++与dev c++。所以,extern "C"与.def文件正是为了解决这两种情况而引入的处理方法。
第一、extern "C"的作用
比如一个C源程序A.c要使用C++编写的库函数,在A.c中#include "B.h",其中B.h中有要使用的函数的原形声明func。当编译链接源程序时,却发现了“链接错误,未决的外部符号...”的错误,这是什么原因呢?
原因就是,C编译器编译A.c时,将func编译为func,当链接时链接器去C++库中寻找func,但是C++的编译器在编译库时将func编译成_func@yyy@rrr,自然链接器就找不着相应的函数的信息了,所以就会报错!有什么办法可以处理这种情况呢?——可以在编写C++库的时候,为每一个函数(或导出函数)加上extern "C",它的含义是告知C++编译器在编译这些函数的时候,以C编译器的方式处理函数名。这样生成的库中的函数名字就是func了,当C程序调用库函数,编译链接时,链接器就能找到期望的信息,则链接成功。
第二、.def文件的作用(仅与VC++编程相关)
前面提到,不同厂商开发的两个C编译器也会有一些差异,最突出的就是microsoft的C编译器,它对函数名字的处理很特别(究竟是什么样子,可以使用Dumpbin工具查看dll的导出函数),所以要在使用他方编写的库时,程序链接能成功,有两种方法:1使用库编写者使用的C编译器(这里指VC++),显然这种方法不合理;2库的编写者在使用VC++编写库时使用.def文件。
.def文件的作用即是,告知编译器不要以microsoft编译器的方式处理函数名,而以指定的某方式编译导出函数(比如有函数func,让编译器处理后函数名仍为func)。这样,就可以避免由于microsoft VC++编译器的独特处理方式而引起的链接错误。
用.def文件描述dll的输出函数,比如
EXPORTS
DllFun1 @1 NONAME
DllFun2 @2 NONAME
DllFun3 @3 NONAME
DllFun4 @4 NONAME
...
@后面是dll输出函数的顺序号,顺序号要求在1到max(函数个数)之间。
用.def声明了输出函数以后,不要在头文件里再声明AFX_EXT_API或者是dllexport之类的声明,而用extern "C"声明了输出函数以后,要引用原来输出函数的头文件。如果不是用extern "C"声明的(包括用.def声明的),那么头文件里无需用AFX_EXT_API或者是dllexport之类的声明。
__declspec(dllexport) 与 DEF文件以及extern "c"
情形如下:
在DLL中用 __declspec(dllexport)导出函数,比如导出int fun(void);,在EXE程序中通过LoadLibrary()显示调用,用GetProcAddress()函数来获取函数地址,获取失败,通过查看发现DLL中的函数名已经编译为?fun@@YAHXZ,通过测试,用?fun@@YAHXZ来调用GetProcAddress()确实能够获取fun的地址。
后来查询资料知道应该用extern "C" 来指定C链接。加了extern "C"之后,验证通过。
之后,我用DEF文件导出,直接用EXPORTS fun导出函数,并且不加extern "c" 发现在显示调用时也能通过。
但是,MSDN上有这么一句话:使用 .def 文件的主要缺点是:在 C++ 文件中导出函数时,必须将修饰名放到 .def 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。
但是我在用.def文件时并未使用修饰名,而是直接用的fun,而且没有指定C链接,却能够执行成功