C как использовать winapp вместо main
Перейти к содержимому

C как использовать winapp вместо main

  • автор:

Точка входа в приложение WinMain

Каждая программа Windows включает функцию точки входа с именем WinMain или wWinMain. В следующем коде показана сигнатура для wWinMain:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow); 

Ниже приведены четыре параметра wWinMain .

  • hInstance — это дескриптор экземпляра или дескриптор модуля. Операционная система использует это значение для идентификации исполняемого файла или EXE-файла при загрузке в память. Некоторым функциям Windows требуется дескриптор экземпляра, например для загрузки значков или растровых изображений.
  • hPrevInstance не имеет смысла. Он использовался в 16-разрядной версии Windows, но теперь всегда равен нулю.
  • pCmdLine содержит аргументы командной строки в виде строки Юникода.
  • nCmdShow — это флаг, указывающий, является ли основное окно приложения свернуто, развернуто или отображается в обычном режиме.

Функция возвращает int значение. Операционная система не использует возвращаемое значение, но вы можете использовать его для передачи кода состояния в другую программу.

Соглашение о вызовах, например WINAPI , определяет, как функция получает параметры от вызывающего объекта. Например, соглашение о вызовах определяет порядок отображения параметров в стеке. Обязательно объявите функцию wWinMain , как показано в предыдущем примере.

Функция WinMain аналогична функции wWinMain, за исключением того, что аргументы командной строки передаются в виде строки ANSI. Предпочтительнее использовать строку Юникода. Функцию ANSI WinMain можно использовать, даже если вы скомпилируете программу как Юникод. Чтобы получить копию аргументов командной строки в Юникоде, вызовите функцию GetCommandLine . Эта функция возвращает все аргументы в одной строке. Если вы хотите, чтобы аргументы были массивом в стиле argv, передайте эту строку в CommandLineToArgvW.

Как компилятору известно, что вместо стандартной функции mainвызывается wWinMain? Фактически библиотека среды выполнения Microsoft C (CRT) предоставляет реализацию main , которая вызывает WinMain или wWinMain.

CRT выполняет некоторые дополнительные действия внутри main. Например, он вызывает любые статические инициализаторы до wWinMain. Хотя компоновщик может использовать другую функцию точки входа, при связывании с CRT следует использовать значение по умолчанию. В противном случае код инициализации CRT пропускается, а непредсказуемые результаты, такие как некорректная инициализация глобальных объектов.

В следующем коде показана пустая функция WinMain :

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow)

Теперь, когда у вас есть точка входа и вы понимаете некоторые основные термины и соглашения о программировании, вы готовы создать свою первую программу Windows.

Объясните про функцию main();

Я раньше думал, что функция main должна быть в программе только 1.
Я думал, что для Вин программ нужно писать WinMain вместо main. Ну а main это для консольных.
Но сегодня я убедился что это не так.
Если написать так:

#include windows.h> #include stdio.h> int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int) < MessageBox( 0, "alala", "ololo", MB_OK); return 0; > int main( ) < WinMain( 0, 0, 0, 0); printf( "hello"); return 0; >

Это работает. Хотя мне кажется, что этот код не может работать, т.к. у него 2 MAIN.
Если написать так:

#include windows.h> #include stdio.h> int main( ) < MessageBox( 0, "alala", "ololo", MB_OK); printf( "hello"); return 0; >

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

Обьясните мне об этих функциях?
Какие использовать, если я собираюсь писать кросс платформенный движок?

#1
19:31, 5 ноя 2008

Примерно вот так

#include #include #include "mainwindow.h" int main(int argc, char *argv[]) < QApplication app(argc, argv); CMainWindow mw(NULL); mw.show(); return app.exec(); >#ifdef _WIN32 #include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) < main(__argc, __argv); >#endif

Для Windows компилировать с /Subsystem:Windows

#2
6:15, 6 ноя 2008

oistalker
в WinMain тогда уж вот так:

return main(__argc, __argv);

#3
11:47, 6 ноя 2008

Pokimon
Если в свойствах проекта указано win приложение, то будет использовается winmain, если консольное, то main.
WinApi можно использовать из любых приложений. Отличия — при консольном приложение создается консиольное окно автоматически, при win приложение ничего не создается, для создание окна надо вызывать win api функции.
ВСЕ!
Надеюсь понятно!
Удачной разработки!

#4
12:12, 6 ноя 2008

Не совсем понятно.
Если я сделаю, как написал oistalker, то в Win argc and argv передадутся main()?
И в чем смысл __argc такой записи.

