混合版本的MSVCRT

时间:2013-11-14 15:41:20

标签: c++ visual-c++ msvcrt

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

我已经非常小心如何完成任务。

  1. 内存永远不会通过DLL屏障来释放
  2. 运行时C ++对象不会跨越障碍(即向量,地图等......),除非它们是在障碍的那一侧创建的。
  3. 没有文件句柄或资源句柄在障碍之间传递
  4. 然而,我仍然有一些简单的代码导致堆损坏。

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

    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();
    }
    

    然后我在我的应用程序中使用它,如:

    void bar() {
        Foos list;
    }
    

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

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

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

4 个答案:

答案 0 :(得分:9)

  

内存永远不会通过DLL屏障

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

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

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

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

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

那时你可以顺利编写COM代码,也就是你在DirectX中看到的样式。

答案 1 :(得分:3)

map成员变量仍由应用程序创建,其中一些内部数据由应用程序而不是DLL分配(并且它们可能使用map的不同实现)。根据经验,不要使用DLL中的堆栈对象,在DLL中添加类似Foos * CreateFoos()的内容。

答案 2 :(得分:3)

  

运行时C ++对象不会跨越障碍(即矢量,地图,   等等......除非它们是在障碍的那一边创造的)

你正是这样做的。您的Foos对象由堆栈中的主程序创建,然后在库中使用。该对象包含一个地图作为其中的一部分...

编译主程序时,它会查看头文件等,以确定为Foos对象分配多少堆栈空间。并调用库中定义的构造函数...可能期望对象的布局/大小完全不同

答案 3 :(得分:0)

它可能不符合您的需求,但不要忘记在头文件中实现整个过程可以简化问题(某种程度): - )