afxwin.h 问题 Visual Studio 2015 Windows 表单应用程序

afxwin.h issues in Visual Studio 2015 Windows Form App

不久前我写了一个 C++ CLI Windows Form 应用程序,它在 Visual Studio 2013 中编译得很好。现在我想在 Visual Studio 2015 Update 1 中重新编译它,但是我'我遇到了一个问题,经过几个小时的测试,我发现罪魁祸首是 afxwin.h.


TL;DR - 有什么方法可以在 Windows 使用 Visual Studio 2015 编译的表单应用程序没有在启动时崩溃?


以下是重现我在我的应用中遇到的相同问题的方法。

由于 Windows Form 在 VS2015 中不再可用作项目模板,我创建了一个名为 TestCLR 空项目

Ctrl-Shift-A 添加一个 UI > Windows 表单 调用 我的表格

MyForm.cpp中我添加了这个:

#include "MyForm.h"

using namespace System;
using namespace System::Windows::Forms;

[STAThread]
int main(cli::array<System::String^>^ args)
{
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    Test::MyForm form;
    Application::Run(%form);
}

配置属性 > 链接器 > 高级下我设置了条目指向main

配置属性 > 链接器 > 系统下我设置了子系统Windows (/SUBSYSTEM/WINDOWS)

编译(调试配置): 编译没有 errors/warnings

运行: 运行没有任何问题。

现在让我们尝试将 afxwin.h 添加到 MyForm.cpp:

#include <afxwin.h>

配置属性>常规下,我将MFC的使用设置为在共享 DLL 中使用 MFC

编译(调试配置): 编译没有 errors/warnings

运行: 应用甚至无法启动,它只是显示 调试断言失败 表达式错误 _CrtIsValidHeapPointer(块)

现在要修复此错误,我发现有必要删除 入口点 ,因此:

配置属性 > 链接器 > 高级下我删除了条目Point值(我之前设置为main

编译(调试配置): 编译没有 errors/warnings

运行: 应用程序甚至无法启动,它不再显示 Debug Assertion FailedSystem.AccessViolationException 在未知模块中 和 "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

这些是我在我的应用程序中遇到的错误,我想知道简单地包含 afxwin.h 怎么可能在 VS2015 中给出所有这些问题,而在 VS2013 中却没有。

有什么办法可以修复它,而无需返回到 VS2013?

James McNellis 为 VS2015 重写了 C 运行时库。他是 C++ 的忠实粉丝,他编写的新代码存在 C++ 程序中常见的慢性 SIOF 问题。 Static Initialization Order Fiasco 肯定也存在于您的 VS2013 项目中,但碰巧不是字节,原始 CRT 代码暴露于 SIOF 多年,因此可能表现得更好。

在这种情况下很难调试,失败的代码来自 CRT 源代码文件,该文件未包含在名为 thread_safe_statics.cpp 的安装中。不能 100% 确定它的作用,因为没有源代码可供查看,但文件名几乎没有想象空间。

MFC 具有静态状态,需要在使用前对其进行初始化。特别是,程序必须有一个在正确的时间初始化的静态 CWinApp 变量。这需要入口点是 WinMain(),在 MFC 中实现,并在源代码中显式声明 CWinApp 实例。像这样:

[STAThread]
int main(cli::array<System::String^>^ args)
{
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    Application::Run(gcnew Test::MyForm);
}

class MyMfcApp : public CWinApp {
public:
    virtual int Run() override {
        return main(__nullptr);
    }
} MyApp;

将链接器的 EntryPoint 设置重置为默认值(空白),以便首先初始化 CRT,然后运行 ​​MFC 的 WinMain 函数。小心我走了捷径,你得不到 args。我修复了你的 main() 函数中的一个错误,它错误地使用了 堆栈语义

此 hack 让您的程序再次 运行。它是否实际上正确是相当值得怀疑的。这患有与大型框架相关的 "Who is the Boss" 综合症。不要依赖任何 MFC window 才能正常工作,因为它是 Winforms 正在调度消息。但是你应该在 VS2013 中也遇到过这个问题。 "Don't do it" 是唯一可靠的建议。