У меня есть немного кода, который вызывает метод из COM-объекта (IDirect3D9
), но каждый вызов вызывает ошибку проверки времени выполнения # 0. Ошибка вызвана тем, что ESP не сохраняется должным образом во время вызова, поэтому проблема со стеком (как COM-методы - все __stdcall
). Необычная часть - простота подписи метода и обстоятельств.вызов метода COM случайно повреждает стек
Код построен только в 32-разрядном режиме, с MSVC 10 (VS 2010 SP1), с использованием заголовков и библиотек DirectX SDK (июнь 2010 г.). Я переустановил SDK, чтобы убедиться, что заголовки не повреждены, и вам не повезло.
Я запустил код как с отладчиком VS, так и с WinDBG, а также несколько раз после перезагрузки/обновления драйверов. Проблема возникает каждый раз и идентична. Включение проверки валида (и большинства других опций) в gflags, похоже, не предоставляет никакой дополнительной информации и не работает с Application Verifier. Оба просто сообщают ту же ошибку, что и всплывающее окно, или segfault, возникший вскоре после этого.
Без вызова (вместо этого возвращаемое постоянное значение) программа работает как ожидалось. У меня нет идей о том, что здесь может быть не так.
Данная функция относится к IDirect3D9::GetAdapterModeCount
, вызывается из обертки D3D8-to-9 (часть a graphics upgrade project for old games). Для получения дополнительной информации, the full file is here.
Я пробовал все следующие формы вызова:
UINT r = m_Object->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
UINT r = m_Object->GetAdapterModeCount(0, (D3DFORMAT)22);
UINT adapter = D3DADAPTER_DEFAULT;
D3DFORMAT format = D3DFMT_X8R8G8B8; // and other values
UINT r = m_Object->GetAdapterModecount(adapter, format);
Все, которые вызывают сбой проверки. m_Object
является действительным IDirect3D9
, и используется ранее для множества других вызовов, в частности:
201, 80194887, Voodoo3D8, CVoodoo3D8::GetAdapterCount() == 3
201, 80195309, Voodoo3D8, CVoodoo3D8::GetAdapterIdentifier(0, 2, 0939CBAC) == 0
201, 80195309, Voodoo3D8, CVoodoo3D8::GetAdapterDisplayMode(0, 0018F5B4) == 0
201, 80196541, Voodoo3D8, CVoodoo3D8::GetAdapterModeCount(0, D3DFMT_X8R8G8B8) == 80
Последовательность регистрируется с помощью отладки кода трассировки, и представляется правильным и возвращает ожидаемые значения (3 монитора и так д). Первые 3 вызова, по одному и тому же объекту с моей стороны (один экземпляр CVoodoo3D8
), все успешны без предупреждений о стеках. Четвертый - нет.
Если я переупорядочу вызовы, чтобы вызвать GetAdapterModeCount
для вызова непосредственно перед любым из других объектов в одном и том же объекте, появляется тот же самый сбой проверки времени выполнения. От тестирования это, похоже, исключает немедленный предыдущий вызов, разбивающий стек; 4 метода, вызывающие эти 4 функции, все происходят в разных местах, и вызов GetAdapterModeCount
в любом месте этого файла вызывает проблему.
Это приводит нас к необычной части. Другой класс (CVoodoo3D9
) также вызывает ту же последовательность методов IDirect3D9
с аналогичными параметрами, но не прерывается (это эквивалентный класс-оболочка для D3D9). Объекты не используются одновременно (код выбирается в зависимости от процесса визуализации, который мне нужен), но оба они дают одинаковое поведение каждый раз. Код для другого класса хранится в другом файле, что привело меня к подозрению в проблемах с препроцессором (об этом чуть позже).
После этого не предоставлялась какая-либо информация, я рассмотрел вызывающие соглашения моего кода и параметров. Опять же, ничего не получилось. Кодовая база компилируется с /w4 /wX
и имеет некоторое время, с SAL для большинства функций, и все правила PREfast включены (и передаются).
В частности, вызов вызывается при вызове в этом классе, независимо от того, вызван ли вызов моего метода из моего кода или другой программы с использованием объекта.Он выходит из строя независимо от того, где он вызывается, но только внутри этого файла.
Полный метод:
UINT STDMETHODCALLTYPE CVoodoo3D8::GetAdapterModeCount(UINT Adapter)
{
UINT r = m_Object->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
gpVoodooLogger->LogMessage(LL_Debug, VOODOO_D3D_NAME, Format("CVoodoo3D8::GetAdapterModeCount(%d, D3DFMT_X8R8G8B8) == %d") << Adapter << r);
return r;
}
недостаточность Проверка происходит сразу после вызова GetAdapterModeCount
и снова, как мой метод возвращает, если разрешено выполнять в этой точке.
Выход препроцессор, как указано в опции предобработки в файл, имеет декларацию метод (от d3d9.h
) правильно, как:
virtual __declspec(nothrow) UINT __stdcall GetAdapterModeCount(UINT Adapter,D3DFORMAT Format) = 0;
Декларация моего метода, по существу, идентичны:
virtual __declspec(nothrow) UINT __stdcall GetAdapterModeCount(UINT Adapter);
Мой метод практически не расширяется, становясь:
UINT __stdcall CVoodoo3D8::GetAdapterModeCount(UINT Adapter)
{
UINT r = m_Object->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
gpVoodooLogger->LogMessage(LL_Debug, L"Voodoo3D8", Format("CVoodoo3D8::GetAdapterModeCount(%d, D3DFMT_X8R8G8B8) == %d") << Adapter << r);
return r;
}
Выход препроцессора кажется правильным для обоих методов, в декларации и определении.
Узел листинг до точки отказа является:
UINT STDMETHODCALLTYPE CVoodoo3D8::GetAdapterModeCount(UINT Adapter)
{
642385E0 push ebp
642385E1 mov ebp,esp
642385E3 sub esp,1Ch
642385E6 push ebx
642385E7 push esi
642385E8 push edi
642385E9 mov eax,0CCCCCCCCh
642385EE mov dword ptr [ebp-1Ch],eax
642385F1 mov dword ptr [ebp-18h],eax
642385F4 mov dword ptr [ebp-14h],eax
642385F7 mov dword ptr [ebp-10h],eax
642385FA mov dword ptr [ebp-0Ch],eax
642385FD mov dword ptr [ebp-8],eax
64238600 mov dword ptr [ebp-4],eax
UINT r = m_Object->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
64238603 mov esi,esp
64238605 push 16h
64238607 push 0
64238609 mov eax,dword ptr [this]
6423860C mov ecx,dword ptr [eax+8]
6423860F mov edx,dword ptr [this]
64238612 mov eax,dword ptr [edx+8]
64238615 mov ecx,dword ptr [ecx]
64238617 push eax
64238618 mov edx,dword ptr [ecx+18h]
6423861B call edx
6423861D cmp esi,esp
6423861F call _RTC_CheckEsp (6424B520h)
64238624 mov dword ptr [r],eax
Для получения разъяснений, ошибка происходит в 6423861F
(призыв к _RTC_CheckEsp
), предполагая, что вызов или препарат нарушил стек. Я работаю с предположением, что, поскольку тот же самый вызов работает в других местах, это не что-то в пределах разрыва вызова.
Для моего неподготовленного глаза единственной необычной частью является пара mov register, dword ptr [register+8]
. Поскольку это 32-разрядная система, я не уверен, что +8
может увеличиваться слишком далеко или как она может попасть в сборку, если это так.
Вскоре после того, как мой метод вернется, по-видимому, из-за разрыва вызова ESP, программа segfaults. Если я не вызываю GetAdapterModeCount
и просто возвращаю значение, программа выполняется, как ожидалось.
Кроме, релиз сборки (без RTC) на ошибку сегментации аналогичной точки, со стеком:
d3d8.dll!CEnum::EnumAdapterModes() + 0x13b bytes
Voodoo_DX89.dll!ClassCreate() + 0x963 bytes
Хотя я не уверен, о последствиях адреса. Это, насколько я могу судить, не то же место, что segfaults в отладочных сборках; они находятся внутри программы после того, как мои методы вернутся, это, по-видимому, во время одного из моих методов, который извлекает данные из D3D8. Редактировать: Segfault происходит в более позднем вызове, который я сейчас отлаживаю.
На данный момент у меня полная потеря относительно того, что происходит неправильно или как, и я не могу проверить.
Что произойдет, если вы закомментируете вызов 'gpVoodooLogger-> LogMessage'? – MSN
@MSN Никаких изменений, этот вызов, кажется, работает нормально. Проверка ESP происходит и выходит из строя до ее вызова. Комментирование другого вызова действительно помогает, но является важным. – ssube
Нет веской причины, что несбалансированное значение ESP приведет к краху вашего приложения. ESP восстанавливается функцией эпилога. Я бы просто исходил из предположения, что * обе проблемы имеют одну и ту же причину, внутреннее повреждение состояния программы. –