在C++Builder里创建可以被Visual C++使用的DLL

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

在C++Builder里创建可以被VisualC++使用的DLL这篇文章举例说明如何用C++Builder创建一个DLL,使它可以在VisualC++工程里调用。简介:为什么这个这么难指导方针摘要例1:隐式连接例2:显式连接例3:用#define组装隐式连接例4:用stdcall函数隐式连接结论简介:为什么这个这么难如果你用BCB创建了一个DLL,它可以被BCB的可执行文件调用,你知道这种使用DLL的方式没什么难度。当你构造一个DLL,BCB生成一个带“.LIB”扩展名的引入库。把这个LIB文件添加到你的工程里。连接器按引入库决定DLL内部调用。当你运行你的程序时,DLL隐式的被载入,你不必去考虑DLL内部调用工作是。当EXE文件是由MicrosoftVisualC++编译的时候,情况会变得比较复杂。有3个主要的问题。首先,BCB和MSVC对DLL中的函数命名方式是不一致的。BCB使用一种习惯,MSVC使用另一种不同的习惯。当然,两种习惯是不兼容的。命名问题在如何在C++Builder工程里使用VC++编译的DLL那篇文章里已经讨论过了。表1总结了各个编译器在各自的调用习惯下,导出的MyFunction函数。注意Borland给__cdecl函数前加了一个下划线,而MSVC没有。另一方面,MSVC认为导出的__stdcall函数前面带有下划线,后面还有一些垃圾。表1:VisualC++andC++Builder命名习惯调用习惯VC++命名VC++(使用了DEF)C++Builder命名-----------------------------------------------------------------------__stdcall_MyFunction@4MyFunctionMyFunction__cdeclMyFunctionMyFunction_MyFunction第2个问题是Borland引入库与MSVC不是二进制兼容的。当你编译DLL时,由BCB创建的引入库不能被MSVC用来连接。如果你想使用隐式连接,那么你需要创建一个MSVC格式的引入库。另一种可选择的办法就是采用显式连接(LoadLibrary和GetProcAddress)。第3个问题是不能从DLL里导出C++类和成员函数,如果你想让MSVC的用户也可以调用它。好吧,那不完全属实。你的DLL能导出C++类,但是MSVC不能使用它们。原因就是C++成员函数名被编译器改编(mangled)。这个改编的名字结果了DLL。为了调用在DLL里被改编的函数,你必需知道被改编的是哪个函数。Borland和Microsoft使用了不同的名字改编方案。结果是,MSVC不能恰好看到Borland编译的DLL里的C++类和成员函数。注意:Borland和Microsoft没有采用相同的方式改编函数,因为依照ANSIC++标准,C++编译器不被假定追随相同的指导方针。名字改编只是实现的细节。这三个问题使得Borland创建的DLL可以在MSVC里被调用变得非常困难,但并非不可能的。这篇文章描述了一套指导方针,你可以跟着制作与Microsoft兼容的BCBDLL。我们讨论四种不同的技术。三种采用引入库隐式连接调用,一种在运行时利用显式连接。指导方针摘要你可以跟着下面的指导方针摘要列表建造你的DLL。第1个列表讨论隐式连接;第2个列表描述显式连接;第3种技术采用#define组装隐式连接;最后一个例子利用假的MSVCDLL工程为__stdcall函数创建引入库。技术1:隐式连接------------------------------------------------------------------------------1-使用__cdecl调用习惯代替__stdcall。2-导出简单的C风格函数,没有C++类或成员函数。3-确定你有一个externC{}包围你的函数原型。4-创建DEF文件,包含与Microsoft兼容的导出函数别名。别名也就是不包含前面的下划线。DEF文件内容如下:EXPORTS;MSVCname=BorlandnameFoo=_FooBar=_Bar5-把DEF文件加入到你的工程里重新编译它。6-把DLL和DLL头文件拷贝到你的MSVC工程目录里。7-运行impdef为DLL创建第2个DEF文件。这个DEF文件用来创建引入库。impdefmydll.defmydll.dll8-运行Microsoft的LIB工具,用上一步创建的DEF文件创建COFF引入库。调用格式为:lib/DEFmydll.def9-把用LIB.EXE创建的LIB文件添加到你的MSVC工程里。技术2:显式连接------------------------------------------------------------------------------1-使用__cdecl或__stdcall,如果你使用__stdcall可以跳过第4,5步。2-导出简单的C风格函数,没有C++类或成员函数。3-确定你有一个externC{}包围你的函数原型。4-如果你使用__cdecl,那么你可能想去掉导出函数前面的下划线,但你不必这么做。你可以用例1的第4,5步去掉下划线。如果你没有去掉下载线,在调用GetProcAddress函数时函数名必须前面的下划线。5-把DLL拷贝到MSVC工程目录里。6-在MSVC应用程序中,使用LoadLibraryAPI函数载入DLL。7-调用GetProcAddressAPI在DLL里查找你想要的调用函数,保存GetProcAddress函数返回的函数指针。当你想调用函数的时候,提取函数指针。8-当你用完DLL时调用FreeLibrary。技术3:用#define组装隐式连接------------------------------------------------------------------------------1-用__cdecl调用习惯代替__stdcall。2-导出简单的C风格函数,没有C++类或成员函数。3-确定你有一个externC{}包围你的函数原型。4-在你的DLL头文件里,为每一个导出函数名创建一个#define。#define会调用预编译器在每一个函数名前加上下划线。因为我们只想为MSVC创建别名,所以代码检查_MSC_VER。#ifdef_MSC_VER#defineFoo_Foo#defineBar_Bar#endif5-把DLL和DLL头文件拷贝到MSVC工程目录里。6-运行impdef为DLL函数DEF文件。impdefmydll.defmydll.dll7-使用Microsoft的LIB工具为DEF文件创建COFF格式的引入库。lib/defmydll.def8-把LIB.EXE创建的LIB文件添加到MSVC工程里。技术4:用__stdcall函数隐式连接------------------------------------------------------------------------------1-当建造你的DLL时使用__stdcall调用习惯。2-导出简单的C风格函数,没有C++类或成员函数。3-确定你有一个externC{}包围你的函数原型。4-为MSVC创建一个引入库。这一部分比较困难。你不能用LIB.EXE为__stdcall函数创建引入库。你必须创建一个由MSVC编译的的假的DLL。这样做,按这些步骤:4a-用MSVC创建一个不使用MFC的DLL4b-从BCB里拷贝覆盖DLL头文件和DLL源代码4c-编辑你的DLL源代码,抛开每一个例程的函数体部分,使用一个假的返回值返回4d-配置MSVC工程生成的DLL,采用和BCBDLL同的的名字4e-把DEF文件添加到MSVC工程,禁止它对__stdcall命名进行修饰(_Foo@4)5-编译第4步得到的虚假DLL工程。这将会生成一个DLL(你可以把它丢到垃圾筒里)和一个LIB文件(这是你需要的)。6-把从第5步得到的LIB文件添加到你需要调用这个BCBDLL的MSVC工程里。LIB文件会确保连接。为MSVC可执行文件配置BCBDLL(不是虚假DLL)。注意:一般情况下,隐式连接比显式连接要优先考虑,因为对程序员来说隐式连接更简单,而且它是类型安全的(错误发生在连接时而不是运行时)。不管用哪种方法,当你在编译器间共享DLL时,如果你选择坚持使用隐式连接,就必须为每一个编译器创建兼容的引入库。创建兼容的引入库比用显式连增加的负担就是要注意更多的要求。注意:如果你想使你的DLL可以被VisualBasic的开发者使用,显式连接的指导方针同样适用。如果你想把你的DLL给VC开发者,按显式连接的指导方针,采用__stdcall调用习惯。下面4个部分详细描述每一种技术。例1:显式连接这个例子详细描述了上一部分技术1的指导方针。技术1的指针方针可以分为两组。1-5项处理在BCB这边编译DLL;6-9项处理在MSVC这边使用DLL。我们将沿这条主线分别进行讨论。在这个例子里,我们将用BCB建造一个DLL,它导出两个函数:Foo和Bar。两个函数都返回一个整型值。函数原型为:intFoo(intValue);intBar(void);然后我们在MSVC里建造一个测试EXE,用来调用BorlandDLL。用BCB编译DLL下面两个程序清单包含我们的DLL源代码。清单1要在BCB和MSVC之间共享的头文件;清单2包含我们的DLL函数实现部分。创建一个BCBDLL工程,从清单1和2中拷贝代码粘贴到工程里。或者你可以下载这篇文章的源代码以节省时间。BCBDLL工程已经为你设置好了。(参见最下面的下载部分)//----------------------------------------------//Listing1-DLLheaderfile#ifndefBCBDLL_H#defineBCBDLL_H#ifdef__cplusplusexternC{#endif#ifdefBUILD_DLL#defineIMPORT_EXPORT__declspec(dllexport)#else#defineIMPORT_EXPORT__declspec(dllimport)#endifIMPORT_EXPORTint__cdeclFoo(intValue);IMPORT_EXPORTint__cdeclBar(void);#ifdef__cplusplus}#endif#endif//----------------------------------------------//----------------------------------------------//Listing2-DLLsourcecode#includewindows.h#pragmahdrstop#defineBUILD_DLL#includebcbdll.hint__cdeclFoo(intValue){returnValue+1;}int__cdeclBar(void){staticintret=0;returnret++;}//----------------------------------------------关于头文件有两个要注意的地方。首先,观察我们用externC的方法确保函数名不会被C++编译器改编;其次,注意到在我们建造DLL时,导出函数有一个特殊指示的前缀__declspec(dllexport)。当我们从MSVC里使用DLL时,函数前缀变为__declspec(dllimport)。这个指示的改变是通过IMPORT_EXPORT宏定义实现的。最后,注意我们显式声明了__cdecl为调用习惯。技术上,我们可以省略__cdecl关键字,因为__cdecl已经是默认的。但是,我想不管怎样把它列出来是一个好习惯。通过列出调用习惯

1 / 34
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功