如何使用CMake将C ++应用程序入口点设置为Windows上的main()?

时间:2017-08-17 00:13:20

标签: c++ windows winapi cmake cross-platform

我最近开始使用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吗?

2 个答案:

答案 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()作为入口点。