Toggle MFC application to console app

Hoang, Steve 21 Reputation points
2020-09-25T16:07:27.77+00:00

We have an existing MFC application. We want to launch as console mode (depends on the parameters), which still using Windows resources, ... Is it possible?
Thanks,
Steve

Developer technologies | C++
0 comments No comments
{count} votes

Accepted answer
  1. RLWA32 49,636 Reputation points
    2020-09-28T17:35:02.143+00:00

    I added a small bit of code to the InitInstance function of an MFC application created by the New Application template.

    The code looks for /CONSOLE on the command line and, if it is found, it runs a console interface on a secondary thread while the main thread waits for the secondary thread to exit. After the console interface is closed the program terminates.

    BOOL CMFCToggleApp::InitInstance()
    {
        // InitCommonControlsEx() is required on Windows XP if an application
        // manifest specifies use of ComCtl32.dll version 6 or later to enable
        // visual styles.  Otherwise, any window creation will fail.
        INITCOMMONCONTROLSEX InitCtrls;
        InitCtrls.dwSize = sizeof(InitCtrls);
        // Set this to include all the common control classes you want to use
        // in your application.
        InitCtrls.dwICC = ICC_WIN95_CLASSES;
        InitCommonControlsEx(&InitCtrls);
    
        CWinAppEx::InitInstance();
    
        int nArgs = 0;
        bool bAlloc = false;
    
        LPWSTR *psz = CommandLineToArgvW(GetCommandLineW(), &nArgs);
        for (int i = 0; i < nArgs; i++)
        {
            if (_wcsicmp(psz[i], L"/CONSOLE") == 0)
                bAlloc = true;
        }
    
        if (bAlloc)
        {
            std::thread tconsole([]{
                FILE *fpstdin = stdin, *fpstdout = stdout, *fpstderr = stderr;
    
                if (AllocConsole())
                {
                    // Initialize for console i/o
                    freopen_s(&fpstdin, "CONIN$", "r", stdin);
                    freopen_s(&fpstdout, "CONOUT$", "w", stdout);
                    freopen_s(&fpstderr, "CONOUT$", "w", stderr);
    
                    puts("This is the console interface on a secondary thread");
                    puts("The MFC main window has not been created");
                    puts("Hit a key to exit the program");
                    _getch();
    
                    fclose(stderr);
                    fclose(stdout);
                    fclose(stdin);
    
                    FreeConsole();
                }
                else
                {
                    CString strError;
                    strError.Format(_T("AllocConsole failed, error code was %d"), GetLastError());
                    AfxMessageBox(strError);
                }
            });
    
            tconsole.join(); // wait for console interface thread to exit
            return FALSE;  // exit the program
        }
    
        // Initialize OLE libraries
        if (!AfxOleInit())
        {
            AfxMessageBox(IDP_OLE_INIT_FAILED);
            return FALSE;
        }
    
        AfxEnableControlContainer();
    
        EnableTaskbarInteraction(FALSE);
    
        // AfxInitRichEdit2() is required to use RichEdit control   
        // AfxInitRichEdit2();
    
        // Standard initialization
        // If you are not using these features and wish to reduce the size
        // of your final executable, you should remove from the following
        // the specific initialization routines you do not need
        // Change the registry key under which our settings are stored
        // TODO: You should modify this string to be something appropriate
        // such as the name of your company or organization
        SetRegistryKey(_T("Local AppWizard-Generated Applications"));
        LoadStdProfileSettings(4);  // Load standard INI file options (including MRU)
    
    
        InitContextMenuManager();
    
        InitKeyboardManager();
    
        InitTooltipManager();
        CMFCToolTipInfo ttParams;
        ttParams.m_bVislManagerTheme = TRUE;
        theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
            RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
    
        // Register the application's document templates.  Document templates
        //  serve as the connection between documents, frame windows and views
        CSingleDocTemplate* pDocTemplate;
        pDocTemplate = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CMFCToggleDoc),
            RUNTIME_CLASS(CMainFrame),       // main SDI frame window
            RUNTIME_CLASS(CMFCToggleView));
        if (!pDocTemplate)
            return FALSE;
        AddDocTemplate(pDocTemplate);
    
    
        // Parse command line for standard shell commands, DDE, file open
        CCommandLineInfo cmdInfo;
        ParseCommandLine(cmdInfo);
    
    
    
        // Dispatch commands specified on the command line.  Will return FALSE if
        // app was launched with /RegServer, /Register, /Unregserver or /Unregister.
        if (!ProcessShellCommand(cmdInfo))
            return FALSE;
    
        // The one and only window has been initialized, so show and update it
        m_pMainWnd->ShowWindow(SW_SHOW);
        m_pMainWnd->UpdateWindow();
        return TRUE;
    }
    
    1 person found this answer helpful.
    0 comments No comments

