Click here to Skip to main content
15,910,981 members
Articles / Programming Languages / C++
Tip/Trick

VC++: Convert Console Project to Non-Console

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
18 Feb 2024MIT2 min read 7.2K   111   5   12
Make a console program not to show the console screen
This tip guides Visual C++ developers on converting a Console project to hide the console screen.

This short write-up is a tip on making console program not to show console. As such, it is not eligible to participate in the CodeProject monthly article competition.

This tip is written in response to the Q&A question posted by Eduardo Quintana from Brazil. I encountered this same problem many years back when I downloaded an OpenGL tutorial that launched a console window before opening an OpenGL window. Having a console is extremely useful to log the messages to screen for troubleshooting but as a final product, only the OpenGL window should be shown to the user. In this tip, I'll show you how to convert a Visual C++ Console project not to show the console screen.

Firstly, to open Project Properties dialog, right-click on the project in the Solution Explorer and select Properties at the bottom of the pop-up menu or you can type Alt+Enter key. Select the Linker->System on the tree on the left panel. Then, change the Configuration dropdown to All Configurations and Platform dropdown to All Platforms. Finally, change the Subsystem from Console (/SUBSYSTEM:CONSOLE) to Windows (/SUBSYSTEM:WINDOWS) as shown on the screenshot.

  • Configuration: All Configurations
  • Platform: All Platforms
  • Subsystem: Windows (/SUBSYSTEM:WINDOWS)

Select Windows Subsystem

Then, you can rebuild the project, but you will encounter this linker error.

error LNK2019: unresolved external symbol WinMain referenced in function 
               "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)

The solution is to add WinMain function and include Windows.h. Console program starts from main while Windows program begins from WinMain. You can call your original main from WinMain.

C++
#include <Windows.h>

// Your original main function
int main()
{
    // your original code here
    return 0;
}

int __stdcall WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR     lpCmdLine,
    int       nShowCmd
)
{
    // You can use OutputDebugStringA to log your
    // debug message to your Visual Studio debugger
    // or Sysinternals DebugView
    OutputDebugStringA("Hello World!\n");

    return main(); // call your main() here
}

If you do not wish to include the heavy duty Window.h to bring in the WinMain, you can declare WinMain as such. I cannot do it here as I need the OutputDebugStringA from that header.

C++
#define HINSTANCE void*
#define LPSTR const char*
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                      LPSTR lpCmdLine, int nShowCmd);

Rebuild your project. It should run without a console window. If you need to forward lpCmdLine to your main, you can use ParseCmdlineA for ASCII or ParseCmdlineW for Unicode version. Both take care of command argument specified in quotes. Unfortunately, lpCmdLine does not contain the path of executable which I have to separately retrieve using GetModuleFileNameA. This feature is suggested by Richard Chambers to make the solution complete.

C++
void ParseCmdlineW(const wchar_t* cmdLine,
    std::vector<wchar_t*>& vecArgs, std::wstring& holder)
{
    // implementation not shown
}

void ParseCmdlineA(const char* cmdLine,
    std::vector<char*>& vecArgs, std::string& holder)
{
    // implementation not shown
}

#ifdef UNICODE
    #define ParseCmdlineT ParseCmdlineW
#else
    #define ParseCmdlineT ParseCmdlineA
#endif

int main(int argc, char* argv[])
{
    // print out the cmd args in debugger
    for (int i = 0; i < argc; ++i)
    {
        OutputDebugStringA(argv[i]);
        OutputDebugStringA("\n");
    }

    return 0;
}

int __stdcall WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR     lpCmdLine,
    int       nShowCmd
)
{
    std::vector<char*> vecArgs;
    std::string holder;
    char executablePath[MAX_PATH];
    DWORD res = GetModuleFileNameA(
        NULL,
        executablePath,
        MAX_PATH
    );
    if (res != 0)
    {
        vecArgs.push_back(executablePath);
        ParseCmdlineA(lpCmdLine, vecArgs, holder);
    }

    return main((int)vecArgs.size(), vecArgs.data());
}

Alternate Solutions: Hide the Console or Make a Window Service

In the article comment section, Stacy Dudovitz gives two solutions: either move the console window offscreen and hide it or make your application a Windows service. Marius Bancila has written an excellent Windows service article: Interact with Windows Services in C++; You can use it as a guide to write your service.

C++
#include <Windows.h>

