【问题标题】:How to create an object from a DLL file in c++ at runtime?如何在运行时从 C++ 中的 DLL 文件创建对象?
【发布时间】:2020-01-08 08:02:10
【问题描述】:

我有一个 DLL 文件,其中有一个名为 trial 的类,它包含一个名为 test 的函数,我有另一个项目,我在其中使用 windows 模块中的 loadlibrary 函数加载了 DLL,现在我想知道如何创建一个新项目中的类型试验对象。

我尝试将类定义为“class __declspec(dllexport) trial”,但我现在不知道如何在主文件中创建对象。

Trail.h如下:

class __declspec(dllexport) TRIALSHARED_EXPORT Trial
{
public:
    Trial();
    void test();
};

Trail.cpp如下:

extern "C"{
Trial::Trial(){
    cout<<"object is created"<<endl;
}
    void Trial:: test(){
    cout<<"dynamic thingy"<<endl;
}
}

主要功能如下:

int main()
{
    HINSTANCE here=LoadLibrary(L"C:\\Users\\vinay\\Documents\\qt c++\\build-trial-Desktop_Qt_5_12_0_MinGW_64_bit-Debug\\debug\\trial.dll");
    if(!here){
        cout<<"could not load the lib"<<std::endl;
    }
    else{
        cout<<"library loaded"<<endl;
        typedef void(*FNPTR)();
        FNPTR fn=FNPTR(GetProcAddress(here,"Trial"));
        fn();
    }
}

【问题讨论】:

  • 你基本上需要对应的头文件。
  • 你考虑过加载时间绑定吗?这意味着不使用LoadLibrary,而是使用可以在链接DLL 时创建的导出库。与您的main 函数一起显示的程序将与导出库中定义的存根链接。使用加载时绑定,您仍然可以对实现/某些类的 DLL 使用相同的方法。 docs.microsoft.com/en-us/windows/win32/dlls/…
  • @MaxLanghof 我需要在我的主目录中包含 Trial.h 吗?让我创建一个 Trial 类型的对象?
  • @harper 我应该只通过运行时链接来实现目标。有没有办法在运行时绑定中实现这一点?
  • 请不要在 cmets 中扩展您的问题。如果您需要澄清一些要求,例如“仅运行时链接”,这应该是问题的一部分。

标签: c++ class object dll


【解决方案1】:

如果您不能使用加载时绑定,您应该使用带有工厂的模式。工厂创建实现接口的对象的实例。接口是一个抽象类,可以在消费者中使用。

像这样改变你的头文件:

class ITrial
{
public:
    virtual void test() = 0;
};

class Trial : ITrial
{
public:
    Trial();
    virtual void test() override;
};

// declare a function name that can be easily found with GetProcAdress.
extern "C" TRIALSHARED_API ITrial* CreateTrial();
typedef ITrial* (*PFN_Factory)();

编辑: ITrial 类是功能的接口。您在 DLL 的实现和使用者之间定义一个契约。只能访问此处定义的成员。 DLL 定义了一个从接口类继承的类作为实现类。实现类可以根据需要具有任何其他成员。
编辑结束。

头文件不能包含__declspec(dllexport)。宏TRIALSHARED_EXPORT 应该使用条件编译来定义。

#ifdef TRIAL_EXPORTS // defined only in the exporting DLL project
#define TRIALSHARED_API __declspec(dllexport)
#else
#define TRIALSHARED_API __declspec(dllimport)
#endif

添加到您的试用版实施中:

ITrial* CreateTrial()
{
    return new Trial;
}

改变你的主要功能:

int main()
{
    auto here=LoadLibrary(L"C:\\Users\\vinay\\Documents\\qt c++\\build-trial-Desktop_Qt_5_12_0_MinGW_64_bit-Debug\\debug\\trial.dll");
    if(!here) {
        cout<<"could not load the lib"<<std::endl;
        return 1;
    }
    auto factory = reinterpret_cast<PFN_Factory>(GetProcAddress(here, "CreateTrial"));
    if (!factory) {
        cout<<"could not find factory"<<std::endl;
        return 1;
    }
    auto pTrial = factory();
    if (!pTrial) {
        cout<<"factory failed"<<std::endl;
        return 1;
    }
    pTrial->test();
    delete pTrial;
}

错误处理有点笨拙。根据需要进行更改。

【讨论】:

  • 好的,你可以使用工厂方法导出类!但它笨拙、凌乱,而且容易被误用。
  • @Adrian 它还允许新版本向后兼容,并且在扩展它时不必重新编译依赖于库的所有内容。
  • 谢谢!我有点用了类似的方法,并且能够解决问题。
【解决方案2】:

您的代码中存在许多错误,更重要的是,在您采用的方法中。首先,您需要将class Trial 定义为__declspec(dllexport)在构建DLL 时,但定义为__declspec(dllimport)在构建EXE 时!这通常使用预处理器决策来完成,如下所示:

#ifdef _DLL // Assuming a Windows DLL build
#define IMPOREXP __declspec(dllexport)
#else
#define IMPOREXP __declspec(dllimport)
#endif
class IMPOREXP Trial
{
public:
    Trial();
    void test();
};

接下来——也是关键——你不能使用在 DLL 中定义的类,除非你使用隐式的“加载时绑定”! LoadLibrary()GetProcAddress() 函数对于导出/导入的类无任何作用

如果您认为有用,我可以提供更多信息:评论问题或“接受”,我将尝试通过更多示例引导您完成整个过程。

【讨论】:

  • #idef _DLL 仅在头文件未包含在使用 DLL 中时才有效。为了使这个条件编译健壮,您需要检查在实现 DLL 预处理器定义中唯一的定义。
  • @harper Re: '#ifdef _DLL` - 是的,我的代码示例是一个“常规”示例。
猜你喜欢
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多