Выпуск 12: Заканчиваем программу на WinApi

 

Архив рассылки

Доброго времени суток! :)

Для новых подписчиков я должен повторить: Сначала прочитайте все предыдущие выпуски, которые находятся в архиве рассылки!

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

Delphi:

uses
  Windows, // Модуль для работы с окном
  Messages; // Модуль для работы с сообщениями

var
  Wc: TWndClassEx; // Переменная для определения класса окна
  Wnd: HWND; // Ссылка на окно
  Msg: TMsg; // Переменная для хранения сообщений

(*
  Оконная процедура
  Её задача "ловить" сообщения и правильно на них отреагировать
  Тип переменной для сообщений отличается от того, который использовался среди
  глобальных переменных
*)

function WndProc(wnd: HWND; Msg: UINT;
                 wParam: WPARAM; lParam: LPARAM): lResult; stdcall;
begin
 if Msg = WM_DESTROY // Обработка сообщения "уничтожения" приложения
  then
   begin
    PostQuitMessage(0); // Выход из программы
    Result:= 0; // Сообщает программе, что всё прошло отлично
   end
  else // Остальные сообщения обрабатываются по умолчанию
   Result:= DefWindowProc(wnd, Msg, wParam, lParam);
end;

begin
 Wc.Style:= CS_VREDRAW or CS_HREDRAW;
 Wc.hIcon:= LoadIcon(0, IDI_APPLICATION);
 Wc.hCursor:= LoadCursor(0, IDC_ARROW);
end.

C:

#include < windows.h > // Модуль для работы с окном

WNDCLASSEX Wc; // Переменная для определения класса окна
HWND Wnd; // Ссылка на окно
MSG Msg; // Переменная для хранения сообщений

/*
  Оконная процедура.
  Её задача "ловить" сообщения и правильно на них отреагировать
  Тип переменной для сообщений отличается от того, который использовался среди
  глобальных переменных
*/

LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
     if(msg == WM_DESTROY) // Обработка сообщения "уничтожения" приложения
     {
         PostQuitMessage(0); // Выход из программы
         return 0; // Сообщает программе, что всё прошло отлично
     }
     else // Остальные сообщения обрабатываются по умолчанию
      return DefWindowProc(wnd, msg, wParam, lParam);
}

int WINAPI WinMain()
{
     Wc.Style = CS_VREDRAW || CS_HREDRAW;
     Wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
     Wc.hCursor = LoadCursor(NULL, IDC_ARROW);
}

Теперь можно продолжить.

Значит, что здесь произошло. Пиктограмма указывается свойством hIcon, а курсор - hCursor. Дальше идёт функция, которая и загружает пиктограмму/курсор. Названия функций соответствуют объекту (курсору/пиктограмме). Первый параметр здесь не нужен, по этому ставим ноль. Но не везде можно поставить ноль. Всё зависит от типа аргумента. В данном случае ноль заменяет NULL (в Си, а в Дельфи nil). Следущий параметр - сама пиктограмма/курсор. Пиктограмма является стандартной, а курсор - стрелка. Чтобы увидеть больше вариантов, напишите IDI_ (для пиктограммы, от Icon ID), или IDC_ (для курсора, от Cursor ID), и нажмите Ctrl + пробел. А если хотите выбрать объект из папки, напишите полный путь к файлу в соответсвующих скобках (в зависимосьти от языка). Пиктограмма, которую мы используем, является большой. Можно ещё указать и маленькую (которая бывает в SysTray, или левом верхнем углу окна). Для этого надо изменить свойство hIconSm (handle Small Icon).

Следущее свойство - описатель екземпляра программы (адрес начала образа exe файла в адресном пространстве). Не берите это глубоко к сердцу :)

Delphi:

begin
 Wc.Style:= CS_VREDRAW or CS_HREDRAW;
 Wc.hIcon:= LoadIcon(0, IDI_APPLICATION);
 Wc.hCursor:= LoadCursor(0, IDC_ARROW);
 Wc.hInstance:= hInstance;
end.

C:

int WINAPI WinMain(HINSTANCE hInstance)
{
     Wc.Style = CS_VREDRAW || CS_HREDRAW;
     Wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
     Wc.hCursor = LoadCursor(NULL, IDC_ARROW);
     Wc.hInstance = hInstance;
}

В Дельфи сразу можно указать значение. Вернее, мы полностью значение определять не будем. Это сделает за нас Windows. Но в Си сначала надо создать переменную этого значения. Тут предётся создать ещё несколько переменных. Везде есть свои плюсы и минусы :) Создадим всё сейчас.

C:

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

Объясню что они делают, когда до них доберёмся.

Честно говоря, всё это скукота! Я не люблю долго работать и не видеть результатов. Пишу этот выпуск с всё меньшим энтузиазмом. Код программы, хоть и не сложный, но сложно объясняемый. Самое главное, что нельзя посмотреть, как всё выглядит, не закончив кода! Когда будем создавать окно OpenGL, я не буду сразу всё объяснять! Можно умереть от скукоты!!! :) Всё будет объяснятся постепенно.

Перейдём к делу. Вот весь код программы.

Delphi:

uses
  Windows, // Модуль для работы с окном
  Messages; // Модуль для работы с сообщениями

const WndClass = 'WndApi'; // Имя класса
      WndCaption = 'Добро пожаловать!'; // Заголовок окна

var
  Wc: TWndClassEx; // Переменная для определения класса окна
  Wnd: HWND; // Ссылка на окно
  Msg: TMsg; // Переменная для хранения сообщений

(*
  Оконная процедура
  Её задача "ловить" сообщения и правильно на них отреагировать
  Тип переменной для сообщений отличается от того, который использовался среди
  глобальных переменных
*)

function WndProc( wnd: HWND; Msg: UINT;
                 wParam: WPARAM; lParam: LPARAM ): lResult; stdcall;
begin

 if Msg = WM_DESTROY // Обработка сообщения "уничтожения" приложения
  then
   begin
    PostQuitMessage( 0 ); // Выход из программы
    Result:= 0; // Сообщает программе, что всё прошло отлично
   end
  else // Остальные сообщения обрабатываются по умолчанию
    Result:= DefWindowProc(wnd, Msg, wParam, lParam);

end;

begin

 with Wc do
  begin
   Style:= CS_VREDRAW or CS_HREDRAW; // Стиль окна
   hIcon:= LoadIcon( 0, IDI_APPLICATION ); // Пиктограмма
   hIconSm:= LoadIcon( 0, IDI_APPLICATION ); // Маленькая пиктограмма
   hCursor:= LoadCursor( 0, IDC_ARROW ); // Курсор
   hInstance:= hInstance; // Экземпляр программы
   cbSize:= SizeOf( WndClassEx ); // Размер структуры
   cbClsExtra:= 0; // Дополнительная память, используемая Windows
   cbWndExtra:= 0; // Дополнительная память, используемая Windows
   hbrBackground:= HBRUSH( COLOR_BACKGROUND ); // Цвет фона
   lpszMenuName:= nil; // Имя меню (сейчас нету меню)
   lpszClassName:= WndClass; // Имя класса окна
   lpfnWndProc:= @WndProc; // Указатель на оконную процедуру
  end;

 RegisterClassEx( Wc ); // Регистрация окна

 Wnd:= CreateWindowEx( 0, WndClass,
                      WndCaption, WS_OVERLAPPEDWINDOW,
                      10, 10,
                      640, 480,
                      0, 0,
                      hInstance, nil );

 ShowWindow( Wnd, CmdShow ); // Показываем окно
 UpdateWindow( Wnd ); // Обновляем окно

 While GetMessage( Msg, 0, 0, 0 )
  do
   begin
    TranslateMessage( Msg );
    DispatchMessage( Msg );
   end;

 Halt( Msg.wParam );

end.

C:

#include < windows.h >

const char WndClass[] = "WndApi",
           WndCaption[] = "Добро пожаловать!";

LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
     if(msg == WM_DESTROY)
     {
         PostQuitMessage(0);
         return 0;
     }
     else
      return DefWindowProc(wnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX Wc;
    HWND Wnd;
    MSG Msg;

    Wc.cbSize = sizeof(WNDCLASSEX);
    Wc.style = CS_VREDRAW || CS_HREDRAW;
    Wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    Wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    Wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    Wc.hInstance = hInstance;
    Wc.cbClsExtra = 0;
    Wc.cbWndExtra = 0;
    Wc.hbrBackground = HBRUSH( COLOR_BACKGROUND );
    Wc.lpszMenuName = NULL;
    Wc.lpszClassName = WndClass;
    Wc.lpfnWndProc = WndProc;

    RegisterClassEx( &Wc );

    Wnd = CreateWindowEx( 0, WndClass,
                      WndCaption, WS_OVERLAPPEDWINDOW,
                      10, 10,
                      640, 480,
                      0, 0,
                      hInstance, NULL );

    ShowWindow( Wnd, nCmdShow );
    UpdateWindow( Wnd );

    while(GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

Вот и всё :) Наконец... :) Обратите внимание на изменения в коде. Прежде всего объясню некоторые изменения в Си.

Как я и обещал, глобальные переменные перетащил в главную функцию. Второе изменение: оказывается style в Си пишется с маленькой буквы! И чтобы найти эту ошибку пришлось компилировать весь код! Про что я и говорил. Не закончив кода не проверишь программу! Ещё одно изменение, уже в обеих языках, я использовал все свойства класса окна, потому что каждый компьютер по своему воспринимает отсуствие тех, или инных параметров. Например, на одном компе у меня всё действовало и с 10 свойствами, а другой просто отказывался показывать окно! :) Парадокс... :)

Теперь Дельфи. Опять же не буду объяснять сразу всего, но это также касается и языка Си.

    1) Свойства класса окна прокомментированны.
    2) Дальше регистрируется (в операционной системе) это окно (функция RegisterClassEx). В качестае аргумента - ссылка на окно.
    3) Создаётся само окно. Аргументы: 1 - ссылка на окно (указывать не надо, потому что само окно присваивается переменной hwnd типа), 2 - имя класса окна (каждое окно имеет своё уникальное имя (не изменяеммое), по которому к нему обращается винда. это значение у меня записанно в константу), 3 - заголовок окна (тоже в константе), 4 - стиль окна (сейчас - сборник стилей, т.е. самое обычное окно Windows), 5 и 6 - позиция окна (как Left и Top в визуальном программировании), 7 и 8 - размер окна, 9 - родительское окно (в нашей программе не существует), 10 - имя меню (меню отсутствует в нашей программе), 11 - экземпляр программы (он вообще часто используется, но конкретно не указывается), 12 - дополнительный параметр (наша программа не использует никаких дополнительных параметров). Чтобы лучше понять - поэкспериментируйте с этими значениями.
    4) Окно показывается на экране. Первый параметр - ссылка на созданное окно, второй - способ показа окна. Наш способ - часто используемый, но зависимый от операционки, по этому при компиляции появляется предупреждение. Не обращайте на него внимания.
    5) Окно обновляется. Эта функция не обязательна, но рекомендуема! Иногда после показа окна (на всякий случай) надо его сразу обновить.
    6) Главный цыкл программы. Он и "ловит" все сообщения, переводя их в оконную процедуру. Вернее, ловит функция GetMessage (нам нужен всего один параметр), TranslateMessage переводит сообщение на "понятный операционной системе язык", и DispatchMessage помогает обработать сообщения, т.е. пересылает их в оконную процедуру.
    7) И наконец происходит успешный выход из программы после завершения цикла и выполнения функции PostQuitMessage.

Конечно, объяснил я не всё :) Но остальное - в следущем выпуске, потому что это будет отдельная тема.

Теперь, задание #9: Сами напишите код обычного окна. Создайте себе план, по которому легче будет это сделать. Я себе создал (поделюсь им в следущем выпуске). Старайтесь не пользоваться рассылкой (хотя в первое время это не избежно!).
Задание #10: ещё пару раз в сутки выполните девятое задание, только теперь вообще не используйте рассылку! Делайте это каждый второй день, до выхода следущего выпуска.

Если что нибудь не поняли - не молчите! На сегодня всё :)

 

2005-10-30

Spider3D http://spider3d.narod.ru

Евгений Нарышкин Spider3D@yandex.ru

Архив Рассылки

Хостинг от uCoz