0

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


TL;DR - 有什么方法可以在使用 Visual Studio 2015 编译的 Windows 窗体应用程序中使用stdafx.hafxwin.h以及所有其他导入),而不会在启动时应用程序崩溃?


以下是如何重现我在我的应用程序中遇到的相同问题。

由于 Windows 窗体在 VS2015 中不再可用作项目模板,因此我创建了一个名为Test的CLR 空项目

Ctrl-Shift-A 添加名为MyForm的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);
}

Configuration Properties > Linker > Advanced下,我将入口点设置为main

Configuration Properties > Linker > System我将SubSystem设置为Windows (/SUBSYSTEM/WINDOWS)

编译(调试配置):编译时没有错误/警告

RUN:运行没有任何问题。

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

#include <afxwin.h>

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

编译(调试配置):编译时没有错误/警告

RUN:应用程序甚至无法启动,它只是在表达式_CrtIsValidHeapPointer(block)中显示Debug Assertion Failed错误

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 现在要修复这个错误,我发现有必要删除Entry Point,所以:

Configuration Properties > Linker > Advanced我删除了入口点值(我之前设置为main

编译(调试配置):编译时没有错误/警告

RUN:应用程序甚至无法启动,它不再显示Debug Assertion FailedSystem.AccessViolationException in an unknown module和“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 这些是我在我的应用程序中遇到的错误,我想知道如何简单地包含afxwin.h在 VS2015 中给出所有这些问题,而在 VS2013 中却没有。

我有什么办法可以解决它,而不回到 VS2013?

4

1 回答 1

2

James McNellis 为 VS2015 重写了 C 运行时库。他是 C++ 的忠实粉丝,他编写的新代码存在 C++ 程序中常见的慢性 SIOF 问题。静态初始化顺序惨败肯定也出现在您的 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 让你的程序再次运行。它是否真的正确是相当值得怀疑的。这受到与大框架相关的“谁是老板”综合症的困扰。不要依赖任何 MFC 窗口来正常工作,因为它是 Winforms 正在发送消息。但是你应该在 VS2013 中也遇到过这个问题。“不要这样做”是唯一可靠的建议。

于 2016-02-23T12:43:14.510 回答