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

Arn*_*rah 7 c++ windows winapi cross-platform cmake

我最近开始使用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")
Run Code Online (Sandbox Code Playgroud)

有了这个,解决方案工作,并且控制台窗口无法在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
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我绝对厌恶这一点,因为它破坏了使用CMake的全部意义.我不想在我的代码中更改基于不同平台的任何内容.这引出了我的问题.如何main()在制作GUI应用程序时将C++应用程序入口点设置为Windows,而无需在Visual Studio中手动设置它?我可以使用跨平台方法直接在CMake中执行此操作吗?或者我必须使用该#if/#else/#endif解决方案吗?上述解决方案的唯一改进是使用一个宏MAIN_FUNCTION来执行预处理器条件.我也想避免这种情况.

另一方面,还有另一种方法可以摆脱Windows上的GUI应用程序中的控制台窗口,我不知道在不使用WIN32选项的情况下使用CMake吗?

小智 14

解决方法是在set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")之前添加add_executable

它隐藏了控制台,同时仍然允许您将通常的int main()作为入口点。


MSa*_*ers 5

您在这里混淆了两件事,但它们密切相关。

显示的控制台是应用程序的结果,该应用程序的 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 之后完成,也可以作为自定义构建后步骤完成。