我们在与“当前MFC状态”和线程相关的基于MFC的应用程序中存在问题 . 在主线程中我们调用VisualManager,因为我们想要有花哨的工具栏 . 该调用最终位于 afxvisualmanageroffice2007.cpp 内的 CMFCVisualManagerOffice2007::OnUpdateSystemColors 函数中,该函数通过调用 AfxSetResourceHandle 函数来更改“当前资源句柄” . 此函数获取“当前模块状态”并将“当前资源句柄”从 MyApp.exe 更改为 mfc140u.dll . 这很好,因为VisualManager的资源位于该DLL中,并且在所有情况下,更改都将恢复为 MyApp.exe .

但是,不正常的是我们在调用VisualManager之前生成一个新线程(通过使用 AfxBeginThread ),这个线程需要从字符串表中加载一些字符串(通过使用 CString 类),但有时候不能这样做 . 它失败是因为有关于 AFX_MODULE_STATE::m_hCurrentResourceHandle 变量的主线程的竞争 . 该线程期望将其设置为 MyApp.exe ,但主线程将其更改为 mfc140u.dll 并返回,“当前资源句柄”实际上是全局变量 .

所以,我的问题是:1)我们在管理基于MFC的线程时是否做了明显错误的事情?我们应该以某种方式复制或保护“模块状态”,以便我们的新线程能够免受主线程所做的更改吗?应该瞄准MFC创建像每线程变量/状态的东西? 2)我认为微软在这里是错误的,改变实际上是一个全局变量并拧紧其他线程的期望,VisualManager应该获取句柄并将其作为参数传递给它的所有函数 . 我对吗?

编辑:

嗨伙计@iinspectable,@ cha,我有更新,抱歉花了这么长时间 . 重现步骤:打开Visual Studio 2015 Update 3,通过向导创建新的MFC应用程序,确保将"Project style"和"Visual style and colors"选为"Office"和"Office 2007 (Blue theme)" . 从MSVS文件夹中打开文件 afxvisualmanageroffice2007.cpp 并将4个断点放入 CMFCVisualManagerOffice2007::OnUpdateSystemColors 函数中,它调用 AfxSetResourceHandle . 在新创建的项目文件夹中打开文件 MFCApplication1.cpp ,并在 CMainFrame* pMainFrame = new CMainFrame; 之前将此代码[1]放入 CMFCApplication4App::InitInstance 函数中,将断点放入此线程proc中 .

现在在调试模式下构建和运行这个MFC应用程序,在每个断点命中,使用冻结线程和从线程窗口解冻线程函数,所以你将在使用 AfxSetResourceHandle 函数设置全局变量之后在 CMFCVisualManagerOffice2007::OnUpdateSystemColors 函数中间安排主线程和 CStringT::LoadString 之前的工作线程 . 现在加载字符串将失败,因为它在 mfc140ud.dll 中查找它而不是使用资源链和 MFCApplication1.exe .

我相信这是微软的错误(改变全局变量一段时间),我的代码库充满了无辜的 CString::LoadString 调用,这些调用依赖于仔细和正确构建的资源链,各种插件DLL和最后的.exe . 如果这不是微软的错误,那么我依靠MFC为我提供可用的资源链 . 我需要创建自己的类似资源链的功能,并在从资源加载字符串和其他东西时随处使用它 .

// [1]
AFX_THREADPROC thread_proc = [](LPVOID pParam){
    CString str;
    str.LoadString(IDS_CAPTION_TEXT);
    UINT ret = 0;
    return ret;
};
::AfxBeginThread(thread_proc, (LPVOID)nullptr);
// Same result with ::AfxBeginThread(CRuntimeClass*) overload.