【问题标题】:Exporting a C++ class from a DLL从 DLL 导出 C++ 类
【发布时间】:2010-09-06 21:08:20
【问题描述】:

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

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

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

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

【问题讨论】:

标签: c++ windows dll


【解决方案1】:

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

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

然后在头文件中声明类:

类 DLL MyClassToExport { ... }

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

我不知道您是否需要为使用 LoadLibrary 做一些不同的事情

【讨论】:

    【解决方案2】:

    当你构建 DLL 和将使用 DLL 的模块时,有某种#define 可以用来区分一个和另一个,然后你可以在你的类头文件中做这样的事情:

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

    编辑:crashmstr 打败了我!

    【讨论】:

    • 如何处理损坏的类和构造函数(等)名称先生。?
    • 就是这样——你不需要处理混乱的名字。 __declspec(dllimport) 告诉编译器/链接器您正在导入它并且它做了正确的事情。
    • this 或 crashmstr 的回复都与原始问题无关。 null 的问题也没有得到妥善解决。
    • @AudioGL 你在说什么?如果包含定义类的头文件__declspec(dllimport),然后将exe与导入库链接(生成DLL),生成的可执行文件会在运行时自动加载DLL并导入类,包括所有方法在上面。损坏的名称和重载的构造函数不是问题,因为它们都由链接器和加载器处理。它可能不像 James Devlin 的那样完整一个答案,但它没有任何问题,它确实解决了最初的问题。
    • 抱歉 - 我推测 Adam 可能一直在处理编译器不兼容问题。如果您尝试在 MSVC 和 MinGW 之间导出类,则不能使用此技术,例如,由于不同的名称修改方案。
    【解决方案3】:

    后期绑定呢?和加载一样 它与 LoadLibrary() 和 获取过程地址()?我已经习惯了 在运行时加载库,它 如果你能那样做就好了 在这里。

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

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

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

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

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

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

    2. 使用delay-load DLL

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

    祝你好运……

    【讨论】:

    • 如果类具有虚函数,我不相信您可以将延迟加载与导入的类一起使用,因为这些类的 vtables 将作为数据符号导入。使用延迟加载导入数据符号。
    【解决方案4】:

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

    就像 COM。 :)

    【讨论】:

      【解决方案5】:

      最近我问自己完全相同的问题,并总结了我的发现in a blog post。您可能会发现它很有用。

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

      【讨论】:

        【解决方案6】:

        添加一个从 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;
        }
        

        【讨论】:

        • 谢谢。在此答案中没有接口类方法,我不得不将所有内容都放在 .h 文件中以防止链接器错误。
        • 为什么需要工厂函数?如果没有工厂功能,我该怎么办?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-15
        • 2015-12-21
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多