【问题标题】:Can I switch the Visual C++ runtime to another heap?我可以将 Visual C++ 运行时切换到另一个堆吗?
【发布时间】:2011-01-27 13:57:37
【问题描述】:

我的程序使用了一个内部有大量内存泄漏的第三方动态链接库。我的程序和库都是 Visual C++ 本机代码。两者都动态链接到 Visual C++ 运行时。

我想强制库进入另一个堆,以便在库代码运行时通过 Visual C++ 运行时完成的所有分配都在该堆上完成。我可以打电话给HeapCreate() 和后来的HeapDestroy()。如果我以某种方式确保所有分配都在新堆中完成,我不再关心泄漏 - 当我销毁第二个堆时它们都会消失。

是否可以强制 Visual C++ 运行时在指定堆上进行所有分配?

【问题讨论】:

  • 你是如何链接到图书馆的?
  • @GMan:程序动态链接到库。
  • DLL 如何链接到运行时?
  • @Joe Gauterin:程序和 DLL 都动态链接到 CRT。
  • 它们是否都动态链接到相同版本的 CRT?第 3 方 .dll 是否使用与您构建应用程序相同版本的 MSVC 编译器构建?

标签: c++ visual-c++ memory-management


【解决方案1】:

如果两个二进制文件以相同的方式链接到它,重定向 DLL 的分配是很棘手的。

我能想到的最可靠的方法是将 DLL 移动到一个单独的进程中。对于只使用 IDispatch 接口或提供代理/存根 DLL 的 COM DLL,这相当容易。否则,您将需要编写自定义包装器 - 取决于 DLL 的 API,这需要大量工作或可能存在性能问题。

如果需要保持在进程中,您可以挂钩 CRT 分配,并将库所做的分配重定向到另一个分配器(例如 Win32 堆)。

通过包装对设置全局标志的库的所有调用来做出最安全的决定是我/他们的决定。或者,您可以检查调用堆栈 - 但这并非在所有情况下都有效。对于这两种解决方案,请注意代码中实现但由库调用的回调。

[edit] _CRTSetAllocHook 仅在调试版本中有效。

【讨论】:

  • 挂钩分配是否适用于 CRT 的非调试版本?
  • 你是对的,_CrtSetDebugHook 也只能在调试版本中工作。如果它们也适用于所有分配的发布版本,您可以按照 rep_movsd 的建议检查弯路。否则......快速谷歌不会出现任何结论。也许等待其他回复,或者将其作为新问题打开。
【解决方案2】:

对不起,我的最后一个答案半生不熟,我按下了 Tab 键并输入,却没有记住这是一个文本框而不是编辑器...

无论如何,它是完整的:

你可以使用 detours 库来挂钩分配和释放函数,用你自己的替换它们:

大概是这样的:

//declare a global 
HANDLE g_currentHeap;

LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) 
{ 
    return OriginalHeapAlloc(g_currentHeap, dwFlags, dwBytes);
}


BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
{
    return OriginalHeapFree(g_currentHeap, dwFlags, lpMem);
}

在应用程序加载中

HANDLE g_Heaps[2];

int main()
{
    // Two heaps
    g_Heaps[0] = HeapCreate(...);
    g_Heaps[1] = HeapCreate(...);


    // Do whatevers needed to hook HeapAlloc and HeapFree and any other heap functions 
    // and redirect them to the versions above
    // Save the old function pointers so we can call them
}

然后,每次您从 3rd 方 DLL 调用 API 时,您都可以这样做

void someFn()
{
    g_currentHeap = g_Heaps[1];
    Some3rdPartyAPI();
    g_currentHeap = g_Heaps[0];

    SomeOtherFunction();

}

这应该可以解决您的问题

@peterchen:C++ 运行时为 new 和 malloc() 调用 HeapAlloc,所以这种方法可以工作。事实上,我相信几乎所有语言的运行时都会使用 win32 堆函数,除非有特殊原因不这样做。

【讨论】:

  • 至少在 VC6 中,HeapAlloc 总是在 Debug Builds 中被调用,在 Release Builds 中,一个自定义内存管理器启动了 msot 分配。 我不知道这是否在 VS2005/2008 中发生了变化 - 需要验证。
  • Visual Studio 8 的 CRT 源似乎确实调用了 HeapAlloc() 我在测试程序的发布模式下在 HeapAlloc() 上放置了一个断点......似乎每个 malloc 都调用它() 和新的。
  • Detours 非常有用,但需要小心,特别是如果有多个线程。我在使用它进行调试时遇到了一些非常棘手的问题。如果您选择使用它,最好的办法是在加载任何其他 DLL 或启动除主线程之外的任何线程之前初始化和设置所有绕行的函数。否则,您可能会遇到棘手的情况,即另一个线程可能已经在您试图绕行的函数中。
猜你喜欢
  • 2010-10-01
  • 1970-01-01
  • 2021-04-05
  • 2020-10-22
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
  • 1970-01-01
  • 2014-05-04
相关资源
最近更新 更多