Минимальная программа на WinAPI
Минимальная программа на WinAPI
Минимальная программа на WinAPI Источник: http://decoding.narod.ru/api/min/min.html Данная статья начинает серию статей, посвященных программированию в Delphi на Win API. API (Application Program Interface) - это набор функций, которые находятся в стандартных библиотеках (DLL), располагающихся в системном каталоге Windows. Операционная система предоставляет эти функции для использования каждой программе. Что дает нам использование API функций? В первую очередь минимальные размеры получившегося приложения. Это может оказаться важным при написании таких программ как инсталляторы, при создании пачтей или при работе с графикой. Программы маленьких размеров удобно распространять через интернет. Также стоит отметить, что программы, написанные на Win API, работают быстрее, чем их "собратья", написанные с использованием классов объектно-ориентированных языков. Первое знакомство с Win API начнем с того, что создадим окно. Просто пустое окно, которое в дальнейшем будет служить нам шаблоном. Постепенно наращивая этот шаблон, мы будем создавать более сложные приложения. Итак, приступим. Запускаем Delphi и создаем новый проект. Нам необходимо убрать из проекта форму, для этого делаем следующее. В меню Project выбираем Remove from Project..., в появившемся окне выделяем строку Unit1 и нажимаем кнопку OK. Delphi попросит подтвердить, хотим ли мы удалить Unit1 из проекта, ответьте утвердительно. Теперь необходимо открыть файл проекта. Снова лезем в меню Project и выбираем View Source. Последний шаг, нужно удалить несколько лишних строк. Приведите файл проекта к следующему виду. program Project1; begin end. Да, это все, что должно остаться! Перед нами "чистый холст" и мы начинаем творить. Как уже упоминалось выше, API функции располагаются в динамических библиотеках, и чтобы вызвать их оттуда, нам необходимо их описать. К счастью для нас, в Delphi уже имеются модули, в которых описаны многие API функции, нам нужно просто упомянуть их в разделе uses. program Project1; uses Windows, Messages; begin end. Для начала нам хватит этих двух модулей. А мы продолжаем, и переходим к объявлению переменных. Когда в Windows создаётся некоторый объект, ему присваивается уникальный 32-разрядный номер, который называется дескриптором. В дальнейшем при работе с этим объектом каждой функции передаётся этот дескриптор, поэтому нам необходимо его сохранить. Для этого нам понадобится переменная типа HWND. Также потребуется переменная типа TWndClassEx, ее мы используем для того, чтобы описать создаваемый нами объект. И еще одна переменная типа TMsg будет использоваться для обработки сообщений. Наш проект принимает следующий вид. program Project1; uses Windows, Messages; var Wc: TWndClassEx; Wnd: HWND; Msg: TMsg; begin end. Теперь опишем оконную процедуру. function WindowProc( Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall; begin case Msg of WM_DESTROY: begin PostQuitMessage( 0 ); Result := 0; Exit; end; else Result := DefWindowProc( Wnd, Msg, wParam, lParam ); end; end; Эта процедура занимается обработкой сообщений, полученных нашим приложением, и пока выглядит скромно. Когда приложение пытаются закрыть, оно получает сообщение WM_DESTROY. Получив это сообщение мы, закрываем программу. Процедура PostQuitMessage сообщает Windows, что поток, связанный с нашим приложением, сделал запрос на закрытие. Дальше все понятно. Получив любое другое сообщение, мы вызываем оконную процедуру, заданную по умолчанию, чтобы обеспечить обработку сообщения по умолчанию. Другими словами, эта функция гарантирует, что каждое сообщение будет обработано. DefWindowProc вызывается с теми же самыми параметрами, полученными оконной процедурой. Небольшое отступление. Действие процедуры PostQuitMessage аналогично сообщению WM_QUIT, и описанную выше процедуру можно было бы написать следующим образом. function WindowProc( Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM ): LRESULT; stdcall; begin case Msg of WM_DESTROY: begin PostMessage( Wnd, WM_QUIT, 0, 0 ); Result := 0; Exit; end; else Result := DefWindowProc( Wnd, Msg, wParam, lParam ); end; end; Подготовка окончена, и теперь нам пора создавать окно. Последовательность действий будет такая: описать класс создаваемого объекта, зарегистрировать этот класс в системе, создать и показать окно. Приступаем. ... const WndClass = ?TWinApiWnd?; WndCaption = ?Минимальная программа на Win API?; ... begin with Wc do begin cbSize := SizeOf( Wc ); style := CS_HREDRAW or CS_VREDRAW; lpfnWndProc := @WindowProc; cbClsExtra := 0; cbWndExtra := 0; hInstance := hInstance; hIcon := LoadIcon( 0, IDI_APPLICATION ); hCursor := LoadCursor( 0, IDC_ARROW ); hbrBackground := COLOR_WINDOW; lpszMenuName := nil; lpszClassName := WndClass; end; RegisterClassEx( Wc ); Wnd := CreateWindowEx( 0, WndClass, WndCaption, WS_OVERLAPPEDWINDOW, 10, 10, 300, 100, 0, 0, hInstance, nil ); ShowWindow( Wnd, SW_SHOWNORMAL ); while GetMessage( Msg, 0, 0, 0 ) do begin TranslateMessage( Msg ); DispatchMessage( Msg ); end; Halt( Msg.wParam ); end. Теперь я дам краткое описание происходящего. Начнем с описания класс объекта. Флаги CS_HREDRAW и CS_VREDRAW говорят о том, что окно должно перерисовываться при изменении вертикального или горизонтального размера. lpfnWndProc присваиваем адрес созданной нами оконной процедуры. hInstance содержит описатель экземпляра приложения (адрес начала образа exe файла в адресном пространстве). С иконкой, курсором и цветом все понятно. lpszMenuName - указатель на главное меню, которого пока нет. И последнее, lpszClassName - имя класса создаваемого объекта. Забегая немного вперед, обращаю ваше внимание на то, что второй параметр функции CreateWindowEx имеет тоже значение, что и поле lpszClassName описываемого объекта. Они должны быть одинаковыми, поэтому я использую константу, созданную ранее. Это не обязательно, просто мне так удобнее. Используя RegisterClassEx регистрируем описанный класс в системе. Создаем окно. Первый параметр CreateWindowEx - расширенный стиль окна. Далее идут имя класса и заголовок окна. Затем описывается стиль окна. Если посмотреть описание флага WS_OVERLAPPEDWINDOW (оно приведено ниже), мы увидим, что это комбинация нескольких флагов. Другими словами, мы сами можем определять стиль окна, используя различные комбинации, например WS_OVERLAPPED or WS_SYSMENU or WS_MINIMIZEBOX. Советую вам поэкспериментировать с этим параметром. Следующие 4 параметра отвечают за позицию и размер окна. Затем идет дескриптор окна родителя, его у нас нет, так что ставим 0. Меню у нас пока тоже нет, так что дальше тоже ставим 0. Далее идут дескриптор оконной процедуры и указатель на структуру CREATESTRUCT. Создав окно, воспользуемся процедурой ShowWindow, чтобы его показать. {$EXTERNALSYM WS_OVERLAPPEDWINDOW} WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_THICKFRAME or WS_MINIMIZEBOX or WS_MAXIMIZEBOX); Заканчивается все циклом обработки сообщений. Функция TranslateMessage транслирует сообщения виртуальных клавиш в символьные сообщения. Функция DispatchMessage посылает сообщения оконной процедуре. Цикл обработки сообщений может меняться в зависимости от ситуации, но эти 2 процедуры присутствуют всегда.