28

我的大部分 C/C++ 开发都涉及单片模块文件并且绝对没有任何类,所以通常当我需要创建一个具有可访问函数的DLL__declspec(dllexport)时,我只需使用标准指令将它们导出。然后通过LoadLibrary()头文件和 lib 文件或在编译时动态访问它们。

当你想导出整个类(以及它的所有公共方法和属性)时,你如何做到这一点?

是否可以在运行时动态加载该类,如果可以,如何加载?

您将如何使用标头和库进行编译时链接?

4

6 回答 6

17

当您构建 DLL 和将使用 DLL 的模块时,有某种 #define 可以用来区分一个和另一个,然后您可以在类头文件中执行以下操作:

#if defined( BUILD_DLL )
    #define IMPORT_EXPORT __declspec(dllexport)
#else
    #define IMPORT_EXPORT __declspec(dllimport)
#endif
class IMPORT_EXPORT MyClass {
    ...
};

编辑: crashmstr 打败了我!

于 2008-08-26T13:26:55.560 回答
17

后期绑定呢?就像用 LoadLibrary() 和 GetProcAddress() 加载它一样?我习惯于在运行时加载库,如果你能在这里做到这一点,那就太好了。

所以有两种加载DLL的方法。第一个是从 DLL 中引用一个或多个符号(例如您的类名),提供适当的导入 .LIB 并让链接器解决所有问题。

第二种是通过 LoadLibrary 显式加载 DLL。

这两种方法都适用于 C 级函数导出。您可以让链接器处理它,也可以按照您的说明调用 GetProcAddress。

但是当涉及到导出时,通常只使用第一种方法,即隐式链接到 DLL。在这种情况下,DLL 在应用程序启动时加载,如果找不到 DLL,应用程序将无法加载。

如果要链接到 DLL 中定义的类,并且希望动态加载该 DLL,则在程序启动后的某个时间,您有两种选择:

  1. 使用特殊的工厂函数创建类的对象,该函数在内部必须使用(一点点)汇编程序将新创建的对象“连接”到它们适当的偏移量。显然,这必须在加载 DLL 之后在运行时完成。可以在这里找到对这种方法的一个很好的解释。

  2. 使用延迟加载 DLL

考虑到所有事情......可能最好只使用隐式链接,在这种情况下,您肯定希望使用上面显示的预处理器技术。事实上,如果您在 Visual Studio 中创建一个新的 DLL 并选择“导出符号”选项,这些宏将为您创建。

祝你好运...

于 2008-08-26T15:07:29.330 回答
13

添加一个简单的工作示例,用于从 DLL 导出 C++ 类:

下面给出的示例仅简要概述了 dll 和 exe 如何相互交互(自我解释),但它需要添加更多内容才能更改为生产代码。

完整示例示例分为两部分

A. 创建 .dll 库 (MyDLL.dll)

B. 创建一个使用 .dll 库的应用程序(应用程序)。

A. .dll 项目文件(MyDLL.dll):

1.dllHeader.h

#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

#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 文件的应用程序项目:

 #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.\n";
    }
    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;
}
于 2014-06-24T10:48:41.920 回答
12

我使用一些宏来标记导入或导出的代码

#ifdef ISDLL
#define DLL __declspec(dllexport)
#万一

#ifdef USEDLL
#define DLL __declspec(dllimport)
#万一

然后在头文件中声明该类:

类 DLL MyClassToExport { ... }

然后#define ISDLL在库中,并USEDLL在您要使用该类的地方包含头文件之前。

我不知道你是否可能需要做一些不同的事情来处理LoadLibrary

于 2008-08-26T13:26:01.133 回答
7

最近我问自己完全相同的问题,并在一篇博文中总结了我的发现。您可能会发现它很有用。

它涵盖了从 DLL 导出 C++ 类,以及使用 动态加载它们LoadLibrary,并讨论了一些与此相关的问题,例如内存管理、名称修改和调用约定。

于 2011-09-17T03:22:02.933 回答
0

如果您愿意在要导出的类中放置一个 vtable,您可以导出一个返回接口的函数并在 .dll 中实现该类,然后将其放入 .def 文件中。您可能需要做一些声明技巧,但这不应该太难。

就像 COM 一样。:)

于 2008-08-27T13:50:57.537 回答