关于Windows:从DLL导出C ++类

关于Windows:从DLL导出C ++类

Exporting a C++ class from a DLL

我的大多数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。 :)


    推荐阅读