Вообще не плохо, когда создается консоль + окно. В консоль можно много интересного написать + она есть и в Linux

#5
12:49, 6 ноя 2008

> Обьясните мне об этих функциях?

Одной из коренных идеологий C/C++ (в отличие от многих других компиляторов типа Pascal) является принципиальная РАВНОПРАВНОСТЬ модулей в соответствии с полной совсместимостью с объектными файлами (OBJ) компиляции, как это было в ассемблере.
Принцип компиляции упрощенно можно представить себе так:

А) из одного или нескольких исходных файлов (c/cpp) с помощью программы-компилятора собираются объектные файлы .obj в том же количестве. в объектных файлах содержаться данные и код с информацией для линковки — импорт и экспорт адресов (адресами могут быть как функции так и данные, глобальные переменные). Интересным моментом является то, что в ряде случаев не обязательно использовать один и тот же компилятор — можно компилить в разных, лишь бы они генерировали совместимые *.obj-шки. Так раньше спрягали код на ASM и на С, например, в одном проекте. С C++ сложнее, т.к. там из-за конструкторов-деструкторов и манглинга имен спряжение получается больше односторонним.

Б) из *.obj файлов с возможной (но не обязательной) добавкой дополнительных внешних *.obj файлов (обычно это библиотеки) с помощью программы-линкера (linker.exe) собирается уже рабочий EXE-шник путем склеивания *.obj-ешек в единый материал, коррекция импортных и экспортных адресов на получившийся снимок памяти и добавления EXE-заголовка.

На этапе (Б) имеет место одна очень важная фигня, называемая Entry Point. Это собственно точка входа в приложение — тот адрес кода в памяти откуда ОС загрузив EXE-шник будет запускать программу.
С помощью ключей компиляции можно задавать Entry Point вручную, что используется для полухакерских вещей типа создания EXE-шников размером 512 байт. Может показаться, что main и есть entry point для C/C++ программы. Но это не так.

Так же на этапе (Б) имеет место еще одна очень важная фигня, называемая runtime library (RTL). Дело тут вот в чём — обычно, любой C/C++ программе доступны некоторые функции, которые находятся в неких *.obj-шках (либах), которые мы явно как бы не подключаем к проекту (include — это не то). Это такие вещи как malloc или strcmp в C и new/delete в C++. Откуда же берутся и подключаются *.obj-шки с этими вещами? На самом деле действительно среда разработки запускает линкер неявно давая ему в параметрах ссылки на ряд *.obj-шек (заключенных в *.lib-ки как правило), в которых эти вещи и находятся.

И вот на этом вот этапе так же и указывается (неявно средой разработки) entry point и entry point этот не main(), а некая другая функция, располагающаяся в недрах RTL, которая и является истинной entry point программы. Данная вещь нужна для того, чтобы привести некоторую инфраструктуру программы в рабочее состояние (например в C++ вызвать конструкторы глобальных объектов, а в C/C++ инициализировать те массивы char *argv[], которые будут переданы как параметры в main()).
И вот после всей этой инициализации эта некоторая «реальная» entry-point функция в недрах RTL и вызывает нашу main()!
Интересно тут то, что вот это вот слово main ни в коем случае не является каким-то ключевым или зарезервированным словом программы — это просто тупо соглашение о том, что в недрах RTL (*.obj) будет вызвана функция с именем main. Косвенным фактом это подтверждающим, является то, что если попытатся скомпилить программу без функции main компилер не ругнется ни разу! Ругнется ЛИНКЕР на то, что не может слинковать какую-нибудь crt0xdgb.obj (это и будет наша RTL) так как не видит нигде импортируемую в неё функцию main().

А теперь возвращаемся в самое начало — «одной из коренных идеологий C/C++ является принципиальная РАВНОПРАВНОСТЬ модулей». Это означает что и RTL и твои модули c/cpp и все другие в принципе равноправны с точки зрения компиляторов и линкера. Неравноправность тут только в одном — в опциях компилятора, в его настройках, которые неявно для нас подставляют ЛИНКЕРУ на линковку те или иные модули по умолчанию, в том числе тот или иной RTL.
Что значит «тот или иной RTL?». А тут всё просто — в зависимости от того для какой платформы (Win/Console/DLL) ты компилишь компилятор указывает линкеру совершенно РАЗНЫЕ lib-ки RTL-ей для линковки по умолчанию. И в этих RTL-ках могут быть разные вызовы твоих «main»-ов.
Например в Win32 RTL это будет вызов WinMain().
В Console RTL это будет main().
В DLL RTL это будет DLLMain().

