Как установить точку входа приложения 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, либо как пользовательский шаг после сборки.