5 additional answers

Sort by: Most helpful
  1. RLWA32 49,636 Reputation points
    2020-09-25T16:54:09.907+00:00

    Windows will automatically create a console for an application that is linked with /SUBSYSTEM:CONSOLE when it starts running. And for these applications the CRT will also initialize the handles used for console i/o such as stdin, stdout and stderr. A console application can still create windows and use gui resources if it provides the necessary scaffolding (e.g., message pump, etc.).

    Conversely, Windows does not automatically create a console for a gui application that is linked with /SUBSYSTEM:WINDOWS when it starts running. And for these applications the CRT does not initialize the handles used for console i/o. A gui application can use AllocConsole to create a console and then manually initialize stdin, stdout and stderr for any console i/o.

    One thing that I have seen done is to create a windows gui application. After the executable has been linked the EDITBIN utility is used to change the /SUBSYSTEM setting from /SUBSYSTEM:WINDOWS to /SUBSYSTEM:CONSOLE.

    0 comments No comments

  2. Hoang, Steve 21 Reputation points
    2020-09-25T16:58:46.487+00:00

    But I don't want a new application, I need to toggle the existing MFC app to run as console mode.
    Thanks,
    Steve Hoang


  3. Darran Rowe 1,991 Reputation points
    2020-09-26T19:24:08.09+00:00

    The one very important thing to remember about this is that a GUI application, any GUI application, is ill equipped to use the console. The MFC projects are even worse because you do not have access to the WinMain function. This is important because, while you are unclear on what you mean by "toggle the existing MFC app", the meaning that I am assuming is that you want to switch between a GUI mode and a console mode. The default MFC WinMain does not let you do this.

    The MFC WinMain works as a regular GUI main function, it initialises the application and windows and then goes into a message loop. If you want it to work as a hybrid then you can't do this. You would need to be able to customise how the application initialises and decide whether or not you need to use the GUI or the console UI. It is definitely not something you can "toggle".

    To put it simply, it is not something that your application can do automatically, the behaviour must be added by you and because of the problems with WinMain, this isn't easy, maybe not even possible to do with an MFC application. So unfortunately your options are limited. You could do the following:

    1) Write a new application and have the WinMain function specifically written to switch between the two based on a command line argument. This requires you checking to see if a console is available and reinitialising the IO functions if a console was not available at the start.
    2) Extract the functionality you want out into a separate DLL and then writing a console frontend for this. This allows you to keep the UI and functionality separate.
    3) Find some way to override the MFC default entry point so you can hijack it and then forward it to the MFC if you want to go into GUI mode.

    As you probably noticed, there is no easy option and that is because what you want is not a default for a Windows application. So you have the options of going from a GUI subsystem and allocating the console or going from a console application and deleting the console.


  4. Hoang, Steve 21 Reputation points
    2020-09-28T13:53:39.507+00:00

    DarranRowe is understand my issue. The management doesn't want to create new application & copy the files over, since it will be a huge project, and a chance of create mistakes are very high.

    Can I just create a std::thread (not AfxCreateThread) & run that thread only? It's seem like that thread will not yield back to the main message loop.

    Thanks,
    Steve Hoang

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.