Написание программ на чистом WinAPI

Материал из DRKB

Написание программ на чистом WinAPI[править | править код]

Попробуем написать с Вами программу, которая не будет пользоваться VCL, а будет использовать вызовы функций Windows API.

Приложения такого типа нужны, когда размер исполняемого файла является критичным. Например, в инсталяторах, деинсталяторах, самораспаковывающихся архивах и т.п. В крайнем случае, для того чтобы посмотреть какую работу выполняет за нас VCL, и что из себя представляет Windows-программа.

На самом деле все очень просто... Для этого нам необходимо:

1. Зарегистрировать класс окна для окна главной формы.

function InitApplication: Boolean;
var
  wcx: TWndClass;
begin
  // Заполняем структуру TWndClass
  // перерисовываем, если размер изменяется
  wcx.style := CS_HREDRAW or CS_VREDRAW;
  // адрес оконной процедуры
  wcx.lpfnWndProc := @MainWndProc;
  wcx.cbClsExtra := 0;
  wcx.cbWndExtra := 0;
  // handle to instance
  wcx.hInstance := hInstance;
  // загружаем стандандартную иконку
  wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
  // загружаем стандартный курсор
  wcx.hCursor := LoadCursor(0, IDC_ARROW);
  // делаем светло-cерый фон
  wcx.hbrBackground := COLOR_WINDOW;
  // пока нет главного меню
  wcx.lpszMenuName :=  nil;
  // имя класса окна
  wcx.lpszClassName := PChar(WinName);

  // Регистрируем наш класс окна.
  Result := RegisterClass(wcx) <> 0;
end;


2. Написать подпрограмму обработки оконных сообщений.

function MainWndProc(Window: HWnd; AMessage, WParam,
                    LParam: Longint): Longint; stdcall; export;
begin
  // подпрограмма обработки сообщений
  case AMessage of
    WM_DESTROY: begin
      PostQuitMessage(0);
      Exit;
    end;
    else
       Result := DefWindowProc(Window, AMessage, WParam, LParam);
  end;
end;


3. Создать главное окно приложения.

function InitInstance: HWND;
begin
  // Создаем главное окно.
  Result := CreateWindow(
    PChar(WinName),   // имя класса окна
    'Small program',  // заголовок
    WS_OVERLAPPEDWINDOW, // стандартный стиль окна
    // стандартные горизонтальное, вертикальное положение, ширина и высота
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    0, // нет родительского окна
    0, // нет меню
    hInstance, // handle to application instance
    nil);      // no window-creation data
end;


4. Написать тело программы.

var
  hwndMain: HWND;
  AMessage: msg;
begin
  if (not InitApplication) then
  begin
    MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok);
    Exit;
  end;
  hwndMain := InitInstance;
  if (hwndMain = 0) then
  begin
    MessageBox(0, 'Ошибка создания окна', nil, mb_Ok);
    Exit;
  end
  else
  begin
    // Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
    ShowWindow(hwndMain, CmdShow);
    UpdateWindow(hwndMain);
  end;
  while (GetMessage(AMessage, 0, 0, 0)) do
  begin
    // Запускаем цикл обработки сообщений
    TranslateMessage(AMessage);
    DispatchMessage(AMessage);
  end;
  Halt(AMessage.wParam);
end.


5. Запустить программу на исполнение. ;)

Наша программа пока только может немногое - отображать форму, и закрываться после нажатия на кнопку закрытия формы... Но посмотрите на размер исполняемого файла - он больше чем на порядок меньше созданного с использованием VCL.

Полный текст програмы:

program SmallPrg;

uses Windows,  Messages;

const
  WinName = 'MainWClass';

function MainWndProc(Window: HWnd; AMessage, WParam,
                    LParam: Longint): Longint; stdcall; export;
begin
  // подпрограмма обработки сообщений
  case AMessage of
    WM_DESTROY: begin
      PostQuitMessage(0);
      Exit;
    end;
    else
      Result := DefWindowProc(Window, AMessage, WParam, LParam);
  end;
