C++ COM方法调用意外损坏堆栈

C++ COM方法调用意外损坏堆栈,c++,com,stack-smash,C++,Com,Stack Smash,我有一段代码从COM对象调用方法(IDirect3D9),但每次调用都会导致运行时检查失败#0。失败是由于ESP没有在整个调用中正确保留,因此出现了某种堆栈问题(因为COM方法都是\uu stdcall)。不同寻常的是方法签名和环境的简单性 该代码仅以32位模式构建,使用MSVC 10(VS 2010 SP1),使用DirectX SDK(2010年6月)头文件和libs。我已经重新安装了SDK,以确保标题没有损坏,没有运气 我已经在VS调试器和WinDBG连接的情况下运行了代码,并且在重新启动

我有一段代码从COM对象调用方法(
IDirect3D9
),但每次调用都会导致运行时检查失败#0。失败是由于ESP没有在整个调用中正确保留,因此出现了某种堆栈问题(因为COM方法都是
\uu stdcall
)。不同寻常的是方法签名和环境的简单性

该代码仅以32位模式构建,使用MSVC 10(VS 2010 SP1),使用DirectX SDK(2010年6月)头文件和libs。我已经重新安装了SDK,以确保标题没有损坏,没有运气

我已经在VS调试器和WinDBG连接的情况下运行了代码,并且在重新启动/更新驱动程序后多次运行了代码。问题每次都会出现,并且是相同的。在gflags中启用堆验证(以及大多数其他选项)似乎不会提供更多信息,也不会使用应用程序验证程序运行。两者都只是报告与弹出窗口相同的错误,或者在弹出窗口后不久导致的segfault

如果没有调用(而是返回一个常量值),程序将按预期运行。我不知道这里会出什么问题

所讨论的函数是从D3D8-to-9包装器(的一部分)调用的。有关更多一般信息

我尝试了以下所有形式的通话:

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_对象
是一个有效的
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,并且所有的预快速规则都已启用(并通过)

尤其是,当在此类中调用时,调用失败,无论对我的方法的调用来自我的代码还是使用该对象的其他程序。无论在何处调用,它都会失败,但仅在该文件中失败

完整的方法是:

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;
}
我的方法声明基本相同:

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;
}
为了澄清,错误出现在
6423861F
(调用
\u RTC\u CheckEsp
),表明调用或准备工作破坏了堆栈。我的工作假设是,由于同一个呼叫在其他地方工作,因此它不是呼叫中断的东西

在我未经训练的眼中,唯一不寻常的部分是一对
mov寄存器,dword ptr[register+8]
。由于它是一个32位系统,我不确定
+8
是否会增加太多,或者如果是这样,它如何进入构建

在我的方法返回后不久,显然是由于调用中断ESP,程序发生了故障。如果我不调用
GetAdapterModeCount
并简单地返回一个值,程序将按预期执行

另外,发布版本(无RTC)在类似点发生故障,堆栈:

d3d8.dll!CEnum::EnumAdapterModes()  + 0x13b bytes   
Voodoo_DX89.dll!ClassCreate()  + 0x963 bytes
虽然我不确定这个地址的含义。就我所知,它与调试构建中的segfaults不是同一个位置;在我的方法返回后,这些都在程序中,这似乎是在我的一个方法从D3D8检索数据期间Edit:segfault发生在稍后的调用中,我目前正在调试该调用


在这一点上,我完全不知道哪里出了问题,怎么出了问题,也没有什么可检查的。

我看不出您正在做的事情或生成的汇编代码有任何问题

不过,我可以回答你的一个问题

ecx,dword ptr [eax+8]
这样做的目的是将m_对象的地址移动到ecx寄存器中。+8是类到m_对象的偏移量,这可能是正确的

要看的东西。逐步执行程序集代码,直到达到此点:

6423861B  call        edx  
6423861D  cmp         esi,esp
此时,检查esi和esp寄存器(在VS中,只需将鼠标悬停在寄存器名称上)

在执行调用之前,ESI应比ESP高12。调用之后,ESI应相等。如果他们不是,张贴他们是什么

更新:

因此,吸引我注意的是,在您显示要调用的4个方法中,只有
getAdapterModelCount
在D3D8和D3D9之间具有不同的签名,并且该签名相差4个字节,这就是堆栈中的差异

m_对象是如何获得的?由于这是D3D8和D3D9之间的某种适配器,您的m_对象是否可能实际上是一个IDirect3D8对象,在某个点上转换为IDirect3D9?这将解释错误,以及为什么它在另一个上下文中工作
ecx,dword ptr [eax+8]
6423861B  call        edx  
6423861D  cmp         esi,esp