Ничего страшного не будет, если ты рядом определишь main и WinMain — это не ключевые и не зарезервированные слова компилятора. Просто в зависимости от того какая RTL по итогам сборки проекта будет подключена — она вызовет ту или другую, а может и ругнется от того что не окажется нужной ей DLLMain =).

#6
13:55, 6 ноя 2008

Pokimon
>Не совсем понятно.
>Если я сделаю, как написал oistalker, то в Win argc and argv передадутся
>main()?
>И в чем смысл __argc такой записи.

__argc и __argv это просто глобальные переменные из RTL, которые являются тем самым что передает RTL в main.

>Вообще не плохо, когда создается консоль + окно. В консоль можно много
>интересного написать + она есть и в Linux

Это да, но для этих целей уж лучше лог, тем более что в релизе консоль надо убирать.

#7
15:37, 6 ноя 2008

=A=L=X=
спасибо, узнал много нового 🙂 Только не понятен один момент,
я как-то где-то читал, что Console/Win отличаются между собой ещё
и битом в заголовке .exe файла. Зачем оно надо ? Для простоты
идентификации файла ?

#8
17:19, 6 ноя 2008

=A=L=X=
спасибо. позновательно 8)

#9
17:43, 6 ноя 2008

=A=L=X=
Хорошо написано я 2 раза прочитал.

Может расскажите еще про статические функции и синглот.
Я начал писать свой движок заново на С++ вместо С.
Сейчас пишу BCore.
Я применил к нему паттерн синглторн. Пол дня читал, про паттеры, в итоге идея мне даже понравилась.
Вот так у меня получилось.

class BCore //my first singleton here < public: /*singleton functions*/ static BCore* New( void); void Delete( void); /*load resource and init engine*/ void InitEngine( void); /*start main loop*/ void Start( void); /*unload resource*/ void DestroyEngine( void); protected: BCore( ); ~BCore( ); private: /*singleton*/ static BCore* init; static int links; /*default window width and height*/ static const int WindowWidth = 800; static const int WindowHeight = 600; /*Всякие функции и переменные*/ /* . */ /*Working with messages*/ static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wpar, LPARAM lpar); >;

Это версия для WIN32 все работает.
НО мне нужно использовать из WndProc функции и переменные моего класса.
Но WndProc объявлена как статичная и компилятор ругается.
Я немного подумал и написал так:

private: static BCore *p;
p->функция

Все работает, но я не уверен правильно ли я поступил?

#10
17:49, 6 ноя 2008

watchtower
>=A=L=X=
>спасибо, узнал много нового 🙂 Только не понятен один момент,
>я как-то где-то читал, что Console/Win отличаются между собой ещё
>и битом в заголовке .exe файла. Зачем оно надо ? Для простоты
>идентификации файла ?

ilusha_nil же сказал уже «Отличия — при консольном приложение создается консиольное окно автоматически, при win приложение ничего не создается, для создание окна надо вызывать win api функции.». Эта штука никакого отношения к RTL не имеет — это галочка для самой ОС и вещи типа создания консольного окна делает она еще до запуска entry point.

#11
17:57, 6 ноя 2008

Pokimon
Совет тебе на будущее — паттерны это замечательно, но не надо их
пихать всюду, куда только можно. К тому же не стоит забывать, что
паттерны в ОО программировании помогают решать проблемы, которых
вне ОО программирования просто не существует. Есть конечно
«фундаментальные» паттерны, если можно так выразиться (итераторы, итп.).
Короче, про паттерны нужно почитать, и забыть, оно само всплывёт из памяти
в нужный момент, если надо.

ИМХО: синглтон — не труъ

Про ключевое слово static лучше поищи сам в интернете (просто долго тут писать),
оно в разном контексте может означать довольно разные вещи, а именно, поищи инфу про:

— статические переменные, члены класса
— статические переменные в функциях
— статические функции
— статические функции, члены класса

по моему ещё что то было

Как использовать main() вместо WinMain?

Я хочу написать GUI приложение для Windows, но мне не нравится что MS Visual Studio для этого надо писать WinMain . Хочу писать привычную main() или main(argc, argv) — как это сделать?

