【问题标题】:Crashes after converting C++/Win32 console app to DLL将 C++/Win32 控制台应用程序转换为 DLL 后崩溃
【发布时间】:2011-10-10 19:05:48
【问题描述】:

我最近将一个高度线程化的非托管 Win32 C++ 控制台应用程序 (MediaServer.exe) 转换为非托管 Win32 DLL (MediaServer.dll)。我在一个单独的非托管 Win32 控制台应用程序中托管和调试这个 DLL,一切都编译并运行,但大约一分钟后,我会在一个毫无意义的地方随机崩溃,显然是一个损坏的调用堆。这些崩溃发生在各种不同的地方,而且在某种程度上是随机的:但共同点是(显然是损坏的)调用堆栈上总是有各种 libxml2.dll 函数,例如,崩溃可能出现在看起来像这样:

xmlDoc * document = xmlReadMemory(message.c_str(), message.length(), "noname.xml", NULL, 0);

或者像这样:

xmlBufferPtr buffer = xmlBufferCreate();

调用堆栈可能如下所示:

feeefeee()  
libxml2.dll!000eeec9()  
[Frames below may be incorrect and/or missing, no symbols loaded for libxml2.dll]   
libxml2.dll!00131714()  
libxml2.dll!001466b6()  
libxml2.dll!00146bf9()  
libxml2.dll!00146c3c()  
libxml2.dll!0018419e()  

或者如果你很幸运,像这样:

ntdll.dll!_RtlpWaitOnCriticalSection@8()  + 0x99 bytes  
ntdll.dll!_RtlEnterCriticalSection@4()  - 0x15658 bytes 
libxml2.dll!1004dc6d()  
[Frames below may be incorrect and/or missing, no symbols loaded for libxml2.dll]   
libxml2.dll!10012034()  
libxml2.dll!1004b7f7()  
libxml2.dll!1003904c()  
libxml2.dll!100393a9()  
libxml2.dll!10024621()  
libxml2.dll!10036e8f()  
MediaServer.dll!Controller::parse(std::basic_string<char,std::char_traits<char>,std::allocator<char> > message)  Line 145 + 0x20 bytes  C++
MediaServer.dll!Controller::receiveCommands()  Line 90 + 0x25 bytes C++
MediaServer.dll!MediaServer::processCommands()  Line 88 + 0xb bytes C++
MediaServer.dll!MediaServer::processCommandsFunction(void * mediaServerInstance)  Line 450 + 0x8 bytes  C++
MediaServer.dll!CustomThread::callThreadFunction()  Line 79 + 0x11 bytes    C++
MediaServer.dll!threadFunctionCallback(void * threadInstance)  Line 10 + 0x8 bytes  C++
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes   
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes    

崩溃本身通常会显示类似“MediaServerConsole.exe 中 0x77cd2239 (ntdll.dll) 处的未处理异常:0xC000005:访问冲突写入位置 0x00000014。”

不用说,当我将模块编译为控制台应用程序时,这并没有发生。

在将项目转换为 DLL 时我可能忽略了什么?这不是我以前做过的事情,所以如果我忽略了一些明显的事情,我一点也不感到惊讶。任何帮助表示赞赏。

【问题讨论】:

  • 您是否尝试过运行this topic 中的任何建议程序? Purify、Insure++ 等可以帮助您追踪程序中的细微错误。
  • 没有,但我现在正在下载 Visual Leak Detector,看看它是否可以查明任何东西。 (大概和 Purify 或 Insure++ 不在一个级别,但它是免费的……)
  • 什么原因导致崩溃?访问冲突?我假设是的,因为释放的堆标有 0xFEFEEE。听起来您的堆栈溢出或堆已损坏。另外,您能否检查 DLL 与哪个 CRT 链接(静态/动态、调试/发布)
  • 我的常用清单:选择解决方案中的所有项目并验证:C++->优化:优化、偏好大小或速度以及整个程序优化的相同设置。 C++->代码生成:运行时库(在您的情况下为多线程 DLL)和结构成员对齐的相同设置。
  • 我会使用 Application Verifier 并启用完整的堆检查。像这样的问题总是很难追踪。

标签: c++ dll crash libxml2


【解决方案1】:

我会说您在 DLL_THREAD_ATTACH 而不是 DLL_PROCESS_ATTACH 中初始化内存。这种情况会导致您使用已在执行线程之外的另一个线程中分配的指针或内存。

另一件事是检查您为 DLL 加载的依赖项。

让我解释一下。当您的 DLL 使用 loadlibrary 加载时,CRT 会进行全局内存分配。这是为了初始化所有全局变量,范围从 C 原始类型,将它们初始化为零作为默认值。然后它为结构/类分配内存,并在必要时调用它们的构造函数。

CRT 然后使用 DLL_PROCESS_ATTACH 调用您的 DLLMain 方法,以告知您的进程已加载的 DLL。对于该进程中的每个线程,CRT 然后使用 DLL_THREAD_ATTACH 调用您的 DLL。

你说过这些是空的,然后你调用你导出的 C 函数。尽管我可以看到您的 dll 陷入了关键部分。这告诉我,您的全局分配变量和您的线程在 Start() 中分配内存时发生了死锁情况。

我建议将您的初始化代码移动到 Process_Attached 中,这将确保您的所有内存都分配在主进程线程上,类似于应用程序作为单个可执行文件的工作方式。

【讨论】:

  • 这是一件好事,但我的 DllMain 是(正如 MS 显然建议的那样)一个空存根。我目前只在单个 Start() 函数中分配内存,声明为:extern "C" __declspec (dllexport) int Start(){/* Stuff */ }
  • 是的,但是 CRT 在不同的线程上分配全局内存。这就是我要说的。当两个线程试图访问一个 CriticalSection 时,您似乎遇到了死锁。
  • 很可能我在这里遗漏了一些东西,然后:-)。如果我没有在 DllMain 中初始化任何东西,我如何确保我在 Start() 中初始化的东西被正确初始化/在正确的线程上?
  • 嗯,这似乎已经做到了,尽管我承认我不完全理解为什么。我将我的主类的 initialize() 方法拆分为 initialize() 和 start(),并将 initialize() 的东西移动到 DllMain/DLL_Process_Attached 中,并将 start() 的东西移动到我导出的 Start() 函数中:看起来去工作。希望我能更好地理解为什么,但我想随着时间的推移和更多的砖墙:-)。谢谢!
【解决方案2】:

我会将另一个答案保留为“已接受”的答案,但它可能有助于人们知道问题的关键部分是我在错误的线程上初始化 libxml2 的事实。具体来说,在进行任何调用之前,您需要在主线程上调用 xmlInitParser()。对我来说,这意味着:

MediaServer::MediaServer() : mProvidePolicyThread  (0),
                         mProcessCommandsThread(0),
                         mAcceptMemberThread   (0)
{
   xmlInitParser();
}

同样,您需要在退出时调用 xmlCleanupParser():

MediaServer::~MediaServer()
{
   xmlCleanupParser();
}

这一切都记录在这里:http://xmlsoft.org/threads.html

【讨论】:

    猜你喜欢
    • 2014-09-10
    • 2017-08-02
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多