这是导入类的最简单的方法,你也可以使用.def文件来导入它们,所以我们在这里不讨论它们。
很多时候不需要导入一个类,可以让DLL导入一个变量、常量、对象,导入它们只需要做一个简单的声明:_declspec(dllexport)intMyInt;
_declspec(dllexport)externconstCOLORREFMyColor=RGB(0,0,0);
_declspec(dllexport)CRectrect(10,10,20,20);
导入常量时必须使用关键字extern,否则会出现链接错误。
注意:如果客户端程序识别该类并且有自己的头文件,则只能导入一个类对象。 如果在DLL中创建类,那么如果不使用头文件,客户端程序将很难识别该类。
导入对象或变量时,加载 DLL 的每个客户端程序都有自己的副本。 也就是说,如果两个程序使用相同的 DLL,则一个应用程序所做的更改不会影响另一应用程序。
当我们导入时,只能导入DLL中的全局变量或对象,而不能导入局部变量和对象,因为它们在经过作用域后就不再存在,因此DLL将无法正常工作。 喜欢:
我的函数()
_declspec(dllexport)intMyInt;
_declspec(dllexport)CMyClass对象;
3.导出功能
导入函数与导入变量/对象类似,只需在函数原型的开头添加 _declspec(dllexport) 即可:
_declspec(dllexport)intMyFunction(int);
如果是常规DLL,则与C编写的程序一起使用,声明方法如下:
extern "c"_declspec(dllexport) intMyFunction(int);
完成:
extern "c"_declspec(dllexport) intMyFunction(intx)
...//操作
如果要创建动态链接到 MFC 代码库 DLL 的常规 DLL,则必须插入 AFX_MANAGE_STATE 作为导入函数的第一行,因此定义如下:
extern "c"_declspec(dllexport) intMyFunction(intx)
AFX_MANAGE_STATE(AfxGetStaticModuleState());
...//操作
有时,为了安全起见,将其添加到每个常规DLL中,不会有问题,但静态链接时该宏无效。 这是导入函数的方式,记住只有MFC扩展DLL才能使用MFC数据类型作为参数和返回值。
4.导出指针
导入指针的形式如下:
_declspec(dllexport)int*pint;
_declspec(dllexport)CMyClassobject=newCMyClass;
如果指针在声明的时候就被初始化了,那么就需要找一个合适的地方来释放指针。 扩展DLL中有一个函数DllMain()。 (注意,如果函数名中的两个ls都是大写字母),可以在这个函数中处理指针:
#include“MyClass.h”
_declspec(dllexport)CMyClass*pobject=newCMyClass;
DllMain(HINSTANCEhInstance,DWORDdwReason,LPVOIDlpReserved)
if(dwReason==DLL_PROCESS_ATTACH)
......//
elseif(dwReason==DLL_PROCESS_DETACH)
删除对象;
常规DLL有一个从CWinApp派生的类对象来处理DLL的打开和关闭,并且可以使用类向导添加InitInstance/ExitInstance函数。
intCMyDllApp::ExitInstance()
删除对象;
返回 CWinApp::ExitInstance();
3. 在客户程序中使用DLL
编译DLL将创建两个文件:.dll文件和.lib文件。 首先将这两个文件复制到客户端程序项目的文件夹中,这里需要注意DLL和客户端程序的版本,尽量使用相同的版本,都使用RELEASE或DEBUG版本。
然后需要在客户端程序中设置LIB文件c源码编译dll,打开ProjectSettings--->Link--->Object/libraryModules并输入LIB文件名和路径。 如:Debug/SampleDll.lib。 不仅DLL和LIB文件,客户端程序还需要头文件来导入类、函数、对象和变量。 现在导出添加的关键字为:_declspec(dllimport),如:
_declspec(dllimport)intMyFunction(int);
_declspec(dllimport)intMyInt;
_declspec(dllimport)CMyClass对象;
extern "C"_declspec(dllimport) intMyFunction(int);
有时为了导出一个类,必须在客户端程序中添加相应类的头文件,不同的是类声明的flag需要改变:
class_declspec(dllimport)CMyClass,如果创建扩展 DLL,两个位置都是:
类AFX_EXT_CLASSCMyClass。
使用 DLL 的一个更严重的问题是编译器之间的兼容性问题。 不同的编译器对 C++ 函数在补码级别的实现方法不同。 所以对于基于C++的DLL来说,如果编译器不同的话,就会很麻烦。 如果创建 MFC 扩展 DLL,则不会有问题,因为它只能动态链接到 MFC 客户端应用程序。 这不是本文的重点。
1.重新编译问题
我们先来看一个实际中可能遇到的问题:
例如,现在已经构建了一个DLL并导入到CMyClass类中,客户也可以正常使用这个DLL,假设CMyClass对象的大小是30字节。 如果我们需要将DLL中的CMyClass类更改为具有相同的函数和成员变量,并添加一个私有成员变量int类型,则CMyClass对象的大小现在为34字节。 当客户直接使用这个新的DLL来替换原来的30字节DLL时c源码编译dll,客户应用程序期望的是一个30字节的对象,但现在它是一个34字节的对象。 哎呀,客户程序出了问题。
类似的问题,如果不导入CMyClass类,而是在导入的函数中使用CMyClass,改变对象的大小仍然会出现问题。 这时,改变这个问题的唯一办法就是替换客户端程序中CMyClass的头文件,重新编译整个应用程序,让客户端程序使用大小为34字节的对象。
这是一个严重的问题,有时如果没有客户程序的源代码,那么我们就无法使用这个新的DLL。
二、解决办法
为了防止客户端程序重新编译,这里有两种方法: (1)使用socket类。 (2) 使用创建和销毁类的静态函数。
1.使用socket类
socket类是创建第二个类,作为socket导入类,这样当导入的类改变时,不需要重新编译客户端程序,因为socket类没有改变。
假设导入的CMyClass类有两个函数FunctionAFunctionB。 现在创建一个套接字类CMyInterface,以下是DLL中CMyInterface类的头文件的代码:
#include“MyClass.h”
class_declspec(dllexport)CMyInterface
CMyClass*pmyclass;
CMyInterface();
〜CMyInterface();
民众:
intFunctionA(int);
intFunctionB(int);
};
客户端程序中的头文件略有不同,并且不需要 INCLUDE 语句,因为客户端程序没有它的副本。 相反,使用 CMyClass 的前向声明,它无需头文件即可编译:
class_declspec(dllexport)CMyInterface
classCMyClass;//声明转发
CMyClass*pmyclass;
CMyInterface();
〜CMyInterface();
民众:
intFunctionA(int);
intFunctionB(int);
};
CMyInterface在DLL中的实现如下:
CMyInterface::CMyInterface()
pmyclass=newCMyClass();
CMyInterface::~CMyInterface()
删除我的类;
intCMyInterface::FunctionA()
returnpmyclass->FunctionA();
intCMyInterface::FunctionB()
returnpmyclass->FunctionB();
……
对于导入类 CMyClass 的每个成员函数,CMyInterface 类都提供了自己对应的函数。 客户端程序与CMyClass没有任何联系,所以随意改变CMyClass不会有问题,因为CMyInterface类的大小没有改变。 虽然向CMyInterface类添加函数以便能够访问CMyClass中的新变量并不是问题。
然而,这些技术也存在明显的问题。 导入类的每个函数和成员变量都必须相应实现,有时socket类会很大。 同时,减少了客户端程序调用所需的时间。 减少了程序开销。
2.使用静态函数
您还可以使用静态函数来创建和销毁类对象。 创建导入类时,减少两个静态公共函数CreateMe()/DestroyMe(),头文件如下:
class_declspec(dllexport)CMyClass
CMyClass();
~CMyClass();
民众:
staticCMyClass*CreateMe();
静态 voidDestroyMe(CMyClass*ptr);
};
实现函数为:
CMyClass*CMyClass::CMyClass()
返回新CMyClass;
voidCMyClass::DestroyMe(CMyClass*ptr)
删除;
然后像其他类一样导入CMyClass类。 这时,在客户端程序中使用该类的方式就略有不同。 例如,如果您创建一个 CMyClass 对象,它应该是:
CMyClassx;
CMyClass*ptr=CMyClass::CreateMe();
使用后删除:
CMyClass::DestroyMe(ptr);
- - - - - - - - - - - - - - - - - - - - - - - -结束 - -----------------------------------------------------------