Отслеживать
задан 26 апр 2015 в 20:46
31k 13 13 золотых знаков 96 96 серебряных знаков 157 157 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Прежде всего надо понимать, что ни main() , ни WinMain() — не являются «точками входа» .exe файла. Точкой входа (entry point) является некоторая функция, которая будет вызвана операционной системой при старте приложения. В С++ это функция рантайма (CRT), которая проводит инициализацию CRT, вызывает конструкторы глобальных объектов, и затем уже вызывает main или WinMain .

Линкеру передается параметр /subsystem , который задает тип приложения, например /SUBSYSTEM:WINDOWS для GUI приложений, или /SUBSYSTEM:CONSOLE для консольных приложений. В первую очередь это нужно операционной системе — тип приложения прописывается в заголовке .exe файла, и по нему ОС понимает надо ли создавать окно консоли, или нет. Также, при помощи этого параметра линкер выбирает точку входа, которая будет использована по-умолчанию:

  • mainCRTStartup (или wmainCRTStartup ) — при использовании /SUBSYSTEM:CONSOLE . Вызывает main (или wmain )
  • WinMainCRTStartup (или wWinMainCRTStartup ) — при использовании /SUBSYSTEM:WINDOWS . Вызывает WinMain (или wWinMain ).

Точку входа можно переопределить, передав линкеру параметр /entry . Для того чтобы использовать main в GUI приложении, мы должны вызвать линкер с параметрами /SUBSYSTEM:WINDOWS и /ENTRY:mainCRTStartup . Тогда ОС не будет создавать окно консоли, а CRT будет вызывать функцию main .

Значение параметра /entry можно переопределить в опциях проекта MSVS (linker > advanced > entry point). Либо его можно написать прямо в коде программы, с помощью pragma comment :

#include #pragma comment(linker, "/entry:mainCRTStartup") int main()

Капля здравого смысла для Windows-разработки на C и C++

Суровая действительность разработки на C и C++ для Windows такова: для этой платформы никогда не существовало качественной, нативной реализации стандартной библиотеки этих языков. Стандартная библиотека должна абстрагировать механизмы базовой системы ради упрощения разработки переносимого программного обеспечения. С и C++ на Windows очень плохо состыкованы с интерфейсами операционной системы. В результате большая часть переносимых, или, так сказать, «почти всегда переносимых» программ, которые отлично работают практически везде, в Windows оказываются едва заметно «поломанными», в особенности — за пределами англоговорящего мира. Причины этого почти наверняка связаны с политикой тех или иных компаний, с искусственными ограничениями, а не с техническими особенностями систем, что лишь усугубляет положение. Эта статья посвящена рассказу о проблемах Windows-разработки на C и C++ и о том, как они выражаются. Здесь же будут представлены некоторые простые методы борьбы с этими проблемами при разработке переносимого ПО.

Существует множество реализаций компилятора С. Как такое может быть, что все они не в порядке, даже те, что были созданы одними из первых? Библиотека времени выполнения Microsoft C определила то, как стандартная библиотека C должна работать в Windows, а все остальные реализации ради совместимости следовали за ней. Исключением я считаю платформу Cygwin и её главный форк — MSYS2, несмотря на то, что они не унаследовали описываемых недостатков. Они, в ходе эволюции, так сильно изменились, что, по сути, представляют собой совершенно новые платформы, которые нельзя назвать полностью соответствующими «обычной» Windows.

На практике стандартные библиотеки C++ реализованы на базе стандартной библиотеки C. Именно поэтому у C++ имеются те же проблемы, что и у C. CPython избегает этих проблем. Хотя эта реализация Python и написана на C, на Windows она обходит неправильную стандартную библиотеку C и напрямую обращается к проприетарным интерфейсам. Реализации других языков программирования, вроде gc в случае с Go, попросту создаются не на базе механизмов C, вместо этого, с самого начала, делая всё как нужно, поступая так, как библиотекам времени выполнения C стоило бы поступать уже давно.

Если вы работаете над единственным крупным проектом, обход библиотек времени выполнения C — это не так уж и сложно. И вы, вероятно, уже так и поступаете, обращаясь к важному функционалу платформы. Вам, по правде, даже и не нужна библиотека времени выполнения C. Но если вы заняты разработкой множества небольших программ (как я), то написание особого кода для их поддержки в Windows быстро станет основной частью вашей работы. И, откровенно говоря, обеспечение поддержки Windows не стоит подобных усилий. Я пришёл к тому, что чаще всего просто принимаю то, что мне предлагают по умолчанию, хотя и знаю, что это приведёт к проблемам в Windows.

