Сбои после преобразования консольного приложения C ++ / Win32 в DLL - PullRequest
1 голос
/ 22 июля 2011

Недавно я преобразовал неуправляемое консольное приложение Win32 C ++ (MediaServer.exe) с высокой поточностью в неуправляемую DLL-библиотеку Win32 (MediaServer.dll). Я размещаю и отлаживаю эту DLL в отдельном неуправляемом консольном приложении Win32, и все компилируется и запускается, но примерно через минуту я получаю случайный сбой, в месте, которое не имеет смысла, с явно поврежденным вызовом стек. Эти сбои происходят в разных местах и ​​в несколько случайное время: но общность заключается в том, что (очевидно, поврежденный) стек вызовов всегда имеет где-то различные функции 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    

Сам сбой, как правило, говорит что-то вроде «Необработанное исключение в 0x77cd2239 (ntdll.dll) в MediaServerConsole.exe: 0xC000005: Место записи нарушения доступа 0x00000014.»

Излишне говорить, что этого не произошло, когда я компилировал модуль как консольное приложение.

Есть ли что-то, что я мог упустить из виду при преобразовании проекта в DLL? Это не то, что я делал раньше, так что я бы не удивился, если бы что-то очевидное я пренебрег. Любая помощь приветствуется.

Ответы [ 2 ]

1 голос
/ 22 июля 2011

Я бы сказал, что вы инициализируете память в DLL_THREAD_ATTACH вместо DLL_PROCESS_ATTACH. Ситуация может привести к тому, что вы будете использовать указатель или память, выделенную в другом потоке, отличном от выполняющего потока.

Другое дело, чтобы проверить загрузку зависимостей для DLL.

Позвольте мне объяснить. CRT выполняет глобальное распределение памяти, когда ваша DLL загружается с loadlibrary. Это делается для инициализации всех глобальных переменных в диапазоне от примитивных типов C, которые по умолчанию инициализируют их до нуля. Затем он выделяет память для структуры / классов и при необходимости вызывает их конструкторы.

Затем CRT вызывает ваш метод DLLMain с помощью DLL_PROCESS_ATTACH, чтобы сообщить DLL, которая была загружена вашим процессом. Для каждого потока внутри этого процесса CRT затем вызывает вашу DLL с DLL_THREAD_ATTACH.

Вы сказали, что они оставлены пустыми, а затем вы вызываете экспортированную функцию Си. Хотя я вижу, что ты dll попал в критическую секцию. Это говорит мне, что у вас возникла ситуация мертвой блокировки с вашими глобально распределенными переменными и вашим потоком, выделяющим память в Start ().

Я рекомендую переместить код инициализации в Process_Attached, это обеспечит выделение всей вашей памяти в потоке основного процесса, аналогично тому, как приложение работало как один исполняемый файл.

0 голосов
/ 26 июля 2011

Я оставлю другой ответ как «принятый», но людям может быть полезно знать, что ключевой частью проблемы был тот факт, что я инициализировал libxml2 не в том потоке. В частности, вам нужно вызвать xmlInitParser () в вашем основном потоке, прежде чем делать какие-либо вызовы. Для меня это означало:

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

И аналогично, вам нужно вызвать xmlCleanupParser () при выходе:

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

Это все задокументировано здесь: http://xmlsoft.org/threads.html

...