c中的模板及函数怎么封装为dll.docx
- 文档编号:17361086
- 上传时间:2023-07-24
- 格式:DOCX
- 页数:15
- 大小:24.46KB
c中的模板及函数怎么封装为dll.docx
《c中的模板及函数怎么封装为dll.docx》由会员分享,可在线阅读,更多相关《c中的模板及函数怎么封装为dll.docx(15页珍藏版)》请在冰点文库上搜索。
c中的模板及函数怎么封装为dll
竭诚为您提供优质文档/双击可除
c,,中的模板及函数怎么封装为dll
篇一:
封装dll
基于Visualc++6.0的dll编程实现
一、前言
自从微软推出16位的windows操作系统起,此后每种版本的windows操作系统都非常依赖于动态链接库(dll)中的函数和数据,实际上windows操作系统中几乎所有的内容都由dll以一种或另外一种形式代表着,例如显示的字体和图标存储在gdidll中、显示windows桌面和处理用户的输入所需要的代码被存储在一个userdll中、windows编程所需要的大量的api函数也被包含在kerneldll中。
在windows操作系统中使用dll有很多优点,最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个dll文件,真正实现了资源"共享",大大缩小了应用程序的执行代码,更加有效的利用了内存;使用dll的另一个优点是dll文件作为一个单独的程序模块,封装性、独立性好,在软件需要升级的时候,开发人员只需要修改相应的dll文件就可以了,而且,当dll中的函数改变后,只要不是参数的改变,程序代码并不需要重新编译。
这在编程时十分有用,大大提高了软件开发和维护的效率。
既然dll那么重要,所以搞清楚什么是dll、如何在windows操作系统中开发使用dll是程序开发人员不得不解决的一个问题。
本文针对这些问题,通过一个简单的例子,即在一个dll中实现比较最大、最小整数这两个简单函数,全面地解析了在Visualc++编译环境下编程实现dll的过程,文章中所用到的程序代码在windows98系统、Visualc++6.0编译环境下通过。
二、dll的概念
dll是建立在客户/服务器通信的概念上,包含若干函数、类或资源的库文件,函数和数据被存储在一个dll(服务器)上并由一个或多个客户导出而使用,这些客户可以是应用程序或者是其它的dll。
dll库不同于静态库,在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.lib),Visualc++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。
这个过程称为"静态链接",此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。
在动态库的情况下,有两个文件,一个是引入库(.lib)文件,一个是dll文件,引入库文件包含被dll导出的函数的名称和位置,dll包含实际的函数和数据,应用程序使用lib文件链接到所需要使用的dll文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是dll中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。
从上面的说明可以看出,dll和.lib文件必须随应用程序一起发行,否则应用程序将会产生错误。
微软的Visualc++支持三种dll,它们分别是non-mFcdll(非mFc动态库)、Regulardll(常规dll)、extensiondll(扩展dll)。
non-mFcdll指的是不用mFc的类库结构,直接用c语言写的dll,其导出的函数是标准的c接口,能被非mFc或mFc编写的应用程序所调用。
Regulardll:
和下述的extensiondlls一样,是用mFc类库编写的,它的一个明显的特点是在源文件里有一个继承cwinapp的类(注意:
此类dll虽然从cwinapp派生,但没有消息循环),被导出的函数是c函数、c++类或者c++成员函数(注意不要把术语c++类与mFc的微软基础c++类相混淆),调用常规dll的应用程序不必是mFc应用程序,只要是能调用类c函数的应用程序就可以,它们可以是在Visualc++、dephi、Visualbasic、borlandc等编译环境下利用dll开发应用程序。
常规dll又可细分成静态链接到mFc和动态链接到mFc上的,这两种常规dll的区别将在下面介绍。
与常规dll相比,使用扩展dll用于导出增强mFc基础类的函数或子类,用这种类型的动态链接库,可以用来输出一个从mFc所继承下来的类。
扩展dll是使用mFc的动态链接版本所创建的,并且它只被用mFc类库所编写的应用程序所调用。
例如你已经创建了一个从mFc的ctoolbar类的派生类用于创建一个新的工具栏,为了导出这个类,你必须把它放到一个mFc扩展的dll中。
扩展dll和常规dll不一样,它没有一个从cwinapp继承而来的类的对象,所以,开发人员必须在dll中的dllmain函数添加初始化代码和结束代码。
三、动态链接库的创建
在Visualc++6.0开发环境下,打开Filenewproject选项,可以选择win32
dynamic-linklibrary或mFcappwizard[dll]来以不同的方式来创建non-mFcdll、Regulardll、extensiondll等不同种类的动态链接库。
1.win32dynamic-linklibrary方式创建non-mFcdll动态链接库
每一个dll必须有一个入口点,这就象我们用c编写的应用程序一样,必须有一个
winmain函数一样。
在non-mFcdll中dllmain是一个缺省的入口函数,你不需要编写自己的dll入口函数,用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。
如果应用程序的dll需要分配额外的内存或资源时,或者说需要对每个进程或线程初始化和清除操作时,需要在相应的dll工程的.cpp文件中对dllmain()函数按照下面的格式书写。
boolapientRydllmain(handlehmodule,dwoRdul_reason_for_call,lpVoidlpReserved)
{
switch(ul_reason_for_call)
{
casedll_pRocess_attach:
.......
casedll_thRead_attach:
.......
casedll_thRead_detach:
.......
casedll_pRocess_detach:
.......
}
returntRue;
}
参数中,hmoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_dgRoup段的一个选择符);ul_reason_for_call是一个说明动态库被调原因的标志,当进程或线程装入或卸载动态链接库的时候,操作系统调用入口函数,并说明动态链接库被调用的原因,它所有的可能值为:
dll_pRocess_attach:
进程被调用、dll_thRead_attach:
线程被调用、dll_pRocess_detach:
进程被停止、dll_thRead_detach:
线程被停止;lpReserved为保留参数。
到此为止,dll的入口函数已经写了,剩下部分的实现也不难,你可以在dll工程中加入你所想要输出的函数或变量了。
我们已经知道dll是包含若干个函数的库文件,应用程序使用dll中的函数之前,应该先导出这些函数,以便供给应用程序使用。
要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建dll文件时使用模块定义文件.def。
需要读者注意的是在使用第一种方法的时候,不能使用deF文件。
下面通过两个例子来说明如何使用这两种方法创建dll文件。
1)使用导出函数关键字_declspec(dllexport)创建mydll.dll,该动态链接库中有两个函数,分别用来实现得到两个数的最大和最小数。
在mydll.h和mydll.cpp文件中分别输入如下原代码:
//mydll.h
extern"c"_declspec(dllexport)intmax(inta,intb);
extern"c"_declspec(dllexport)intmin(inta,intb);
//mydll.cpp
#include
#include"mydll.h"
intmax(inta,intb)
{
if(a>=b)returna;
else
returnb;
}
intmin(inta,intb)
{
if(a>=b)returnb;
else
returna;
}
该动态链接库编译成功后,打开mydll工程中的debug目录,可以看到mydll.dll、
mydll.lib两个文件。
lib文件中包含dll文件名和dll文件中的函数名等,该lib文件只是对应该dll文件的"映像文件",与dll文件中,lib文件的长度要小的多,在进行隐式链接dll时要用到它。
读者可能已经注意到在mydll.h中有关键字"externc",它可以使其他编程语言访问你编写的dll中的函数。
2)用.def文件创建工程mydll
为了用.def文件创建dll,请先删除上个例子创建的工程中的mydll.h文件,保留
mydll.cpp并在该文件头删除#includemydll.h语句,同时往该工程中加入一个文本文件,命名为mydll.def,再在该文件中加入如下代码:
libRaRymydll
expoRts
max
min
其中libRaRy语句说明该def文件是属于相应dll的,expoRts语句下列出要导出的函数名称。
我们可以在.def文件中的导出函数后加@n,如max@1,min@2,表示要导出的函数顺序号,在进行显式连时可以用到它。
该dll编译成功后,打开工程中的debug目录,同样也会看到mydll.dll和mydll.lib文件。
2.mFcappwizard[dll]方式生成常规/扩展dll
在mFcappwizard[dll]下生成dll文件又有三种方式,在创建dll是,要根据实际情况选择创建dll的方式。
一种是常规dll静态链接到mFc,另一种是常规dll动态链接到mFc。
两者的区别是:
前者使用的是mFc的静态链接库,生成的dll文件长度大,一般不使用这种方式,后者使用mFc的动态链接库,生成的dll文件长度小;动态链接到mFc的规则dll所有
输出的函数应该以如下语句开始:
aFx_manage_state(afxgetstaticmodulestate())//此语句用来正确地切换mFc模块状态
最后一种是mFc扩展dll,这种dll特点是用来建立mFc的派生类,dll只被用mFc类库所编写的应用程序所调用。
前面我们已经介绍过,extensiondlls和Regulardlls不一样,它没有一个从cwinapp继承而来的类的对象,编译器默认了一个dll入口函数dllmain()作为对dll的初始化,你可以在此函数中实现初始化,代码如下:
boolwinapiapientRydllmain(hinstancehinstdll,dwoRdreason,lpVoidflmpload)
{
switch(reason)
{
……………//初始化代码;
}
returntrue;
}
参数hinstdll存放dll的句柄,参数reason指明调用函数的原因,lpReserved是一个被系统所保留的参数。
对于隐式链接是一个非零值,对于显式链接值是零。
在mFc下建立dll文件,会自动生成def文件框架,其它与建立传统的non-mFcdll没有什么区别,只要在相应的头文件写入关键字_declspec(dllexport)函数类型和函数名等,或在生成的def文件中expoRts下输入函数名就可以了。
需要注意的是在向其它开发人员分发mFc扩展dll时,不要忘记提供描述dll中类的头文件以及相应的.lib文件和dll本身,此后开发人员就能充分利用你开发的扩展dll了。
四、动态链接库dll的链接
应用程序使用dll可以采用两种方式:
一种是隐式链接,另一种是显式链接。
在使用dll之前首先要知道dll中函数的结构信息。
Visualc++6.0在Vcin目录下提供了一个名为dumpbin.exe的小程序,用它可以查看dll文件中的函数结构。
另外,windows系统将遵循下面的搜索顺序来定位dll:
1.包含exe文件的目录,2.进程的(c,,中的模板及函数怎么封装为dll)当前工作目录,3.windows系统目录,4.windows目录,5.列在path环境变量中的一系列目录。
1.隐式链接
隐式链接就是在程序开始执行时就将dll文件加载到应用程序当中。
实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。
下面的例子通过隐式链接调用mydll.dll库中的min函数。
首先生成一个项目为testdll,在dlltest.h、dlltest.cpp文件中分别输入如下代码:
//dlltest.h
#pragmacomment(lib,"mydll.lib")
extern"c"_declspec(dllimport)intmax(inta,intb);
extern"c"_declspec(dllimport)intmin(inta,intb);
//testdll.cpp
#include
#include"dlltest.h"
voidmain()
{inta;
a=min(8,10)
printf("比较的结果为%d",a);
}
在创建dlltest.exe文件之前,要先将mydll.dll和mydll.lib拷贝到当前工程所在的目录下面,也可以拷贝到windows的system目录下。
如果dll使用的是def文件,要删除
testdll.h文件中关键字extern"c"。
testdll.h文件中的关键字progamcommit是要Visualc+的编译器在link时,链接到mydll.lib文件,当然,开发人员也可以不使用#pragmacomment(lib,"mydll.lib")语句,而直接在工程的setting->link页的object/moduls栏填入mydll.lib既可。
2.显式链接
显式链接是应用程序在执行过程中随时可以加载dll文件,也可以随时卸载dll文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。
不过实现显式链接要麻烦一些。
在应用程序中用loadlibrary或mFc提供的afxloadlibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用getprocaddress()获取想要引入的函数。
自此,你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。
在应用程序退出之前,应该用Freelibrary或mFc提供的
afxFreelibrary释放动态链接库。
下面是通过显式链接调用dll中的max函数的例子。
#include
#include
voidmain(void)
{
typedefint(*pmax)(inta,intb);
typedefint(*pmin)(inta,intb);
hinstancehdll;
pmaxmax
hdll=loadlibrary("mydll.dll");//加载动态链接库mydll.dll文件;
max=(pmax)getprocaddress(hdll,"max");
a=max(5,8);
printf("比较的结果为%d",a);
Freelibrary(hdll);//卸载mydll.dll文件;
}
在上例中使用类型定义关键字typedef,定义指向和dll中相同的函数原型指针,然后通过loadlibray()将dll加载到当前的应用程序中并返回当前dll文件的句柄,然后通过getprocaddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用
Freelibrary()卸载dll文件。
在编译程序之前,首先要将dll文件拷贝到工程所在的目录或windows系统目录下。
使用显式链接应用程序编译时不需要使用相应的lib文件。
另外,使用getprocaddress()函数时,可以利用makeintResouRce()函数直接使用dll中函数出现的顺序号,如将getprocaddress(hdll,"min")改为getprocaddress(hdll,makeintResouRce
(2))(函数min()在dll中的顺序号是2),这样调用dll中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。
篇二:
编译Vc++类的动态链接库dll导出类及其中的函数
如果已经写好了一个c++的类,希望把它做成dll动态链接库,这里介绍一种简单的方法。
1、制作dll
利用Vc6新建工程时选择win32dynamic-linklibrary,然后添加头文件和cpp文件。
假设类名为exp,添加头文件exp.h,头文件中声明类的定义,添加exp.cpp,其中是成员函数的具体定义。
与一般写类的定义不同,在exp.h中需要写成
class__declspec(dllexport)exp
{
...
}
从而说明以后从dll要被导出的类是哪一个。
这样编译完就会产生exp.dll和exp.lib两个文件。
2、dll的调用
当已经生成dll后,可以在其它程序中调用dll中的类和成员函数。
方法如下:
a)把exp.dll和exp.lib复制到调用程序的执行路径下,注意不是debug路径下。
b)在project->setting->link里添加exp.lib
c)把exp.h复制到调用程序的执行路径下,将__declspec(dllexport)改成__declspec(dllimport)
这样在主程序中就可以使用exp.h中声明的类和它的成员函数了。
如何在Vc中导出类,这是一个常有人问起的问题,下面我以一个简单的例子来说明这个问题:
首先使用wizard创建一个win32dynamic-linklibrary工程,然后定义一个简单的c++类cindll。
由于该类会被工程之外的文件所引用,所以需要对这个类进行引出。
因为只有引出后所生成的dll中才带有供足够的信息以在连接和运行时被正确引入到进程空间中。
有两种方法可以引出类,使用
__declspec(dllexport)定义和使用定义文件。
下面先讲使用__declspec(dllexport)的方法:
将类定义改为:
class
__declspec(dllexport)cindll就可以了。
(译者:
你也许不相信会有这么简单,我也不相信。
:
-)这样产生的工程在编译时是正确的但是在使用时会产生错误,因为你包含的头文件中也是使用__declspec(dllexport),而使用这个dll的工程中并没有引出这个类,而是需要引入这个类)在使用时需要将类定义改为class__declspec(dllimport)cindll就可以了。
使用定义文件可以有效的避免这个问题,这种方法是利用宏定义在不同的地方产生不同的编译代码:
在头文件中加入如下的代码:
#ifdef_classindll
#defineclassindll_class_decl__declspec(dllexport)
#else
#defineclassindll_class_decl__declspec(dllimport)
#endif
#endif//classindll_h
//将class__declspec(dllexport)cindll改为
classclassindll_class_declcindll
{
cindll();
...
}
在实现这个类的cpp文件的顶部加入#define_classindll语句。
#define_classindll
cindll:
:
cindll()
{
}
...
这样一来在使用这个类时就可以不做任何改动了。
(译者:
这中方法在没有使用mFc时可以使用,如果你使用mFc生成mFcdll那么只要做如下定义就可以了classaFx_ext_classyourclass)
关于dll的函数
动态链接库中定义有两种函数:
导出函数(exportfunction)和内部函数
(internalfunction)。
导出函数可以被其它模块调用,内部函数在定义它们的dll程序内部使用。
输出函数的方法有以下几种:
1、传统的方法在模块定义文件的expoRt部分指定要输入的函数或者变量。
语法格式如下:
entryname[=internalname][@ordinal[noname]][data][pRiVate]其中:
entryname是输出的函数或者数据被引用的名称;
internalname同entryname;
@ordinal表示在输出表中的顺序号(index);
noname仅仅在按顺序号输出时被使用(不使用entryname);
data表示输出的是数据项,使用dll输出数据的程序必须声明该数据项为_declspec(dllimport)。
上述各项中,只有entryname项是必须的,其他可以省略。
对于"c"函数来说,entryname可以等同于函数名;但是对"c++"函数(成员函数、非成员函数)来说,entryname是修饰名。
可以从.map映像文件中得到要输出函数的修饰名,或者使用dumpbin/symbols得到,然后把它们写在.def文件的输出模块。
dumpbin是Vc提供的一个工具。
如果要输出一个"c++"类,则把要输出的数据和成员的修饰名都写入.def模块定义文件。
2、在命令行输出
对链接程序link指定/expoRt命令行参数,输出有关函数。
3、使用mFc提供的修饰符号_declspec(dllexport)
在要输出的函数、类、数据的声明前加上_declspec(dllexport)的修饰符,表示输出。
__declspec(dllexport)在c调用约定、c编译情况下可以去掉输出函数名的下划线前缀。
extern"c"使得在c++中使用c编译方式成为可能。
在"c++"下定义"c"函数,
需要加extern"c"关键词。
用extern"c"来指明该函数使用c编译方式。
输出的"c"函数可以从"c"代码里调用。
例如,在一个c++文件中,有如下函数:
extern"c"{void__declspec(dllexport)__cdecltest(intvar);}其输出函数名为:
testmFc提供了一些宏,就有这样的作用。
aFx_class_impoRt:
__declspec(dllexport)aFx_api_impoRt:
__declspe
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 中的 模板 函数 怎么 封装 dll
![提示](https://static.bingdoc.com/images/bang_tan.gif)