Прежде чем мы перейдём к деталям, хочу кое-что предложить тем, кто хочет легко и быстро решить вышеозначенные проблемы при работе с набором инструментов Mingw-w64, включая w64devkit. Это — моя библиотека libwinsane. Благодаря ей ваши консольные программы, написанные на C и C++, будут работать правильно. Она решает все проблемы, о которых идёт речь в этой статье, за исключением одной. При этом для применения этой библиотеки менять ваш исходный код не нужно. Достаточно просто связать её с вашей программой.

Что конкретно работает неправильно?

Существует две разновидности Windows API: «узкий», с суффиксом A (ANSI), и «широкий» (Unicode, UTF-16) с суффиксом W . Первый — это устаревший API, где активная кодовая страница отображает 256 байт на 256 конкретных символов (поддерживается до 256 символов). На типичных компьютерах, настроенных на работу с европейскими языками, это означает применение кодовой страницы Windows-1252. Грубо говоря, внутри Windows используется кодировка UTF-16, а вызовы, выполняемые через «узкий» интерфейс используют активную кодовую страницу для перевода «узких» строк в «широкие». В результате у обращений к «узкому» API есть лишь ограниченный доступ к системе.

Кодировка UTF-8 изобретена в 1992 году, она была стандартизирована в январе 1993 года. В последующие годы мир Unix принял эту кодировку из-за её обратной совместимости с существующими интерфейсами. Программы могли читать и записывать Unicode-данные, могли пользоваться Unicode-путями, обрабатывать Unicode-аргументы, считывать значения Unicode-переменных окружения и устанавливать их значения. При этом в программах ничего не нужно было менять. В наши дни кодировка UTF-8 стала самым распространённым форматом кодирования текстовой информации. Во многом это так благодаря развитию Всемирной паутины.

В июле 1993 года Microsoft, с выходом Windows NT 3.1, представила «широкий» API Windows, сделав ставку на кодировку UCS-2 (позже — UTF-16), а не на UTF-8. Это, как оказалось, было ошибкой, так как UTF-16 практически во всём уступает UTF-8. Правда, надо признать, что тогда некоторые проблемы не были особенно очевидными.

Главная проблема заключается в том, что стандартные библиотеки C и C++ подключены лишь к «узкому» интерфейсу Windows. Стандартная библиотека, а значит — и типичное переносимое приложение на Windows, не может обрабатывать ничего кроме ASCII-кода. Это приводит к тому, что эти программы не могут выполнять следующие действия в том случае, если они предусматривают применение символов, отличных от ASCII-символов:

  • Принимать аргументы.
  • Читать и устанавливать значения переменных окружения.
  • Работать с путями.
  • Считывать и выводить данные в консоли.

Как решить почти все проблемы поддержки Unicode?

В 2019 году Microsoft представила возможность, позволяющую программам при запуске запрашивать UTF-8 в роли их активной кодовой страницы. Большее, чем раньше, количество функций «узкого» API получило поддержку UTF-8. Это похоже на тот «переключатель», о котором я мечтал, но мою радость несколько омрачает то, что для реализации этих возможностей надо особенным образом встраивать в бинарники некоторый объём неприглядного XML-кода. Но теперь у нас, по крайней мере, появилась некая стандартная возможность работать с UTF-8.

При использовании Mingw-64 это означает необходимость создания такого файла ресурсов:

#include CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "utf8.xml" 

Далее, компилируем это с помощью windres :

$ windres -o manifest.o manifest.rc 

То, что получилось, связываем с программой. Это, удивительным образом, обычно работает! Программы могут получать доступ к Unicode-аргументам, могут работать с переменными окружения, с путями (в том числе — с помощью fopen ). В общем, всё работает так же, как, уже десятилетия, работает на других платформах. Так как активная кодовая страница устанавливается в момент загрузки программы, это событие происходит до конструирования argv (из GetCommandLineA ), и именно поэтому всё это и работоспособно.

В качестве альтернативы можно создать так называемую «параллельную сборку» (SxS, side-by-side assembly), поместив этот XML-код в файл с тем же именем, что и у EXE-файла, но с расширением .manifest (после расширения .exe ), а после этого положив этот файл около EXE-файла. Пользуясь этим приёмом, стоит помнить о существовании SxS-кеша (WinSxS). Изменения в соответствующих файлах могут быть видны лишь через некоторое время после их выполнения.

При использовании описываемого метода, правда, не работает консольный ввод/вывод. Консоль является, по отношению к процессу, внешней сущностью, поэтому на неё требования процесса к активной кодовой странице не распространяются. Её нужно особо настраивать, пользуясь проприетарным вызовом:

