Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
844 views
in Technique[技术] by (71.8m points)

winforms - afxwin.h issues in Visual Studio 2015 Windows Form App

A while ago i wrote a C++ CLI Windows Form app, which compiled fine in Visual Studio 2013. Now i wanted to recompile it in Visual Studio 2015 Update 1 but i'm facing a problem, and after hours of tests i figured out the culprit is afxwin.h.


TL;DR - Is there any way i can use stdafx.h (so afxwin.h and all other imports coming with it) in a Windows Form app compiled with Visual Studio 2015 without having the app crash upon start?


Here's how to reproduce the same issues i'm facing in my app.

Since Windows Form is no longer available as project template in VS2015, i created a CLR Empty Project called Test

Ctrl-Shift-A to add a UI > Windows Form called MyForm

In MyForm.cpp i added this:

#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);
}

Under Configuration Properties > Linker > Advanced i set Entry Point to main

Under Configuration Properties > Linker > System i set SubSystem to Windows (/SUBSYSTEM/WINDOWS)

COMPILE (DEBUG CONFIGURATION): compiles with no errors/warnings

RUN: runs without any problem.

Now lets try adding afxwin.h to MyForm.cpp:

#include <afxwin.h>

Under Configuration Properties > General i set Use of MFC to Use MFC in a shared DLL

COMPILE (DEBUG CONFIGURATION): compiles with no errors/warnings

RUN: the app wont even start, it just shows Debug Assertion Failed error in expression _CrtIsValidHeapPointer(block)

enter image description here enter image description here enter image description here Now to fix this error i found that it's necessary to remove the Entry Point, so:

Under Configuration Properties > Linker > Advanced i removed Entry Point value (which i previously set to main)

COMPILE (DEBUG CONFIGURATION): compiles with no errors/warnings

RUN: the app again wont even start, it no longer shows Debug Assertion Failed but System.AccessViolationException in an unknown module and "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

enter image description here enter image description here enter image description here enter image description here These are the errors i get in my app, and i'm wondering how is it possible that simply including afxwin.h gives all these problems in VS2015 while it didnt in VS2013.

Is there anything i can do to fix it, without going back to VS2013?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The C runtime library was significantly rewritten for VS2015 by James McNellis. He's a big C++ fan, the new code he's written suffers from the chronic SIOF problem that's so common in a C++ program. The Static Initialization Order Fiasco was surely present in your VS2013 project as well but happened not to byte, the original CRT code was exposed to SIOF for many years so likely to behave better.

Excessively hard to debug in this case, the code that fails comes from a CRT source code file that is not included in the install named thread_safe_statics.cpp. Not 100% sure what it does given that there's no source code to look at but the name of the file leaves little to the imagination.

MFC has static state that needs to be initialized before it is usable. In particular, the program must have a static CWinApp variable that is initialized at Just the Right Time. That requires the entrypoint to be WinMain(), implemented in MFC, and an explicit declaration of a CWinApp instance in your source code. Like this:

[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;

Reset the linker's EntryPoint setting back to its default (blank) so the CRT is initialized first and MFC's WinMain function runs next. And beware I took a shortcut, you get no args. And I fixed a bug in your main() function, it used stack semantics incorrectly.

This hack gets your program running again. Whether it is actually correct is rather doubtful. This suffers from the "Who is the Boss" syndrome that's associated with big frameworks. Don't depend on any MFC window to work correctly since it is Winforms that is dispatching messages. But you should have had that problem in VS2013 as well. "Don't do it" is the only solid advice.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...