Sources
Delphi Russian Knowledge Base
DRKB - база знаний по Дельфи в рунете, составленная Виталием Невзоровым

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

01.01.2007

Попробуем написать с Вами программу, которая не будет пользоваться 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 - функциями на этом.

Previous page:
Запись сообщений в журнал событий Windows на Delphi
Top:
DRKB
Next page:
Пример приложений на чистом API