Как установить точку входа приложения C++ в main() в Windows, используя CMake?

Я недавно начал использовать CMake и пытался создать приложение с графическим интерфейсом, у которого нет окна консоли в 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. Я не хочу ничего менять в своем коде, основанном на разных платформах. Это приводит меня к моему вопросу. Как установить точку входа приложения C++ в main() в Windows при создании приложения с графическим интерфейсом без необходимости устанавливать его вручную в Visual Studio? Могу ли я сделать это напрямую в CMake, используя кросс-платформенный метод? Или мне придется использовать #if/#else/#endif решение? Единственное улучшение решения выше - использование макроса MAIN_FUNCTION что делает препроцессор условным. Я тоже хочу этого избежать.

С другой стороны, есть ли другой способ избавиться от окна консоли в приложении с графическим интерфейсом в Windows, о котором я не знал об использовании CMake без использования опции WIN32?

2 ответа

Решение

Решение состоит в том, чтобы добавить set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") до add_executable

Он скрывает консоль, позволяя при этом иметь обычный int main() в качестве точки входа.

Вы путаете две вещи здесь, но они тесно связаны.

Появляющаяся консоль является результатом приложения с заголовком Win32 IMAGE_OPTIONAL_HEADER::Subsystem ценность WINDOWS_CUI вместо WINDOWS_GUI, Это вещь Win32, и она применяется ко всем исполняемым файлам независимо от языка, на котором они написаны.

Подпись точки входа - это выбор, специфичный для компилятора. Это функция входа, вызываемая языковой средой выполнения, а не ОС. ОС вызывает функцию входа языковой среды выполнения, которая сначала инициализирует эту среду выполнения, а затем передает управление вашей точке входа.

Теперь компилятор VC++ использует CRT в качестве среды выполнения. И эта среда выполнения CRT действительно использует две разные подписи для вашей точки входа. Очевидно, что реализация std::cin должен работать с WINDOWS_CUIЭто своего рода интерфейс пользовательского интерфейса командной строки. Но тот же CRT также работает с WINDOWS_GUI,

Здесь вещи становятся сложными. Вы можете изменить Subsystem скомпилированного приложения из CUI в GUI. ЭЛТ не будет возражать, он совместим с обеими подсистемами. Но поскольку это делается для скомпилированного приложения, вызов из одной части приложения (запуск CRT) в другую (ваша точка входа) не затрагивается. Это изменение в Win32, а не в C++.

Чтобы вернуться к CMake: это изменение подсистемы может быть сделано либо после CMake, либо как пользовательский шаг после сборки.

Другие вопросы по тегам