我最近开始使用CMake,并且正在尝试构建一个GUI应用程序,它在Windows上没有控制台窗口。所以在我的CMakeLists.txt
文件中,我这样做了:
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")
有了这个,解决方案工作,并且控制台窗口无法在Windows上打开。 然而,这需要付出代价。当我尝试构建解决方案时,我意识到我必须将函数的签名更改为WinMain
,因此我将主代码更改为以下内容:
#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
}
不幸的是,我绝对厌恶这一点,因为它破坏了使用CMake的全部意义。我不想在我的代码中更改基于不同平台的任何内容。这引出了我的问题。在制作GUI应用程序时如何在Windows上将C ++应用程序入口点设置为main()
,而无需在Visual Studio中手动设置它?我可以使用跨平台方法直接在CMake中执行此操作吗?或者我必须使用#if/#else/#endif
解决方案吗?上述解决方案的唯一改进是使用宏MAIN_FUNCTION
来执行预处理器条件。我也希望避免这种情况。
另一方面,还有另一种方法可以摆脱Windows上的GUI应用程序中的控制台窗口,我不知道在不使用WIN32选项的情况下使用CMake吗?
答案 0 :(得分:3)
你在这里混淆了两件事,但它们密切相关。
出现的控制台是应用程序的结果,其Win32标头IMAGE_OPTIONAL_HEADER::Subsystem
值为WINDOWS_CUI
而不是WINDOWS_GUI
。这是一个Win32的东西,它适用于所有可执行文件,无论它们是用什么语言编写的。
入口点签名是特定于编译器的选择。它是语言运行时调用的入口函数,不是操作系统。操作系统调用语言运行时的入口函数,该函数首先初始化该运行时,然后将控制权交给您的入口点。
现在VC ++编译器使用CRT作为运行时。而CRT运行时确实为您的入口点使用了两个不同的签名。显然std::cin
的实现必须与WINDOWS_CUI
一起使用,这就是命令行用户界面的重点。但同样的CRT也适用于WINDOWS_GUI
。
这里的事情变得复杂。实际上,您可以将已编译应用程序的Subsystem
从CUI更改为GUI。 CRT不会介意,它与两个子系统兼容。但是,由于它是对已编译的应用程序完成的,因此从应用程序的一部分(CRT启动)到另一部分(您的入口点)的调用不会受到影响。这是Win32的变化,而不是C ++的变化。
回到CMake:这个子系统更改可以在CMake之后完成,也可以作为自定义的构建后步骤完成。
答案 1 :(得分:3)
解决方案是在set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
add_executable
它隐藏了控制台,同时仍然允许您使用通常的int main()
作为入口点。