一、dll的不同类型
使用mfc可以生成两种类型的dll:mfc扩展dll和常规dll。常规dll有可以分为动态连接和静态连接。visual c++还可以生成win32 dll,但不是这里讨论的主要对象。
1、mfc扩展dll
每个dll都有某种类型的接口:变量、指针、函数、客户程序访问的类。它们的作用是让客户程序使用dll,mfc扩展dll可以有c++的接口。也就是它可以导出c++类给客户端。导出的函数可以使用c++/mfc数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。同时,在dll中也可以使用dll和mfc。
visual c++使用的mfc类库也是保存在一个dll中,mfc扩展dll动态连接到mfc代码库的dll,客户程序也必须要动态连接到mfc代码库的dll。(这里谈到的两个dll,一个是我们自己编写的dll,一个装mfc类库的dll)现在mfc代码库的dll也存在多个版本,客户程序和扩展dll都必须使用相同版本的mfc代码dll。所以为了让mfc扩展dll能很好的工作,扩展dll和客户程序都必须动态连接到mfc代码库dll。而这个dll必须在客户程序运行的计算机上。
2、常规dll
使用mfc扩展dll的一个问题就是dll仅能和mfc客户程序一起工作,如果需要一个使用更广泛的dll,最好采用常规dll,因为它不受mfc的某些限制。常规dll也有缺点:它不能和客户程序发送指针或mfc派生类和对象的引用。一句话就是常规dll和客户程序的接口不能使用mfc,但在dll和客户程序的内部还是可以使用mfc。
当在常规dll的内部使用mfc代码库的dll时,可以是动态连接/静态连接。如果是动态连接,也就是常规dll需要的mfc代码没有构建到dll中,这种情况有点和扩展dll类似,在dll运行的计算机上必须要mfc代码库的dll。如果是静态连接,常规dll里面已经包含了需要的mfc代码,这样dll的体积将比较大,但它可以在没有mfc代码库dll的计算机上正常运行。
二、建立dll
利用visual c++提供的向导功能可以很容易建立一个不完成任何实质任务的dll,这里就不多讲了,主要的任务是如何给dll添加功能,以及在客户程序中利用这个dll
1、导出类
用向导建立好框架后,就可以添加需要导出类的.cpp .h文件到dll中来,或者用向导创建c++ herder file/c++ source file。为了能导出这个类,在类声明的时候要加“_declspec(dllexport)”,如:
class _declspec(dllexport) cmyclass
{
...//声明
}
如果创建的mfc扩展dll,可以使用宏:afx_ext_class:
class afx_ext_class cmyclass
{
...//声明
}
这样导出类的方法是最简单的,也可以采用.def文件导出,这里暂不详谈。
2、导出变量、常量、对象
很多时候不需要导出一个类,可以让dll导出一个变量、常量、对象,导出它们只需要进行简单的声明:_declspec(dllexport) int myint;
_declspec(dllexport) extern const colorref mycolor=rgb(0,0,0);
_declspec(dllexport) crect rect(10,10,20,20);
要导出一个常量时必须使用关键字extern,否则会发生连接错误。
注意:如果客户程序识别这个类而且有自己的头文件,则只能导出一个类对象。如果在dll中创建一个类,客户程序不使用头文件就无法识别这个类。
当导出一个对象或者变量时,载入dll的每个客户程序都有一个自己的拷贝。也就是如果两个程序使用的是同一个dll,一个应用程序所做的修改不会影响另一个应用程序。
我们在导出的时候只能导出dll中的全局变量或对象,而不能导出局部的变量和对象,因为它们过了作用域也就不存在了,那样dll就不能正常工作。如:
myfunction()
{
_declspec(dllexport) int myint;
_declspec(dllexport) cmyclass object;
}
3、导出函数
导出函数和导出变量/对象类似,只要把_declspec(dllexport)加到函数原型开始的位置:
_declspec(dllexport) int myfunction(int);
如果是常规dll,它将和c写的程序使用,声明方式如下:
extern "c" _declspec(dllexport) int myfunction(int);
实现:
extern "c" _declspec(dllexport) int myfunction(int x)
{
...//操作
}
如果创建的是动态连接到mfc代码库dll的常规dll,则必须插入afx_manage_state作为导出函数的首行,因此定义如下:
extern "c" _declspec(dllexport) int myfunction(int x)
{
afx_manage_state(afxgetstaticmodulestate());
...//操作
}
有时候为了安全起见,在每个常规dll里都加上,也不会有任何问题,只是在静态连接的时候这个宏无效而已。这是导出函数的方法,记住只有mfc扩展dll才能让参数和返回值使用mfc的数据类型。
4、导出指针
导出指针的方式如下:
_declspec(dllexport) int *pint;
_declspec(dllexport) cmyclass object = new cmyclass;
如果声明的时候同时初始化了指针,就需要找到合适的地方类释放指针。在扩展dll中有个函数dllmain()。(注意函数名中的两个l要是小写字母),可以在这个函数中处理指针:
# include "myclass.h"
_declspec(dllexport) cmyclass *pobject = new cmyclass;
dllmain(hinstance hinstance,dword dwreason,lpvoid lpreserved)
{
if(dwreason == dll_process_attach)
{
.....//
}
else if(dwreason == dll_process_detach)
{
delete pobject;
}
}
常规dll有一个从cwinapp派生的类对象处理dll的开和关,可以使用类向导添加initinstance/exitinstance函数。
int cmydllapp::exitinstance()
{
delete pobject;
return cwinapp::exitinstance();
}
三、在客户程序中使用dll
编译一个dll时将创建两个文件.dll文件和.lib文件。首先将这两个文件复制到客户程序项目的文件夹里,这里需要注意dll和客户程序的版本问题,尽量使用相同的版本,都使用release或者都是debug版本。
接着就需要在客户程序中设置lib文件,打开project settings--->link--->object/library modules中输入lib的文件名和路径。如:debug/sampledll.lib。除了dll和lib文件外,客户程序需要针对导出类、函数、对象和变量的头文件,现在进行导入添加的关键字就是:_declspec(dllimport),如:
_declspec(dllimport) int myfunction(int);
_declspec(dllimport) int myint;
_declspec(dllimport) cmyclass object;
extern "c" _declspec(dllimport) int myfunction(int);
在有的时候为了导入类,要把相应类的头文件添加到客户程序中,不同的是要修改类声明的标志:
class _declspec(dllimport) cmyclass,如果创建的是扩展dll,两个位置都是:
class afx_ext_class cmyclass。
文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




