混合版本的MSVCRT

Ear*_*rlz 7 c++ msvcrt visual-c++

所以,我有一个C++库,其中包含MSVCRT的静态链接副本.我希望任何人都可以将我的库与任何版本的MSVC Runtime一起使用.实现这一目标的最佳方法是什么?

事情已经完成,我已经非常小心了.

  1. 内存永远不会通过DLL屏障被释放
  2. 运行时C++对象不会跨越障碍(即向量,地图等)传递,除非它们是在障碍的那一侧创建的.
  3. 障碍之间没有传递文件句柄或资源句柄

然而,我仍然有一些简单的代码导致堆损坏.

我的库中有一个这样的对象:

class Foos
{
public: //There is an Add method, but it's not used, so not relevant here
    DLL_API Foos();
    DLL_API ~Foos();

private:
    std::map<std::wstring, Foo*> map;
};

Foos::~Foos()
{
    // start at the begining and go to the end deleting the data object
    for(std::map<std::wstring, Foo*>::iterator it = map.begin(); it != map.end(); it++)
    {
        delete it->second;
    }
    map.clear();
}
Run Code Online (Sandbox Code Playgroud)

然后我从我的应用程序中使用它,如下所示:

void bar() {
    Foos list;
}
Run Code Online (Sandbox Code Playgroud)

从任何地方调用此函数后,我收到有关堆栈损坏的调试警告.如果我真的让它耗尽,它实际上会破坏堆栈和段错误.

我的调用应用程序是使用Visual Studio 2012平台工具编译的.该库是使用Visual Studio 2010平台工具编译的.

这只是我绝对不应该做的事情,还是我实际上违反了使用多个运行时的规则?

Han*_*ant 9

内存永远不会通过DLL屏障

但是,确实如此.实际上很多次.您的应用程序为类对象创建了存储,在本例中为堆栈.然后传递一个指向库中方法的指针.从构造函数调用开始.该指针是这个库里面的代码.

像这样的场景出了什么问题,它没有创建正确的存储量.你有VS2012编译器来查看你的类声明.它使用st2012 :: map的VS2012实现.你的库是用VS2010编译的,它使用了一个完全不同的std :: map实现.尺寸完全不同.C++ 11带来了巨大的变化.

这只是工作中完全的内存损坏,应用程序中写入堆栈变量的代码将破坏std :: map.反过来说.

在模块边界上公开C++类充满了类似的陷阱.只有在你可以保证所有内容都使用完全相同的编译器版本和完全相同的设置进行编译时才考虑它.没有捷径,你也不能混合使用Debug和Release构建代码.制作库以便不会暴露实现细节当然是可能的,您必须遵守以下规则:

  • 仅使用虚方法公开纯接口,参数类型必须是简单类型或接口指针.
  • 使用类工厂来创建接口实例
  • 使用引用计数进行内存管理,因此它始终是库的发布.
  • 用硬规则来打包包装和调用约定等核心细节.
  • 绝不允许异常跨越模块边界,只使用错误代码.

那时你可以很好地编写COM代码,也就是你在DirectX中看到的样式.