Ответ 1
О функциях.
Стандарты C и С++ требуют какой-либо программы (для "размещенной" реализации C или С++) для функции с именем main
, которая служит в качестве функции запуска . Функция main
вызывается после нулевой инициализации нелокальных статических переменных и, возможно, но необязательно (!, С++ 11 § 3.3.2/4), этот вызов происходит после динамическая инициализация таких переменных. Он может иметь одну из следующих подписей:
int main()
int main( int argc, char* argv[] )
плюс возможные определяемые реализацией сигнатуры (С++ 11 §3.6.1/2), за исключением того, что тип результата должен быть int
.
Поскольку единственная такая функция в С++ main
имеет значение по умолчанию, а именно 0. Если main
возвращается, то после возвращения обычной функции exit
с main
значение результата в качестве аргумента. Стандарт определяет три гарантированных значения: 0 (указывает успех), EXIT_SUCCESS
(также указывает успех и обычно определяется как 0) и EXIT_FAILURE
(указывает на сбой), где две именованные константы определяются заголовком <stdlib.h>
, который также объявляет функцию exit
.
Аргументы main
предназначены для представления аргументов командной строки для команды, используемой для запуска процесса. argc
(количество аргументов) - количество элементов массива argv
(аргументов). В дополнение к этим элементам argv[argc]
гарантированно будет 0. Если argc
> 0 – который не гарантируется! – то argv[0]
гарантированно либо быть указателем на пустую строку, либо указателем на "имя, используемое для вызова программы". Это имя может содержать путь, и это может быть имя исполняемого файла.
Использование аргументов main
для получения аргументов командной строки отлично работает в * nix, потому что C и С++ возникли с помощью * nix. Тем не менее, стандарт de facto Windows для кодирования аргументов main
Windows ANSI, который не поддерживает общие имена файлов Windows (например, для норвежской установки Windows, имена файлов с греческими или кириллическими символами). Поэтому Microsoft решила расширить языки C и С++ с помощью функции запуска Windows, называемой wmain
, которая имеет широкие аргументы на основе символов, закодированные как UTF-16, что может представляют любое имя файла.
Функция wmain
может иметь одну из этих подписей, соответствующую стандартным сигнатурам для main
:
int wmain()
int wmain( int argc, wchar_t* argv[] )
плюс еще несколько, которые не особенно полезны.
I.e., 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
является базовым адресом образа памяти исполняемого файла, в основном используемым для загрузки ресурсов из исполняемого файла, и его альтернативно можно получить из функции APIGetModuleHandle
, -
Аргумент
hPrevInstance
всегда равен 0, -
аргумент
lpCmdLine
можно альтернативно получить из функции APIGetCommandLine
, плюс немного странной логики, чтобы пропустить часть имени программы в командной строке и -
значение аргумента
nCmdShow
альтернативно может быть получено с помощью функцииGetStartupInfo
API, но с современными Windows первое создание окна верхнего уровня делает это автоматически, чтобы оно не практично использовалось.
Таким образом, функция WinMain
имеет те же недостатки, что и стандартные main
, плюс некоторые (в частности, многословие и нестандартные), и не имеет никаких преимуществ, поэтому она действительно необъяснима, за исключением, возможно, как поставщика запирающийся предмет. Однако с помощью цепочки инструментов Microsoft он делает компоновщик по умолчанию для подсистемы GUI, что некоторые считают преимуществом. Но с помощью, например, GNU toolchain он не имеет такого эффекта, поэтому на этот эффект нельзя полагаться.
Функция 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
. Другие функции запуска имеют соответствующие функции точки входа, названные одним и тем же систематическим образом.
Примеры использования стандартной функции main
.
Общий исходный код:
foo.cpp
#undef UNICODE
#define UNICODE
#include <windows.h>
int main()
{
MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}
В приведенных ниже примерах (сначала с инструментальной цепочкой GNU, а затем с инструментальной цепочкой Microsoft) эта программа сначала построена как консольная подсистема, а затем в качестве подсистемы подсистемы GUI. Консольная подсистема, или, короче говоря, только консольная программа, - это та, которая требует окна консоли. Это подсистема по умолчанию для всех линкеров Windows, которые я использовал (по общему признанию, не очень много), возможно, для всех периодов линкеров Windows.
Для консольной программы Windows при необходимости автоматически создает окно консоли. Любой процесс Windows, независимо от подсистемы, может иметь связанное окно консоли и не более одного. Кроме того, интерпретатор команд Windows ожидает завершения программной программы консоли, чтобы текст программы был завершен.
И наоборот, программа подсистемы GUI - это та, которая не требует окна консоли. Командный интерпретатор не ждет программы подсистемы GUI, за исключением пакетных файлов. Один из способов избежать ожидания завершения для обоих видов программы - использовать команду start
. Один из способов представления текста окна консоли из программы подсистемы GUI - перенаправить свой стандартный выходной поток. Другой способ - явно создать окно консоли из программного кода.
Программная подсистема закодирована в исполняемом заголовке. Он не отображается в Проводнике Windows (кроме того, что в Windows 9x можно "быстро просмотреть" исполняемый файл, который теперь представляет собой примерно ту же информацию, что и инструмент Microsoft dumpbin
). Не существует соответствующей концепции С++.
main
с помощью инструментальной цепочки GNU.
[D:\dev\test] > g++ foo.cpp [D:\dev\test] > objdump -x a.exe | find /i "subsys" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Subsystem 00000003 (Windows CUI) [544](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [612](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000003 __subsystem__ [636](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D:\dev\test] > g++ foo.cpp -mwindows [D:\dev\test] > objdump -x a.exe | find /i "subsys" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Subsystem 00000002 (Windows GUI) [544](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [612](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000002 __subsystem__ [636](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D:\dev\test] > _
main
с инструментальной цепочкой Microsoft:
[D:\dev\test] > set LINK=/entry:mainCRTStartup [D:\dev\test] > cl foo.cpp user32.lib foo.cpp [D:\dev\test] > dumpbin /headers foo.exe | find /i "subsys" 6.00 subsystem version 3 subsystem (Windows CUI) [D:\dev\test] > cl foo.cpp /link user32.lib /subsystem:windows foo.cpp [D:\dev\test] > dumpbin /headers foo.exe | find /i "subsys" 6.00 subsystem version 2 subsystem (Windows GUI) [D:\dev\test] > _
Примеры использования функции Microsoft
.Следующий основной код является общим как для инструментальной цепочки 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\test] > g++ bar.cpp d:/bin/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.1/../../../libmingw32.a(main.o):main.c:(.text.startup+0xa3): undefined reference to `WinMain @16' collect2.exe: error: ld returned 1 exit status [D:\dev\test] > _
Сообщение об ошибке связи здесь, WinMain
, связано с тем, что инструментальная цепочка GNU поддерживает эту функцию (предположительно, потому что так много древнего кода ее использует), и ищет ее как последнее средство после отказа найти стандартный 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\test] > g++ bar.cpp wmain_support.cpp [D:\dev\test] > objdump -x a.exe | find /i "subsystem" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Subsystem 00000003 (Windows CUI) [13134](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [13576](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000003 __subsystem__ [13689](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D:\dev\test] > g++ bar.cpp wmain_support.cpp -mwindows [D:\dev\test] > objdump -x a.exe | find /i "subsystem" MajorSubsystemVersion 4 MinorSubsystemVersion 0 Subsystem 00000002 (Windows GUI) [13134](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000004 __major_subsystem_version__ [13576](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000002 __subsystem__ [13689](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 __minor_subsystem_version__ [D:\dev\test] > _
wmain
с инструментальной цепочкой Microsoft.
С помощью инструментальной цепочки Microsoft компоновщик автоматически передает точку входа wmainCRTStartup
, если не указана точка входа, и присутствует функция wmain
(неясно, что произойдет, если присутствует стандартный main
, я не проверял что в последние годы):
[D:\dev\test] > set link=/entry:mainCRTStartup [D:\dev\test] > cl bar.cpp user32.lib bar.cpp LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup bar.exe : fatal error LNK1120: 1 unresolved externals [D:\dev\test] > set link= [D:\dev\test] > cl bar.cpp user32.lib bar.cpp [D:\dev\test] > _
При нестандартной функции запуска, такой как wmain
, возможно, лучше всего указать точку входа явно, чтобы было очень ясно о намерении:
[D:\dev\test] > cl bar.cpp /link user32.lib /entry:wmainCRTStartup bar.cpp [D:\dev\test] > dumpbin /headers bar.exe | find /i "subsystem" 6.00 subsystem version 3 subsystem (Windows CUI) [D:\dev\test] > cl bar.cpp /link user32.lib /entry:wmainCRTStartup /subsystem:windows bar.cpp [D:\dev\test] > dumpbin /headers bar.exe | find /i "subsystem" 6.00 subsystem version 2 subsystem (Windows GUI) [D:\dev\test] > _