SetConsoleOutputCP(CP_UTF8); 

Это, конечно, так себе занятие, но, по крайней мере, теперь всё не так плохо, как раньше. Правда, эта настройка имеет отношение лишь к выводу данных. То есть — программы могут писать в консоль, пользуясь UTF-8, но не читать из консоли. К сожалению, возможность чтения UTF-8-текстов из консоли всё ещё не работает. При установке кодовой страницы для входных данных нам сообщают об успешном проведении операции, но этим всё и ограничивается.

SetConsoleCP(CP_UTF8); // не работает 

Если вам нужно читать Unicode-данные из консоли в интерактивном режиме — вам не остаётся ничего кроме обхода библиотеки времени выполнения C, так как вышеописанный механизм всё ещё неработоспособен.

Преобразование текстовых потоков

Ещё одна давняя проблема программирования для Windows на C и C++ заключается в наличии отличающихся друг от друга «текстовых» и «двоичных» потоков, унаследованных от DOS. В основном это означает автоматическое преобразование символов перевода строки (CRLF и LF). Стандарт C это недвусмысленно позволяет, но на Unix-подобных платформах никогда не делалось различия между текстовыми и двоичными потоками.

Стандарт, кроме того, указывает на то, что стандартные потоки ввода, вывода и ошибок открываются в виде текстовых потоков, при этом нет переносимого способа переключить поток в двоичный режим. Это серьёзный недостаток стандарта. В Unix-подобных системах это неважно, но в Windows это означает, что программа не может читать или писать двоичные данные при работе со стандартными потоками, не вызывая нестандартные функции. Это означает ещё и то, что чтение стандартных потоков и запись в них выполняются медленно. Часто это становится узким местом программ, если только создатель программы не обойдёт этот недостаток.

Лично я предпочитаю писать двоичные данные в стандартный поток вывода, в том числе — видеоданные, и иногда пользуюсь двоичными фильтрами, которые тоже читают двоичные входные данные. Я делаю это так часто, что, вероятно, в половине моих C-программ, в функции main , имеется такой фрагмент кода, обеспечивающий их правильную работу в Windows:

 #ifdef _WIN32 int _setmode(int, int); _setmode(0, 0x8000); _setmode(1, 0x8000); #endif 

Эта магическая формула устанавливает стандартные потоки ввода и вывода в библиотеке времени выполнения C в двоичный режим. При этом не нужны никакие заголовочные файлы, код получается компактным, простым и самодостаточным.

Вышеописанное встроенное преобразование символов перевода строки, а также стандартный текстовый редактор Windows, Notepad, сильно отстающий от других систем, стали причиной того, что многие другие программы, включая Git, создали собственные, вызывающие неудобства, «полезные» возможности по преобразованию символов перевода строки, которые привели к другим проблемам.

Библиотека libwinsane

В начале статьи я говорил о моей библиотеке libwinsane . Она позволяет исправить все вышеописанные проблемы путём простого связывания её с программой. Сюда входит использование раздела .rsrc XML-манифеста, настройка консоли на вывод UTF-8-текстов, перевод стандартных потоков в двоичный режим. Всё это выполняется до вызова функции main (с помощью конструктора GCC). Я называю мою разработку «библиотекой», но это, на самом деле, всего лишь объектный файл. Эта разработка не может быть представлена в виде статической библиотеки, так как она должна быть связана с программой, несмотря на то, что в коде программы она нигде не упоминается.

#include #include int main(int argc, char **argv)

В обычных условиях её компилируют и запускают так:

C:\>cc -o example example.c C:\>example π 1 p 

Как всегда, Unicode-аргументы по-тихому ужимаются до одного байта. А теперь свяжем эту программу с libwinsane :

C:\>gcc -o example example.c libwinsane.o C:\>example π 2 π 

Это приведёт к тому, что в Windows программа заработает так же, как на любой другой платформе.

Если вы занимаетесь поддержкой достаточно крупной программы, то вы, возможно, решите внедрить в свой проект необходимые вам части libwinsane , вместо того, чтобы постоянно связывать его с представленным объектным файлом, который даже не используется в самой программе. Причины его существования заключаются в основном в удобстве использования и в том, чтобы сжато продемонстрировать мои идеи. А в своей версии моего кода вы можете даже решить организовать обработку управляющих последовательностей ANSI.

С какими проблемами вы сталкивались, программируя для Windows на C и C++?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *