Categories: C/C++

winapi-dev-environment/

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

Предвижу возмущение некоторых читателей сего блога, дескать, что за дела, я не хочу читать про WinAPI, я хочу ФП и юниксы! Если вы оказались одним из них, пожалуйста, пройдите по приведенной выше ссылке и внимательно ознакомьтесь с тамошней заметкой, она вам все объяснит. Хорошая новость для новичков — при помощи серии постов о WinAPI, которую я планирую написать, вы можете начать изучение программирования практически с нуля. Впрочем, прочитать сначала книжку Кернигана и Ритчи , а также иметь представление о том, как язык Си хранит в памяти целые числа и строки, не повредит.

Для написания кода я буду использовать Windows XP, запущенный под Ubuntu Linux в VirtualBox, и среду разработки Visual Studio 2005. Если вы тоже не сидите под Windows, советую выбрать такую же конфигурацию, поскольку более поздние версии Visual Studio и Windows требуют крайне много ресурсов. Обязательно установите гостевые дополнения, это позволит использовать в Windows такое же разрешение экрана, что и в хост-системе. Если даже WinXP для вас тяжеловата, подойдут Windows 2000 и Visual C++ 6.0. Несмотря на то, что я буду использовать не самые свежие Windows и Visual Studio, по идее все должно прекрасно работать и в более поздних их версиях. Кроме того, не должно составить большого труда воспроизвести мои действия в каком-нибудь wxDev-C++ или даже Borland Delphi. Писать будем на Си, возможно, лишь изредка прибегая к некоторым фишкам C++.

Дополнение: Надо отдать должное Microsoft. Самые актуальные на середину 2017-го года Windows 10 и Visual Studio Community довольно шустро работают в условиях ограниченных ресурсов под VirtualBox. Более того, Visual Studio Community доступна для скачивания совершенно бесплатно .

Установить Visual Studio несложно. Будем считать, что с этим вы справились. Давайте создадим новый проект. Запустите Visual Studio и выберите в меню File → New → Project. В появившимся диалоге выберите Visual C++ → Win32 → Win32 Project. В поле Name введите какое-нибудь название проекта, например, First. Жмите OK, Next, Finish. В результате будет создан каркас проекта. Обратите внимание на выпадающий список с конфигурациями проекта по центру экрана. Выберите в нем конфигурацию Release:

Для компиляции программы нажмите F7. Для ее запуска нажмите Ctr+F5. Если вы используете VirtualBox, обратите внимание, что правая клавиша Ctr используется в хоткеях, управляющих виртуальной машиной. Например, сочетание Ctr+F служит для переключения между оконным и полноэкранным режимами. Поэтому, если вы решили использовать VirtualBox, используйте в хоткеях Visual Studio клавишу Ctr, находящуюся слева.

Если теперь перейти в каталог с проектом и найти собранную программу (она находится в подкаталоге с именем release), можно обнаружить, что ее размер составляет 64 Кб. По нынешним меркам это не так уж много, но все-таки что-то многовато для программы, всего лишь рисующей окошко с менюшкой. Мы же собираемся писать на Си и WinAPI, а одним из преимуществ этого подхода является возможность писать очень компактные программки.

Давайте разберемся, как это делается:

  • Закройте текущий проект: File → Close Solution;
  • Создайте новый проект с именем Project, в визарде поставьте галочку Additional Options → Empty Project;
  • Выберите конфигурацию Release;
  • Создайте новый файл исходного кода, сказав Project → Add New Item… → Visual C++ → Code → C++ File (.cpp), с именем Main;
  • Откройте свойства проекта: Project → Project Properies…;
  • Отключите проверку безопасности буфера: C/C++ → Code Generation → Buffer Security Check → No;
  • Сообщите, что мы пишем код на Си: С/C++ → Advanced → Compile As → Compile As C Code;
  • Игнорируем библиотеки по умолчанию: Linker → Input → Ignore All Default Libraries → Yes;
  • В качестве точки входа указываем процедуру main: Linker → Advanced → Entry Point → main;
  • Отключаем отладочную информацию: Linker → Debugger → Generate Debug Info → No;
  • Отключаем генерацию манифеста: Liner → Manifest File → Generate Manifest → No;
  • Жмем Apply, затем OK;

Теперь, если вы напишите в Main.cpp код:

void main ( ) {

}

… и скомпилируете его, то получите абсолютно ничего не делающий исполняемый файл Project.exe, размером ровно 1 Кб. Программа получилась такой маленькой, потому что мы выкинули из нее систему времени исполнения языка C++, а также отладочную информацию и прочий «мусор». Фактически, это минимальный размер исполняемого файла в Windows, поскольку он должен содержать некоторую служебную информацию (DOS-заглушку, PE-заголовок, таблицу секций) и иметь некоторое выравнивание. Можно ужать его еще чуть-чуть, но этим мы рискуем сломать совместимость с некоторыми версиями Windows.

Чтобы при создании каждого нового проекта не повторять описанные выше шаги, закройте проект и сохраните его в архиве с названием типа project_template.zip. Теперь попробуем на основе этого проекта создать программу, которая что-то делает. Разархивируйте проект, переименуйте его каталог из Project в MsgBox, откройте проект в Visual Studio. Введите следующий код:

#include <windows.h>

void main ( ) {
MessageBox ( 0 , L «Hello!» , L «MsgBox» , 0 ) ;
ExitProcess ( 0 ) ;
}

Заметьте, что VisualStudio может дополнять имена процедур, если при их вводе нажать Ctr+пробел. Для сохранения кода используйте сочетание Ctr+S. При вводе аргументов процедур отображается подсказка.

Если теперь вы запустите программу, то увидите примерно следующее:

Размер exe-шника при этом составил 2 Кб. Можно уменьшить его до 1 Кб, добавив в самом начале кода строку:

#pragma comment(linker, «/MERGE:.rdata=.text»)

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

Приведенный выше код довольно прост. Заголовочный файл windows.h содержит объявление процедур, структур и констант, необходимых для работы с WinAPI. Процедура ExitProcess завершает текущий процесс с кодом возврата, переданным в качестве аргумента. Процедура MessageBox отображает окно с сообщением. Первый и последний ее аргументы сейчас не важны. Второй по счету аргумент представляет собой строку с сообщением, а третий — заголовок окна.

Префикс L перед открывающей кавычкой означает, что строка хранится в unicode. В действительности, есть две версии процедуры MessageBox — MessageBoxA, работающая с ascii-строками, и MessageBoxW, работающая со строками в unicode. Аналогичная ситуация характерна и для большинства других процедур WinAPI. По умолчанию в свойствах проекта сказано использовать unicode-версии процедур, поэтому во время сборки проекта компилятор подменяет вызов MessageBox на MessageBoxW. На низком уровне ascii-процедуры представляют собой всего лишь обертки над unicode-версиями (для преобразования строк из unicode в ascii и обратно в Windows используются процедуры MultiByteToWideChar и WideCharToMultiByte ). Работая с процедурами, имена которых заканчиваются на «W», мы получаем более быстрые программы.

В заключение хочется отметить, что приведенный код успешно компилируется MinGW и запускается под Wine:

$ i586-mingw32msvc-gcc -s -DUNICODE msgbox.c -o msgbox.exe
$ wine msgbox.exe

Правда, в результате получается exe-шник, содержащий «мусор», избавление от которого выходит за рамки этого поста.

Собственно, это все, о чем я хотел сегодня рассказать. В качестве домашнего задания можете почитать документацию к MessageBox на MSDN . Попробуйте отобразить окна с различными иконками и кнопками, а также по коду возврата определить, какая из кнопок была нажата.

Как всегда, буду рад ответить на ваши вопросы. И заодно скажите, насколько вообще все это вам интересно?

Дополнение: Пишем простое консольное приложение на чистом WinAPI

admin

Share
Published by
admin
Tags: C/C++

Recent Posts

Apple: история логотипа

Как менялся логотип Apple на протяжении многих лет. Логотип Apple — это не просто символ,…

6 дней ago

Security Boot Fail при загрузке Acer — решение проблемы

Security Boot Fail при загрузке Acer — решение проблемы При загрузке ноутбука Acer с флешки,…

3 недели ago

Ноутбук не включается — варианты решения

Ноутбук не включается — варианты решения Если при попытке включить ноутбук вы обнаруживаете, что он…

3 недели ago

The AC power adapter wattage and type cannot be determined — причины и решение

The AC power adapter wattage and type cannot be determined — причины и решение При…

3 недели ago

Свистит или звенит блок питания компьютера — причины и решения

Свистит или звенит блок питания компьютера — причины и решения Некоторые владельцы ПК могут обратить…

3 недели ago

Мигает Caps Lock на ноутбуке HP — почему и что делать?

Мигает Caps Lock на ноутбуке HP — почему и что делать? При включении ноутбука HP…

3 недели ago