Windows — WINMAIN и main () в C ++ (расширенный)

Хорошо, я посмотрел на этот пост: Разница между WinMain, main и DllMain в C ++

Теперь я знаю, что WINMAIN используется для оконных приложений и main() для консолей. Но чтение поста не говорит мне, почему именно, в чем разница.

Я имею в виду, какой смысл разделять различные функции сети для запуска программы? Это связано с проблемами производительности? Или что это?

44

Решение

Стандарты 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 \ тест]
> _
149

Другие решения

По словам @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если он существует

8

Стандартной программе на 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 для большего

2

Я смутно припоминаю, что где-то читал, что программы Windows имеют main() функция. Он просто где-то спрятан в заголовке или библиотеке. Я верю этому main() Функция инициализирует все переменные, необходимые для WinMain() а потом называет это.

Конечно, я новичок в WinAPI, поэтому я надеюсь, что другие, кто более осведомлен, исправят меня, если я ошибаюсь.

1

У меня был 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));
}
0
По вопросам рекламы [email protected]