当CToolBar在32位系统运行时,MFC 64位会崩溃。

3
我有一个使用C++98编写的应用程序,在32位系统上运行良好。但在64位系统上,它会随机崩溃。可能有10次完全不会崩溃,但接下来连续崩溃3次。
这种崩溃发生在菜单栏创建期间。这个菜单栏是一个继承自CToolBar的类。
const DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY;

m_menuBar.SetSizes( CSize( 16,15 ), CSize( 1,1 ) );
const BOOL bCreated = m_menuBar.CreateEx( this, TBSTYLE_FLAT | TBSTYLE_LIST, dwStyle, CRect(0,0,0,0), AFX_IDW_TOOLBAR );

最近,我们经理决定从Visual Studio 2010升级到Visual Studio 2019,但现在64位系统中崩溃问题已经变得严重,而32位系统还没有出现这种情况。

可能出现了什么问题呢?

以下是我代码的栈信息:

Kernel.dll!000000001f009540()
user32.dll!00007fff7c7c63ed()
user32.dll!00007fff7c7c5fbc()
user32.dll!00007fff7c7d28d3()
ntdll.dll!00007fff7e4dfe14()
win32u.dll!00007fff7c4c1f24()
user32.dll!00007fff7c7b8011()
user32.dll!00007fff7c7b7c04()
user32.dll!00007fff7c7db122()
mfc140d.dll!00007fff3ba05208()
mfc140d.dll!00007fff3b9f4e56()
mfc140d.dll!00007fff3b9f4715()
mfc140d.dll!00007fff3b84a4c3()
d.exe!CDcmMainFrame::CreateMenuBar() Line 1001
d_d.exe!CDcmMainFrame::OnCreate(tagCREATESTRUCTA * lpCreateStruct) Line 925
[External Code]
d_d.exe!CVSDicomApp::InitInstance() Line 3074
[External Code]
d.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 26
d_d.exe!main(int ac, char * * av) Line 24
[External Code]

1
我已经阅读了CMFCMenuBar类的文档,以寻找我从x86切换到x64时记得的一些常见陷阱。但是我没有找到。如果this不符合预期怎么办(即这是对之前出现故障的某些内容的跟进)?我能记得一个具体的问题:句柄被存储为DWORD,在x86中很好,但在x64中可能会出现问题。这是不光彩的,因为句柄可能具有上32位为0的值。在这种情况下,它可以正常工作-您不会注意到失败。 - Scheff's Cat
1
也许你在某个地方将指针存储为DWORD或类似的东西。这是一个经典的例子。 - Jabberwocky
堆栈显示在CreateEx行崩溃了...我尝试检查所有指针,但似乎不是这个问题,也许我漏掉了什么。该应用程序有点混乱。 - Jalg
@Jalg 是this指针吗?还是崩溃实际上发生在SetSizes中?在那里设置断点并检查。 - Jabberwocky
@Jalg 请编辑问题并添加堆栈跟踪。它在调试模式和发布模式下都会崩溃吗?请将所有相关信息放入问题中。 - Jabberwocky
显示剩余3条评论
1个回答

0

很可能根本原因位于您发布的代码之外,并且与无效指针转换有关。了解更多有关您代码上下文的信息将会很有帮助。

我能想象到:

  • 您发布的代码位于CWnd派生类的方法中。
  • 该类的指针被转换并存储在32位整数中。
  • 通过将32位整数转换回您类的指针生成指向该类的指针来调用该方法。
  • 在64位系统上,这将清除指针的高32位。
  • 如果这些位为零,则不会发生任何事情,因为指针未更改。
  • 如果这些位不为零,则指针会受损并指向无效的内存区域。最终,在调用CreateEx时,“this”表示这个已经损坏的指针,最终访问了错误的内存位置。
  • 在32位系统上,代码可以正常工作,因为指针仅为32位,当转换为32位整数时不会被截断。

您可以尝试以下操作:

插入一个

Trace("*** MyClass = %p ***\n", this);

在CreateEx调用之前,同时调用您的类的构造函数和直接调用。如果发生崩溃,这两个调试输出是否匹配?

您还可以尝试设置一个断点到CreateEx调用上,这样当执行到CreateEx被调用时,程序会停止。然后检查“this”指针。类的所有成员都看起来有效吗?特别是m_hWnd应该!= NULL。(当然,这必须针对发生崩溃的情况进行评估,因此您可能需要多次尝试,因为您事先不知道结果。)而且,您还可以在创建“this”窗口后直接在CreateEx调用之前一次输出m_hWnd。它们相等吗?

如果我理解正确,代码位于您的主应用程序中-不在DLL内,是吗?(过去我在dll中创建MFC对象时遇到过一些奇怪的行为,当我忘记了AFX_MANAGE_STATE宏时。)

您的应用程序是否使用多个线程?是不是CreateEx调用有时是由不同的线程完成的?当在一个类中混合使用不同线程的MFC调用时,MFC会出现问题。您可以将上述调试输出扩展到以下内容:

    Trace("*** MyClass = %p, ThreadId = %u ***\n", this, GetCurrentThreadId());

线程 ID 应始终等于 "this" 指针。


此create menu的调用在MainFrame派生类中,来自COXFrameWndSizeDock。我会尝试找到一些类似于您定义的指针。 - Jalg
这是带有跟踪的输出:atlTraceGeneral - *** MyClass = 000000001BB5C1E0 *** atlTraceGeneral - *** MyClass = 000000001BB5C1E0 ***所以,是相同的... - Jalg
不需要添加任何代码来仅仅转储一些信息。相反,您可以使用跟踪点记录信息。请参阅:https://learn.microsoft.com/en-us/visualstudio/debugger/using-tracepoints - IInspectable
一些成员可能为空,但 m_hWnd 不会为空。 - Jalg
不是空指针,但它说未使用=???,我不知道这是什么意思或是否正常。是的,它位于我的主应用程序中。我试图解决与32/64位问题相关的一些警告,但没有成功。该应用程序是多线程的,但我认为启动始终是主线程,我将更加注意观察。 - Jalg

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