如何在C ++中构build运行时版本不可知的DLL?

我的产品是一个C ++库,在Windows上,它是作为一个DLL分发的。 它几乎不使用c运行时(基本的iostream,就是这样),所以我相信所有最新版本的CRT都可以。

由于我的客户应该使用我的dll来构build他的应用程序,所以我不想强加给他任何特定的运行时版本。 我想我的DLL绑定到我的客户端的应用程序正在使用的任何运行时库版本(我可以假设他将使用他的CRTdynamic链接)。 毕竟,这不是什么dynamic链接? 那可能吗?

编辑:链接DLL对静态运行库也将无法正常工作,因为那么静态运行时(从DLL)和dynamic运行时(从客户端的应用程序)将被混合,这是不好的。

编辑:我主要问的是我如何告诉运行时加载器链接我的dll与应用程序链接的任何CRT? 可能是与清单的东西? 更一般地说,我的问题是如何build立一个很好的行为的DLL,这是由客户使用build立他们自己的应用程序?

编辑:感谢在答案中的build议,我已经将所有对std类的引用转换为标题中的内联函数,并将我的dll与静态运行时库链接起来。 现在,即使在与不同CRT版本连接的应用程序中,它似乎也能工作

有没有真正的方法来确保您的DLL工作与多个运行时 – 任何类型之间的变化可能导致不兼容。 例如,一个对象的大小可以改变,或者它们中的成员的位置。 这种事情在C ++中有很小的空间。

您可以做的最好的事情是静态链接到运行时,并确保导出的API严格限于严格在您的控制下的类型 – 不将std::string传递给函数,stdlib类型不作为成员,并且不是new的DLL并在另一个delete 。 不要为同一对象混用内联和导出的函数(包括构造函数/析构函数),因为编译器之间的成员顺序和填充可能会改变。 pimpl成语可能在这里帮助。

如果你跨DLL边界公开任何C ++对象,那么这是不可能的。 你可以做什么(我们使用第三方DLL来做到这一点)是以多种配置(32位/ 64位,调试/发布,静态/动态运行时,静态/动态库)来构建你的库,以满足尽可能多的人。 起初这可能有点繁琐,但是一旦你完成了所有的配置设置,只需要把它们全部构建起来。 当然你也需要考虑你正在构建哪个运行时(vc8,vc9,vc10等),所以如果你想覆盖所有的基础,你可以有很多的配置。

链接你的DLL对静态运行库应该工作,除了你必须非常小心内存管理(例如,谁调用你的DLL无法释放()或删除[]任何东西分配的DLL),你不能交换标准的C数据结构(例如FILE *)。 (我错过了什么?)

您可以通过使用WinAPI调用I / O和其他任何可能依赖运行时的调用来实现此目的。

最痛苦的部分是你可能必须重写全局newdelete来独占使用WinAPI函数,因为它们很可能在内部使用malloc / free。 还有很多其他痛苦的方面,我的意见是,这是不值得的麻烦。 这里是一篇关于这个话题的文章。

如果你想暴露你的对象以运行时为中性的方式,那么我不能看到COM以外的任何解决方案。

那么,C运行时和C ++运行时就有很大的区别。 如果你在哪里使用msvcrt.dll,近年来被视为一个真正的系统DLL的“骑士”,你可以依靠它的存在,在XP以上(虽然对于Windows 2000,你需要一些可再发行的版本6 msvcrt.dll )。 您可以使用msvcrt.dll通过编译您的代码与最新的WDK(Windows驱动程序工具包)的编译器。 即使这是用户模式的代码,这是编译它的一个可行且好的方法。

然而IOStreams需要C ++运行时。 这使事情变得复杂了很多。

编辑:链接DLL对静态运行库也将无法正常工作,因为那么静态运行时(从DLL)和动态运行时(从客户端的应用程序)将被混合,这是不好的。

那么,如果你以这种方式混合使用代码,那么你的设计就有问题了。 使用其他代码的发行版本运行DLL的调试版本时,会出现类似的问题,反之亦然。

我只能建议你直接使用COM,或者 – 如果这个太大了 – 尝试模仿COM的一些想法。 最重要的是你有一个工厂函数,并且在这两段代码之间(即DLL和它的调用者)声明了一个(类)接口声明(并且从未改变过)。 工厂函数将返回类的一个实例,类将自己管理其生命周期(这意味着所有分配和释放代码将驻留在相同的实体,即您的DLL)。 终身管理将通过addrefrelease成员函数暴露。 IUnknown可以作为你的界面的基础,而不依赖于实际COM的其他部分。

编辑:我主要问的是我如何告诉运行时加载器链接我的dll与应用程序链接的任何CRT? 可能是与清单的东西? 更一般地说,我的问题是如何建立一个很好的行为的DLL,这是由客户使用建立他们自己的应用程序?

根本不容易。 即使你已经安装了所有版本的VS,你也不得不摆脱这种困境,选择正确的版本。

你的DLL链接到它编译的C运行时。 您的应用程序将始终使用此运行时。 任何链接到你的dll的人使用他们的c-runtime。 所以这个不会有什么问题

如果你使用C ++,那么跨越运行时间边界似乎是不可能的,除非你限制了可以暴露的东西。 如前所述,std :: objects不起作用(例如std :: string)。

这是一个会导致崩溃的小例子:

一流的基地
{
上市:
虚拟〜Base()
{
}
};

class ClassInDll:public Base
{public:__declspec(dllexport)ClassInDll(int arg);
__declspec(dllexport)〜ClassInDll();

private:int _arg;
};

如果将此类编译为VS2008发行版模式DLL,并且在VS2008调试模式下构建.exe,请执行以下操作:

ClassInDll * c = new ClassInDll(1); 删除c;

“delete c”语句导致崩溃。 这与ClassInDll具有虚拟析构函数的事实有关。