我的大多数C / C ++开发都涉及整体模块文件,并且绝对没有类,因此通常当我需要使用可访问函数制作DLL时,我只是使用标准__declspec(dllexport)指令将其导出。 然后通过LoadLibrary()动态访问它们,或者在编译时使用头文件和lib文件访问它们。
当您要导出整个类(及其所有公共方法和属性)时,该怎么做?
是否可以在运行时动态加载该类?
您将如何使用标头和lib进行编译时链接?
What about late-binding? As in loading
it with LoadLibrary() and
GetProcAddress() ? I'm used being able
to load the library at run time and it
would be great if you could do that
here.
因此,有两种加载DLL的方法。第一种是引用DLL中的一个或多个符号(例如,您的类名),提供适当的import .LIB,然后让链接程序找出所有内容。
第二个是通过LoadLibrary显式加载DLL。
两种方法都适用于C级函数导出。您可以让链接程序处理它,也可以按照您的说明调用GetProcAddress。
但是,对于导出的类,通常仅使用第一种方法,即隐式链接到DLL。在这种情况下,DLL是在应用程序启动时加载的,如果找不到DLL,则应用程序将无法加载。
如果要链接到DLL中定义的类,并且希望在程序启动后的某个时间动态加载该DLL,则有两个选择:
使用特殊的工厂函数创建该类的对象,该函数在内部必须使用(一小部分)汇编程序将新创建的对象"连接"到其适当的偏移量。显然,这必须在DLL加载后的运行时完成。在这里可以找到有关此方法的很好的解释。
使用延迟加载DLL。
考虑到所有问题,最好只使用隐式链接,在这种情况下,您肯定要使用上面显示的预处理器技术。实际上,如果您在Visual Studio中创建一个新的DLL并选择"导出符号"选项,则会为您创建这些宏。
祝好运...
构建DLL和将使用DLL的模块时,请使用某种#define来区分彼此,然后可以在类头文件中执行以下操作:
1 2 3 4 5 6 7 8
| #if defined( BUILD_DLL )
#define IMPORT_EXPORT __declspec(dllexport)
#else
#define IMPORT_EXPORT __declspec(dllimport)
#endif
class IMPORT_EXPORT MyClass {
...
}; |
编辑:crashmstr击败了我!
添加一个简单的工作示例以从DLL导出C ++类:
下面给出的示例仅简要概述了dll和exe如何相互交互(自我说明),但是还需要添加更多内容才能更改为生产代码。
完整的示例示例分为两部分
A.创建一个.dll库(MyDLL.dll)
B.创建一个使用.dll库的应用程序(Application)。
.dll项目文件(MyDLL.dll):
1. dllHeader.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #ifdef MYDLL_EXPORTS
#define DLLCALL __declspec(dllexport) /* Should be enabled before compiling
.dll project for creating .dll*/
#else
#define DLLCALL __declspec(dllimport) /* Should be enabled in Application side
for using already created .dll*/
#endif
// Interface Class
class ImyMath {
public:
virtual ~ImyMath() {;}
virtual int Add(int a, int b) = 0;
virtual int Subtract(int a, int b) = 0;
};
// Concrete Class
class MyMath: public ImyMath {
public:
MyMath() {}
int Add(int a, int b);
int Subtract(int a, int b);
int a,b;
};
// Factory function that will return the new object instance. (Only function
// should be declared with DLLCALL)
extern"C" /*Important for avoiding Name decoration*/
{
DLLCALL ImyMath* _cdecl CreateMathObject();
};
// Function Pointer Declaration of CreateMathObject() [Entry Point Function]
typedef ImyMath* (*CREATE_MATH) (); |
2. dllSrc.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include"dllHeader.h"
// Create Object
DLLCALL ImyMath* _cdecl CreateMathObject() {
return new MyMath();
}
int MyMath::Add(int a, int b) {
return a+b;
}
int MyMath::Subtract(int a, int b) {
return a-b;
} |
B.加载并链接已创建的.dll文件的应用程序项目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream>
#include <windows.h>
#include"dllHeader.h"
int main()
{
HINSTANCE hDLL = LoadLibrary(L"MyDLL.dll"); // L".\\Debug\\MyDLL.dll"
if (hDLL == NULL) {
std::cout <<"Failed to load library.\
";
}
else {
CREATE_MATH pEntryFunction = (CREATE_MATH)GetProcAddress(hDLL,"CreateMathObject");
ImyMath* pMath = pEntryFunction();
if (pMath) {
std::cout <<"10+10=" << pMath->Add(10, 10) << std::endl;
std::cout <<"50-10=" << pMath->Subtract(50, 10) << std::endl;
}
FreeLibrary(hDLL);
}
std::cin.get();
return 0;
} |
我使用一些宏来标记要导入或导出的代码
1 2 3 4 5 6 7
| #ifdef ISDLL
#define DLL __declspec(dllexport)
#endif
#ifdef USEDLL
#define DLL __declspec(dllimport)
#endif |
然后在头文件中声明该类:
1
| class DLL MyClassToExport { ... } |
然后在库中使用#define ISDLL,在要使用该类的位置包含头文件之前,先USEDLL。
我不知道您是否可能需要采取其他措施来处理LoadLibrary
最近,我问自己完全相同的问题,并在博客文章中总结了我的发现。您可能会发现它很有用。
它涵盖了从DLL导出C ++类以及使用LoadLibrary动态加载它们的方法,并讨论了与此相关的一些问题,例如内存管理,名称处理和调用约定。
如果愿意将vtable放在要导出的类中,则可以导出一个函数,该函数返回一个接口并在.dll中实现该类,然后将其放入.def文件中。您可能需要做一些声明技巧,但这并不难。
就像COM。 :)