Pull to refresh

Простой способ перейти от 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
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.