我能把Visual C++运行时切换到另一个堆吗?

6
我的程序使用了一个第三方动态链接库,该库内部存在巨大的内存泄漏问题。我的程序和这个库都是Visual C++本地代码。两者都动态链接到Visual C++运行时。
我想将该库强制转换到另一个堆中,以便在库代码运行时通过Visual C++运行时进行的所有分配都在该堆上完成。我可以调用HeapCreate(),然后再调用HeapDestroy()。如果我确保所有分配都在新堆上完成,那么我就不需要关心泄漏问题了——当我销毁第二个堆时,它们全部消失。
是否有可能强制Visual C++运行时将所有分配都放在指定的堆中?

你是如何链接到这个库的? - GManNickG
@GMan:该程序动态链接到库。 - sharptooth
DLL如何链接到运行时? - JoeG
@Joe Gauterin:该程序和DLL都动态链接到CRT。 - sharptooth
它们是否都动态链接到同一版本的CRT?第三方.dll文件是否是使用与构建应用程序所使用的MSVC编译器相同的版本进行构建的? - JoeG
显示剩余2条评论
2个回答

3
抱歉,我的上一个答案发布得不完整,我按下了Tab和Enter键,没有记住这是一个文本框而不是编辑器...

无论如何,以下是完整的内容:

您可以使用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
}

每次从第三方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中,大多数分配使用自定义内存管理器。我不知道这在VS2005/2008中是否有所改变 - 需要进行验证。 - peterchen
Visual Studio 8的CRT源代码似乎调用了HeapAlloc()。我在测试程序的发布模式下对HeapAlloc()设置了断点...似乎每次malloc()和new都会调用它。 - rep_movsd
Detours非常有用,但需要小心,特别是如果有多个线程。我在使用它进行调试时遇到了一些非常棘手的问题。如果您选择使用它,最好在加载任何其他DLL或启动除主线程之外的任何线程之前初始化和设置所有的detour-ed函数。否则,您可能会遇到棘手的情况,其中另一个线程可能已经进入您正在尝试转向的函数。 - cpalmer

0

如果两个二进制文件链接方式相同,仅重定向 DLL 的分配可能是最棘手的。

我能想到最健壮的方法是将 DLL 移动到单独的进程中。这对于仅使用 IDispatch 接口或提供代理/存根 DLL 的 COM DLL 来说相当容易。否则,您需要编写一个自定义包装器 - 根据 DLL 的 API,这可能是很多工作或可能导致性能问题。

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

“我的/他们的”决定最安全的方法是包装所有调用设置全局标志的库的调用。或者,您可以检查调用堆栈 - 但这并不适用于所有情况。对于两种解决方案,请注意回调实现在您的代码中,但由库调用的问题。

[编辑] _CRTSetAllocHook 仅在调试版中有效。


挂钩分配功能对于非调试版本的CRT是否有效? - sharptooth
你是对的,_CrtSetDebugHook只在调试版本中工作。你可以像rep_movsd建议的那样尝试使用detours,看它们是否也适用于所有分配的发布版本。否则……快速谷歌搜索没有得出什么确定性的答案。也许等待其他回复,或将其作为一个新问题打开。 - peterchen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接