// suggested by Stacy Dudovitz
int main()
{
    // move the window offscreen, then hide it
    HWND hwnd = ::GetConsoleWindow();
    ::SetWindowPos(hwnd, NULL, 5000, 5000, 0, 0, 0);
    ::ShowWindow(hwnd, SW_HIDE);

    // application code here 
    //     .....

    return 0;
}

History

  • 19th February, 2024: Added solution if user does not want to include Windows.h for WinMain
  • 7th February, 2024: Added alternate solutions by Stacy Dudovitz. Added HideConsole project download
  • 30th January, 2024: First release

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
Singapore Singapore
Shao Voon is from Singapore. His interest lies primarily in computer graphics, software optimization, concurrency, security, and Agile methodologies.

In recent years, he shifted focus to software safety research. His hobby is writing a free C++ DirectX photo slideshow application which can be viewed here.

Comments and Discussions

 
QuestionWindows app vs. Console app - summary of differences which have not been addressed Pin
Stacy Dudovitz4-Feb-24 17:32
professionalStacy Dudovitz4-Feb-24 17:32 
Apparently in the messages below, there still seems to be some belief that a Console application which is converted to a Windows is functionally the same. Not true, here are the differences (they are important, I'm looking for an answer how these would be addressed):

1) stdin, stdout and stderr are sent to devnull. This isn't just a question of command line parsing, many console apps act as "filters" (think Unix/Linux type apps like grep). These need to be rewritten to handle input/output.

2) That being said, a Console app allocates a Console window. A Windows app does not, but can attach to one. Important when writing a Console app that interacts with Remote Desktop/Terminal Server.

3) Windows app always start in an STA thread (they have to for the Dispatcher to work). Console apps start in an MTA thread. This matter is Console app create COM objects, as the default behavior would rely on MTA cross-COM calls. Suddenly finding oneself in an STA thread would create opportunities for errors and memory corruption. The COM objects would either need to be converted to run in an STA thread, or would require marshaling code i.e. this becomes a DCOM app which adds a host of complexity.

Even if you don't explicitly create COM object, many OS calls are wrappers around COM objects e.g. Shell services for interacting with the underlying file system which would properly show in the Windows Explorer.

4) Thread creation would by default be MTA, since the foreground is MTA in a Console app. Once changed to a Windows app, these additional threads need to change how communicate to the foreground thread. Same issues apply as in (3).

Can all of this be addressed? Of course, if you, Joe/Jane developer are careful and judicious about the approach. That's what's missing from the article, and should be added to avoid error prone code and apps from be let out into the wild.

Same applies to the SO article, but I think I will just leave the comments here in one place (Code Project).
QuestionStackoverflow post with more details about this approach Pin
Richard Chambers31-Jan-24 18:43
Richard Chambers31-Jan-24 18:43 
AnswerRe: Stackoverflow post with more details about this approach Pin
Shao Voon Wong31-Jan-24 20:32
mvaShao Voon Wong31-Jan-24 20:32 
AnswerRe: Stackoverflow post with more details about this approach Pin
Shao Voon Wong6-Feb-24 18:02
mvaShao Voon Wong6-Feb-24 18:02 
QuestionSeveral problems with this approach Pin
Stacy Dudovitz31-Jan-24 10:35
professionalStacy Dudovitz31-Jan-24 10:35 
AnswerRe: Several problems with this approach Pin
Richard Chambers31-Jan-24 18:33
Richard Chambers31-Jan-24 18:33 
AnswerRe: Several problems with this approach Pin
Shao Voon Wong1-Feb-24 20:40
mvaShao Voon Wong1-Feb-24 20:40 
GeneralRe: Several problems with this approach Pin
Stacy Dudovitz4-Feb-24 0:38
professionalStacy Dudovitz4-Feb-24 0:38 
GeneralRe: Several problems with this approach Pin
Richard Chambers4-Feb-24 8:50
Richard Chambers4-Feb-24 8:50 
GeneralRe: Several problems with this approach Pin
Shao Voon Wong6-Feb-24 18:06
mvaShao Voon Wong6-Feb-24 18:06 
AnswerRe: Several problems with this approach Pin
Shao Voon Wong6-Feb-24 18:07
mvaShao Voon Wong6-Feb-24 18:07 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA30-Jan-24 11:18
professionalȘtefan-Mihai MOGA30-Jan-24 11:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.