I have recently started using CMake, and was trying to build a GUI application, that doesn't have the console window on Windows. So in my CMakeLists.txt
file, I did this:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_executable(${EXECUTABLE_NAME} main.cpp)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_executable(${EXECUTABLE_NAME} WIN32 main.cpp) #WIN32 So the console window does not open on Windows
endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
With this, the solution worked, and the console window does not open on Windows. However, this comes at a cost. When I try to build the solution, I realize that I have to change the signature of the function to WinMain
, so I changed my main code to the following:
#ifdef _WIN32
#include <Windows.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) //Windows signature since creating WIN32 application without console
#else
int main()
#endif
{
// ... GUI code
}
Unfortunately, I absolutely detest this, since it ruins the whole point of using CMake. I don't want to have to change anything in my code that is based on different platforms. This leads me to my question. How do I set the C++ application entry point to main()
on Windows when making a GUI application without having to set it manually in Visual Studio? Can I do this directly in CMake using a cross-platform method? Or will I have to use the #if/#else/#endif
solution? The only improvement to the solution above is using a macro MAIN_FUNCTION
that does the preprocessor conditional. I want to avoid this as well.
On the other hand, is there another way to get rid of the console window in a GUI application on Windows that I didn't know of using CMake without using the WIN32 option?
The solution is to add set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
before add_executable
It hides the console while still allowing you to have the usual int main()
as the entry point.
You're confusing two things here, but they are closely related.
The console that appears is a result of an application which has a Win32 header IMAGE_OPTIONAL_HEADER::Subsystem
value of WINDOWS_CUI
instead of WINDOWS_GUI
. This is a Win32 thing, and it applies to all executables regardless of the language they're written in.
The entry point signature is a compiler-specific choice. It's the entry function called by the language runtime, not the OS. The OS calls the entry function of the language runtime, which first initialized that runtime and then hands off control to your entry point.
Now the VC++ compiler uses the CRT as the runtime. And that CRT runtime indeed uses two different signatures for your entry point. Obviously the implementation of std::cin
has to work with WINDOWS_CUI
, that's sort of the point of a Command-Line User Interface. But the same CRT also works with WINDOWS_GUI
.
Here's where things get complex. You can actually change the Subsystem
of a compiled application from CUI to GUI. The CRT won't mind, it's compatible with both subsystems. But since it's done to a compiled application, the call from one part of the application (CRT startup) to another (your entry point) isn't affected. This is a Win32 change, not a C++ change.
To get back to CMake: this subsystem change can be done either after CMake, or as a custom post-build step.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With