Как стать автором
Обновить

Простой способ перейти от main к WinMain, а от командной строки грамотно получить список аргументов

Итак, простой пример, мы хотим старый добрый GLUT (или новый злой FreeGLUT) использовать в своём приложении, но при этом мы любим чтобы приложение под Windows запускалось безо всяких странных консолек на заднем плане. (Согласитесь, пользователей зачастую настораживают всякие досообразные консольки в ваших графических приложениях.) Однако при этом вам жизненно необходим привычный, уютный список аргументов функции main( int argc, char* argv[] ). Причём речь не обязательно про GLUT, вам просто порой жизненно необходимо получить список аргументов в кроссплатформенном приложении.

Большинство из нас забивает на частности, пишет консольное приложение, а про флажок -mwindows в GCC даже и не вспоминает. Что между прочим очень неплохо для отладки, но очень и очень зря для релизной сборки!


Итак, начнём с простого, нам потребуется знакомая всем и каждому конструкция, которую мы постепенно будем дополнять:

#ifdef WIN32
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpCmdLine, int nCmdShow )
#else
int main( int argc, char *argv[] )
#endif


Если пока никакого отторжения, значит всё хорошо, но я ещё ничего не сделал, не расслабляйтесь. Нам потребуется две WinAPI функции, одна в другой:
CommandLineToArgvW( GetCommandLineW(), &argc ).

Как нетрудно догадаться GetCommandLineW возвращает командную строку вызова приложения в кодировке UTF-16, то есть на каждый символ уйдёт по 2 байта, что есть особенно хорошо для простого русского работяги-программиста порою принимающего аргументы в родной кириллице.

CommandLineToArgvW принимает первым параметром как раз то, что возвращает GetCommandLineW, вторым параметром в неё должен приходить указатель на заранее заготовленный int argc, по этому указателю будет заполнено число входящих аргументов из командной строки. Возвращает эта функция как раз argv, вот только увы тоже в UTF-16. Но это не беда, справимся. По окончании работы с выделенным массивом argv, полученным в результате, нужно этот массив «локально освободить» функцией LocalFree( argv ).
В общем виде это выглядит так:

LPWSTR* lpArgv = CommandLineToArgvW( GetCommandLineW(), &argc );
. . .
LocalFree( lpArgv );


Вроде бы всё хорошо, в самом простом случае нам «всего навсего» понадобится
1) Выделить argc элементов массива char** argv
2) Выделить wcslen( lpArgv[i] ) + 1 байт под каждый элемент argv[i]
3) Преобразовать UTF-16 в лучшем случае в ASCII char, в худшем в ANSI cp1251 — стандартную кириллицу Windows. В первом случае нам потребуется просто wcstombs, во втором придётся использовать WideCharToMultiByte.

int argc;
char** argv;
{
LPWSTR* lpArgv = CommandLineToArgvW( GetCommandLineW(), &argc );
argv = (char**)malloc( argc*sizeof(char*) );
int size, i = 0;
for( ; i < argc; ++i )
{
size = wcslen( lpArgv[i] ) + 1;
argv[i] = malloc( size );
wcstombs( argv[i], lpArgv[i], size );
}
LocalFree( lpArgv );
}


Разумеется по окончании приложения принципиально необходимо за собой почистить.

{
int i = 0;
for( ; i < argc; ++i )
free( argv[i] );
free( argv );
}


Сводя всё воедино, необходимо не забыть #include <shellapi.h>, а также то, что нам всё это великолепие для отладки не нужно.

#if defined(WIN32) && defined(DEBUG)
#define MAIN_NEED_CLEAR_ARGV
#include <shellapi.h>
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpCmdLine, int nCmdShow )
{
int argc;
char** argv;
{
LPWSTR* lpArgv = CommandLineToArgvW( GetCommandLineW(), &argc );
argv = (char**)malloc( argc*sizeof(char*) );
int size, i = 0;
for( ; i < argc; ++i )
{
size = wcslen( lpArgv[i] ) + 1;
argv[i] = malloc( size );
wcstombs( argv[i], lpArgv[i], size );
}
LocalFree( lpArgv );
}
#else
int main( int argc, char *argv[] )
{
#endif


Введя дополнительное макроопределение MAIN_NEED_CLEAR_ARGV мы облегчаем себе жизнь, варьируя условие проверки для WinMain. Разумеется очистку мы завяжем на этот новый макрос.

#ifdef MAIN_NEED_CLEAR_ARGV
{
int i = 0;
for( ; i < argc; ++i )
free( argv[i] );
free( argv );
}
#endif


Вот и всё. Всё просто, если немного покопаться в неграх MSDN:
CommandLineToArgvW
GetCommandLineW
WideCharToMultiByte
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.