托管 C++ - 根据配置文件导入不同的 DLL

Posted

技术标签:

【中文标题】托管 C++ - 根据配置文件导入不同的 DLL【英文标题】:Managed C++ - Importing different DLLs based on configuration file 【发布时间】:2009-05-14 18:11:19 【问题描述】:

我目前正在编写一个应用程序,该应用程序将为多个客户端提供类似的用途,但需要对其处理提供的数据的方式进行调整。从本质上讲,它的用途相同,但分发数据的方式完全不同。

所以我决定这样做: -制作通用引擎库,该库将包含所有方式的通用功能并提供默认界面,确保不同引擎将以相同的方式响应。 - 为每一种运行方式编写一个特定的引擎……每一个都编译成自己的.dll。

所以我的项目最终会得到一堆库,其中一些看起来像这样: project_engine_base.dll project_engine_way1.dll project_engine_way2.dll

现在在我们用于用户偏好的配置文件中会有一个引擎部分,以便我们可以决定使用哪个引擎:

[ENGINE]
Way1

所以我们想要在代码中的某个地方做:

If (this->M_ENGINE == "Way1")
  //load dll for way1
Else If (this->M_ENGINE == "Way2")
  //load dll for way2
Else
  //no engines selected...tell user to modify settings and restart application

问题是...我将如何以这种方式导入我的 dll?甚至可能吗?如果没有,我能否获得一些关于如何实现类似功能的建议?

我知道我可以一开始就导入所有 dll,然后选择要使用的引擎,但我的想法是我不想一劳永逸地导入太多引擎并浪费资源,我们没有不想将所有这些 dll 发送给我们的客户。一位客户将使用一种引擎,另一位客户将使用不同的引擎。我们的一些客户可能会使用不止一个,因此我想将其外部化并允许我们的用户使用配置文件进行引擎切换。

有什么想法吗?

编辑: 刚刚意识到,即使我的每个引擎在运行时动态加载并且不是全部在项目中引用时都会呈现相同的界面,我的项目也不会编译。所以我别无选择,只能将它们全部包含在我的项目中,不是吗?

这也意味着它们都必须运送给我的客户。配置中的设置仅由我用来初始化引擎成员的类决定。

我可以将这些引擎中的每一个都编译为相同的名称。在我的主项目中只导入一个 dll,并且将一直使用该特定引擎。这将使我的客户无法将我们的应用程序用于他们自己的多个客户。除非他们愿意手动切换 dll。

有什么建议吗?

编辑#2: 此时看到我的选项,我还可以制作一个包含基本引擎以及所有子引擎和我的配置的大 dll,让用户选择。而不是引用多个 dll 并将它们全部发送。只需拥有一个巨大的并仅运送/参考那个。我也不太喜欢这个,因为这意味着将一个大 dll 发送给我所有的客户,而不仅仅是一两个满足客户需求的小 dll。不过,这仍然是我想出的最佳解决方案。

我仍在寻找更好的建议或回答我原来的问题。 谢谢。

【问题讨论】:

【参考方案1】:

为每个引擎使用单独的 DLL,并在主项目中使用 LoadLibrary 根据配置加载特定引擎。

在某个通用头文件中包含所有引擎都将从中派生的引擎接口,并且该接口也将在您的主项目中使用。

它可能看起来像这样:

// this should be an abstract class
class engine 
public:
     virtual void func1() = 0;
     virtual void func2() = 0;
...
;

在每个不同的引擎实现中,从 DLL 中导出一个函数,如下所示:

// might aswell use auto_ptr here
engine* getEngine()  return new EngineImplementationNumberOne(); 

现在在您的主项目中,只需使用 LoadLibrary 加载您感兴趣的 DLL,然后使用 getEngine 函数 GetProcAddress。

string dllname;

if (this->M_ENGINE == "Way1")
     dllname = "dllname1.dll";         
else if (this->M_ENGINE == "Way2")
     dllname = "dllname2.dll";
else
     throw configuration_error();

HMODULE h = LoadLibraryA(dllname.c_str());
typedef engine* (*TCreateEngine)();

TCreateEngine func = (TCreateEngine)GetProcAddress(h, "getEngine");
engine* e = func();

导出函数的名称可能会被破坏,因此您可以在 DLL 中使用 DEF 文件或 extern "C",也不要忘记检查错误。

【讨论】:

我认为 TCreateEngine 类型定义是错误的(可能是:typedef engine* (*TCreateEngine)(); ??),但答案很好。 +1 哦,在声明和定义函数时不要忘记使用 extern "C",否则编译器会破坏名称。 是的 typedef 错了,我更正了,谢谢。关于外部“C”,我在代码的注释中提到了它,但我会将其移至实际答案,这样会更清楚。 我在托管 C++ 中做所有这些,而不是本机。它的工作方式相同吗? 如果你的类不是托管的 c++ 类,那么这不应该起作用。如果它们是托管类(使用“ref”关键字声明),则可以使用 Assembly 类而不是 LoadLibrary 和 GetProcAddress。【参考方案2】:

我得到的解决方案如下:

Engine_Base^  engine_for_app;
Assembly^ SampleAssembly;
Type^  engineType;

if (this->M_ENGINE == "A")

    SampleAssembly = Assembly::LoadFrom("path\\Engine_A.dll");

    engineType = SampleAssembly->GetType("Engine_A");
    engine_for_app = static_cast<Engine_Base^>(Activator::CreateInstance(engineType, param1, param2));

else

    SampleAssembly = Assembly::LoadFrom("path\\Engine_B.dll");

    engineType = SampleAssembly->GetType("Engine_B");
    engine_for_app = static_cast<Engine_Base^>(Activator::CreateInstance(engineType, param1, param2, param3, param4));

我使用了 Daniel 的答案以及根据他的答案制作的 cmets。经过一些额外的研究,我发现了 LoadFrom 方法。

【讨论】:

以上是关于托管 C++ - 根据配置文件导入不同的 DLL的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C# 导入和使用非托管 C++ 类?

编写用于 C# 的非托管 C++ DLL

C++ 如何导入用 C# 制作的 DLL?

从非托管 C++ 配置 .NET 库

在非托管 C++ DLL 和托管 C# UI 之间发送信息

在非托管 C++ 程序中实现 C# DLL COM 文件