end;

function InitApplication: Boolean;
var
  wcx: TWndClass;
begin
  // Заполняем структуру TWndClass
  // перерисовываем, если размер изменяется
  wcx.style := CS_HREDRAW or CS_VREDRAW;
  // адрес оконной процедуры
  wcx.lpfnWndProc := @MainWndProc;
  wcx.cbClsExtra := 0;
  wcx.cbWndExtra := 0;
  // handle to instance
  wcx.hInstance := hInstance;
  // загружаем стандандартную иконку
  wcx.hIcon := LoadIcon(0, IDI_APPLICATION);
  // загружаем стандартный курсор
  wcx.hCursor := LoadCursor(0, IDC_ARROW);
  // делаем светло-cерый фон
  wcx.hbrBackground := COLOR_WINDOW;
  // пока нет главного меню
  wcx.lpszMenuName :=  nil;
  // имя класса окна
  wcx.lpszClassName := PChar(WinName);

  // Регистрируем наш класс окна.
  Result := RegisterClass(wcx) <> 0;
end;

function InitInstance: HWND;
begin
  // Создаем главное окно.
  Result := CreateWindow(
    PChar(WinName),   // имя класса окна
    'Small program',  // заголовок
    WS_OVERLAPPEDWINDOW, // стандартный стиль окна
    // стандартные горизонтальное, вертикальное положение, ширина и высота
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    Integer(CW_USEDEFAULT),
    0, // нет родительского окна
    0, // нет меню
    hInstance, // handle to application instance
    nil);      // no window-creation data
end;

var
  hwndMain: HWND;
  AMessage: msg;
begin
  if (not InitApplication) then
  begin
    MessageBox(0, 'Ошибка регистрации окна', nil, mb_Ok);
    Exit;
  end;
  hwndMain := InitInstance;
  if (hwndMain = 0) then
  begin
    MessageBox(0, 'Ошибка создания окна', nil, mb_Ok);
    Exit;
  end
  else
  begin
    // Показываем окно и посылаем сообщение WM_PAINT оконной процедуре
    ShowWindow(hwndMain, CmdShow);
    UpdateWindow(hwndMain);
  end;
  while (GetMessage(AMessage, 0, 0, 0)) do
  begin
    // Запускаем цикл обработки сообщений
    TranslateMessage(AMessage);
    DispatchMessage(AMessage);
  end;
  Halt(AMessage.wParam);
end.


Последний раз мы рассмотрели пару ф-й и их особенности(FindWindow, GetNextWindow и GetWindowText), однако несмотря на это были вопросы именно на эту тему - вывод : торопитесь ребята. Старайтесь по максимуму использовать, то что у Вас уже есть.

Итак вдогонку к 12-у выпуску хочется отметить, что при поиске окон, как отмечалось, нужен класс и имя, так вот - если Вы ищите DOS-окно, то его класс всегда = 'tty'.

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

Получить каталог Windows( вдруг при установке Вы назвали его Unix :) ).


var
s1 : array[0..254] of Char;
...
GetWindowsDirectory(s1,255);

В s1 получим искомый путь.

Один момент - не надо описывать s1 просто как PChar, иначе при выполнении получите неприятное сообщение.

Аналогично можно найти и системный каталог. Это тоже важно, поскольку, например для Win9x это 'Windows\System', а для NT 'System32'.

GetSystemDirectory(s1, 255);

255 - это длинна строки. Отдельно подчеркну, что очень рекомендую вместо этого числа ставить переменную Max_Path, содержащую в себе максимальную длину пути в Вашей операционной системе.


Еще очень интересная функция. Она позволяет запретить или разрешить все действия с окном пользователю.

EnableWindow(H: Hwnd, t: Boolean);

Где h-дескриптор окна, если сказать Application.Handle, то свое окно. t=False - запретить действия, True - разрешить.

Ну и все с Api - функциями на этом.


ID: 02013