Хорошо, я посмотрел на этот пост: Разница между WinMain, main и DllMain в C ++
Теперь я знаю, что WINMAIN
используется для оконных приложений и main()
для консолей. Но чтение поста не говорит мне, почему именно, в чем разница.
Я имею в виду, какой смысл разделять различные функции сети для запуска программы? Это связано с проблемами производительности? Или что это?
Стандарты C и C ++ требуют, чтобы любая программа (для «размещенной» реализации C или C ++) имела функцию под названием main
, который служит в качестве программы функция запуска. main
функция вызывается после нулевой инициализации нелокальных статических переменных и, возможно, но не обязательно (!, C ++ 11 §3.6.2 / 4), этот вызов происходит после динамическая инициализация таких переменных. Может иметь одну из следующих подписей:
int main()
int main( int argc, char* argv[] )
плюс возможные сигнатуры, определяемые реализацией (C ++ 11 §3.6.1 / 2), за исключением того, что тип результата должен быть int
,
Как единственная такая функция в C ++ main
имеет результат по умолчанию значение, а именно 0. Если main
затем возвращается после обычной функции return exit
называется с main
значение результата в качестве аргумента. Стандарт определяет три значения, которые гарантированно можно использовать: 0 (указывает на успех), EXIT_SUCCESS
(также указывает на успех и обычно определяется как 0), и EXIT_FAILURE
(указывает на сбой), где две именованные константы определяются <stdlib.h>
заголовок, который также объявляет exit
функция.
main
аргументы предназначены для представления аргументы командной строки для команды, используемой для запуска процесса. argc
(количество аргументов) — это количество элементов в argv
(значения аргумента) массив. В дополнение к этим пунктам argv[argc]
гарантированно будет 0. Если argc
> 0 — что не гарантировано! — затем argv[0]
гарантированно либо указатель на пустую строку, либо указатель на «имя, используемое для вызова программы». Это имя может включать путь, и это может быть имя исполняемого файла.
С использованием main
Аргументы для получения аргументов командной строки прекрасно работают в * nix, потому что C и C ++ произошли с * nix. Тем не менее де-факто Стандарт Windows для кодирования main
аргументы Windows ANSI, который не поддерживает общие имена файлов Windows (например, для норвежской установки Windows, имена файлов с греческими или кириллическими символами). Поэтому Microsoft решила расширить языки C и C ++ с помощью специальной для Windows функции запуска wmain
, который имеет широкие символьные аргументы, закодированные как UTF-16, который может представлять любое имя файла.
wmain
функция может иметь одна из этих подписей, соответствующие стандартным подписям для main
:
int wmain()
int wmain( int argc, wchar_t* argv[] )
плюс еще несколько, которые не особенно полезны.
т.е., wmain
прямая широкая символьная замена для main
,
WinMain
char
Основанная функция была введена в Windows, в начале 1980-х годов:
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
);
где CALLBACK
, HINSTANCE
а также LPSTR
определяются <windows.h>
заголовок (LPSTR
просто char*
).
Аргументы:
hInstance
Значением аргумента является базовый адрес образа памяти исполняемого файла, он в основном используется для загрузки ресурсов из исполняемого файла, и его можно альтернативно получить из GetModuleHandle
Функция API,
hPrevInstance
аргумент всегда равен 0,
lpCmdLine
аргумент может быть альтернативно получен из GetCommandLine
API-функция, плюс немного странной логики для пропуска части имени программы в командной строке, и
nCmdShow
значение аргумента может быть альтернативно получено из GetStartupInfo
Функция API, но в современной Windows первое создание окна верхнего уровня делает это автоматически, поэтому оно не имеет никакого практического применения.
Таким образом WinMain
функция имеет те же недостатки, что и стандартная main
плюс некоторые (в частности, многословность и нестандартность), и никаких собственных преимуществ, так что это действительно необъяснимо, разве что как вещь, связывающая поставщиков. Тем не менее, с помощью цепочки инструментов Microsoft он устанавливает компоновщик по умолчанию для подсистемы GUI, что некоторые считают преимуществом. Но, например, с У цепочки инструментов GNU такого эффекта нет, поэтому на него нельзя положиться.
wWinMain
wchar_t
основанная функция — это широкий символьный вариант WinMain
так же, как wmain
широкоформатный вариант стандарта main
:
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PWSTR lpCmdLine,
int nCmdShow
);
где WINAPI
такой же как CALLBACK
, а также PWSTR
это просто wchar_t*
,
Нет веских причин использовать какие-либо нестандартные функции, кроме наименее известных и наименее поддерживаемых из них, а именно wmain
, а затем просто для удобства: что это позволяет избежать использования GetCommandLine
а также CommandLineToArgvW
API-функции для получения аргументов в кодировке UTF-16.
Чтобы компоновщик Microsoft не работал (компоновщик цепочки инструментов GNU не работает), просто установите LINK
переменная среды для /entry:mainCRTStartup
или укажите эту опцию напрямую. Это функция точки входа библиотеки времени выполнения Microsoft, которая после некоторой инициализации вызывает стандарт main
функция. Другие функции запуска имеют соответствующие функции точки входа, названные таким же систематическим образом.
Общий исходный код:
foo.cpp
#undef UNICODE
#define UNICODE
#include <windows.h>
int main()
{
MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}
В приведенных ниже примерах (сначала с помощью цепочки инструментов GNU, а затем с цепочкой инструментов Microsoft) эта программа сначала создается как программа консольной подсистемы, а потом как Программа с графическим интерфейсом. Консольная подсистема, или, короче говоря, просто консольная программа, это тот, который требует консольного окна. Это подсистема по умолчанию для всех линкеров Windows, которые я использовал (правда, не очень много), возможно, для всех периодов линкеров Windows.
Для консольной программы Windows создает окно консоли автоматически при необходимости. Любой процесс Windows, независимо от подсистемы, может иметь связанное окно консоли и самое большее одно. Кроме того, интерпретатор команд Windows ожидает завершения программы консольной программы, чтобы текстовое представление программы завершилось.
И наоборот, программа с графическим интерфейсом не требует консольного окна. Интерпретатор команд не ожидает программы подсистемы GUI, кроме как в пакетных файлах. Один из способов избежать ожидания завершения для обоих видов программ — использовать start
команда. Одним из способов представления текста окна консоли из программы подсистемы GUI является перенаправление стандартного потока вывода. Другой способ — явно создать консольное окно из кода программы.
Подсистема программы закодирована в заголовке исполняемого файла. Это не отображается в проводнике Windows (за исключением того, что в Windows 9x можно было «быстро просмотреть» исполняемый файл, который представлял примерно ту же информацию, что и Microsoft). dumpbin
инструмент сейчас делает). Не существует соответствующей концепции C ++.
main
с помощью цепочки инструментов GNU.[D: \ DEV \ тест] > g ++ foo.cpp [D: \ DEV \ тест] > objdump -x a.exe | найти / я "subsys" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Подсистема 00000003 (Windows CUI) [544] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [612] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsystem__ [636] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D: \ DEV \ тест] > g ++ foo.cpp -mwindows [D: \ DEV \ тест] > objdump -x a.exe | найти / я "subsys" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Подсистема 00000002 (Windows GUI) [544] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [612] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000002 __subsystem__ [636] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D: \ DEV \ тест] > _
main
с помощью набора инструментов Microsoft:[D: \ DEV \ тест] > установить LINK = / запись: mainCRTStartup [D: \ DEV \ тест] > cl foo.cpp user32.lib foo.cpp [D: \ DEV \ тест] > свалка / заголовки foo.exe | найти / я "subsys" 6.00 версия подсистемы 3 подсистема (Windows CUI) [D: \ DEV \ тест] > cl foo.cpp / link user32.lib / subsystem: windows foo.cpp [D: \ DEV \ тест] > свалка / заголовки foo.exe | найти / я "subsys" 6.00 версия подсистемы 2 подсистема (Windows GUI) [D: \ DEV \ тест] > _
Следующий основной код является общим для демонстраций инструментальной цепи GNU и Microsoft:
bar.cpp
#undef UNICODE
#define UNICODE
#include <windows.h>
#include <string> // std::wstring
#include <sstream> // std::wostringstream
using namespace std;
int wmain( int argc, wchar_t* argv[] )
{
wostringstream text;
text << argc - 1 << L" command line arguments:\n";
for( int i = 1; i < argc; ++i )
{
text << "\n[" << argv[i] << "]";
}
MessageBox( 0, text.str().c_str(), argv[0], MB_SETFOREGROUND );
}
wmain
с помощью цепочки инструментов GNU.Набор инструментов GNU не поддерживает Microsoft wmain
функция:
[D: \ DEV \ тест] > g ++ bar.cpp д: / бен / MinGW / бен /../ Библиотека / GCC / i686-PC-mingw32 / 4.7.1 /../../../ libmingw32.a (main.o): main.c :(. text.startup + 0xa3): неопределенная ссылка на `WinMain @ 16' collect2.exe: ошибка: ld вернул 1 состояние выхода [D: \ DEV \ тест] > _
Ссылка на сообщение об ошибке здесь, о WinMain
потому что GNU toolchain поддерживает тот функция (предположительно потому, что так много древнего кода использует его), и ищет его в качестве крайней меры после того, как не может найти стандарт main
,
Тем не менее, добавить модуль со стандартным main
что вызывает wmain
:
wmain_support.cpp
extern int wmain( int, wchar_t** );
#undef UNICODE
#define UNICODE
#include <windows.h> // GetCommandLine, CommandLineToArgvW, LocalFree
#include <stdlib.h> // EXIT_FAILURE
int main()
{
struct Args
{
int n;
wchar_t** p;
~Args() { if( p != 0 ) { ::LocalFree( p ); } }
Args(): p( ::CommandLineToArgvW( ::GetCommandLine(), &n ) ) {}
};
Args args;
if( args.p == 0 )
{
return EXIT_FAILURE;
}
return wmain( args.n, args.p );
}
Сейчас,
[D: \ DEV \ тест] > g ++ bar.cpp wmain_support.cpp [D: \ DEV \ тест] > objdump -x a.exe | найти / я "подсистема" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Подсистема 00000003 (Windows CUI) [13134] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [13576] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsystem__ [13689] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D: \ DEV \ тест] > g ++ bar.cpp wmain_support.cpp -mwindows [D: \ DEV \ тест] > objdump -x a.exe | найти / я "подсистема" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Подсистема 00000002 (Windows GUI) [13134] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [13576] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000002 __subsystem__ [13689] (с -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D: \ DEV \ тест] > _
wmain
с помощью набора инструментов Microsoft.С помощью набора инструментов Microsoft компоновщик автоматически выводит wmainCRTStartup
точка входа, если точка входа не указана и wmain
функция присутствует (неясно, что происходит, если стандарт main
также присутствует, я не проверял это в последние годы):
[D: \ DEV \ тест] > установить ссылку = / запись: mainCRTStartup [D: \ DEV \ тест] > cl bar.cpp user32.lib bar.cpp LIBCMT.lib (crt0.obj): ошибка LNK2019: неразрешенный внешний символ _main, на который ссылается функция ___tmainCRTStartup bar.exe: фатальная ошибка LNK1120: 1 неразрешенная внешность [D: \ DEV \ тест] > установить ссылку = [D: \ DEV \ тест] > cl bar.cpp user32.lib bar.cpp [D: \ DEV \ тест] > _
С нестандартной функцией запуска, такой как wmain
однако, вероятно, лучше всего указать точку входа в явном виде, чтобы иметь четкое представление о намерении:
[D: \ DEV \ тест] > cl bar.cpp / ссылка user32.lib / entry: wmainCRTStartup bar.cpp [D: \ DEV \ тест] > свалка / заголовки bar.exe | найти / я "подсистема" 6.00 версия подсистемы 3 подсистема (Windows CUI) [D: \ DEV \ тест] > cl bar.cpp / ссылка user32.lib / entry: wmainCRTStartup / subsystem: windows bar.cpp [D: \ DEV \ тест] > свалка / заголовки bar.exe | найти / я "подсистема" 6.00 версия подсистемы 2 подсистема (Windows GUI) [D: \ DEV \ тест] > _
По словам @RaymondChen
Название WinMain это просто соглашение
Хотя функция WinMain документирована в Platform SDK, она
не совсем часть платформы. Скорее, WinMain является обычным
имя для предоставленной пользователем точки входа в программу Windows.Реальная точка входа находится в библиотеке времени выполнения C, которая инициализирует
время выполнения, запускает глобальные конструкторы, а затем вызывает ваш WinMain
функция (или wWinMain, если вы предпочитаете точку входа Unicode).
DllMain и WinMain отличаются друг от друга своими прототипами. WinMain принимает аргумент командной строки, а другой говорит о том, как он подключен к процессу.
Согласно Документация MSDN
По умолчанию начальный адрес является именем функции из библиотеки времени выполнения C. Компоновщик выбирает его в соответствии с атрибутами программы, как показано в следующей таблице.
mainCRTStartup
(или же wmainCRTStartup
) Приложение, использующее
/SUBSYSTEM:CONSOLE;
звонит главный (или wmain
)
WinMainCRTStartup
(или же wWinMainCRTStartup
) Приложение, использующее
/SUBSYSTEM:WINDOWS;
звонки WinMain
(или же wWinMain
), который должен быть
определяется с __stdcall
_DllMainCRTStartup
DLL; звонки DllMain
, который должен быть определен с __stdcall
если он существует
Стандартной программе на C передается 2 параметра командной строкой при запуске:
int main( int argc, char** argv ) ;
char** argv
это массив строк (char*
)int argc
это число char*
в argvФункция загрузки WinMain
То, что программисты должны писать для программы Windows, немного отличается. WinMain
принимает 4 параметра, которые передаются программе Win O / S при запуске:
int WINAPI WinMain( HINSTANCE hInstance, // HANDLE TO AN INSTANCE. This is the "handle" to YOUR PROGRAM ITSELF.
HINSTANCE hPrevInstance,// USELESS on modern windows (totally ignore hPrevInstance)
LPSTR szCmdLine, // Command line arguments. similar to argv in standard C programs
int iCmdShow ) // Start window maximized, minimized, etc.
Смотри мою статью Как создать базовое окно в C для большего
Я смутно припоминаю, что где-то читал, что программы Windows имеют main()
функция. Он просто где-то спрятан в заголовке или библиотеке. Я верю этому main()
Функция инициализирует все переменные, необходимые для WinMain()
а потом называет это.
Конечно, я новичок в WinAPI, поэтому я надеюсь, что другие, кто более осведомлен, исправят меня, если я ошибаюсь.
У меня был exe с использованием _tWinMain и Configuration Properties.Linker.System.Subsystem: Windows (/ SUBSYSTEM: WINDOWS). Позже я хотел, чтобы он поддерживал аргументы cmdline и печатал на консоль, поэтому я добавил:
// We need to printf to stdout and we don't have one, so get one
AllocConsole();
// Redirect pre-opened STDOUT to the console
freopen_s((FILE **)stdout, "CONOUT$", "w", stdout);
но это работало только при печати в другом окне консоли, которое ушло, поэтому это было не так полезно. Ниже описан способ, которым я изменил его для работы с Консолью (/ SUBSYSTEM: CONSOLE) таким образом, чтобы я мог переходить туда-сюда, если мне было нужно.
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
UNREFERENCED_PARAMETER(envp);
return (_tWinMain(NULL, NULL, ::GetCommandLineW(), 0));
}