Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

Набор подобных средств взаимодействия называется интерфейсом прикладных программ PI ~ ppliction progrmming interfce

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

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

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 28.11.2024

5

  1.  АРХИТЕКТУРА  ПРИЛОЖЕНИЙ  WINDOWS

Основные сведения  

Одной из важнейших функций любой операционной системы (ОС) является предоставление прикладной программе средств взаимодействия с аппаратурой, другими программами и, наконец, с самой ОС. Набор подобных средств взаимодействия называется интерфейсом прикладных программ (API – application programming interface). В большинстве однозадачных ОС прикладная программа обычно имеет возможность игнорировать API при доступе к ресурсам.  Например, в MS DOS API реализован в виде функций программного прерывания 21h, однако, ничто не мешает прикладной программе непосредственно вызывать функции BIOS или  обращаться к аппаратуре через порты ввода/вывода. Подобная ситуация недопустима в мультизадачных ОС, так как неизбежно будет приводить к конфликтам при доступе к разделяемым ресурсам. Поэтому в таких ОС единственным способом связи прикладной программы с внешним миром является использование API.

 Windows95 и Windows NT 4.0 используют единый 32-разрядный API, который носит название API WIN32. В идеале это означает, что прикладная программа, использующая WIN32, будет корректно работать и под управлением Windows95, и под управлением Windows NT 4.0. Для большинства программ это соответствует действительности, однако, в силу определенных различий в архитектуре рассматриваемых ОС имеется некоторый набор функций API, которые реализованы в Windows NT, но отсутствуют в Windows95, и наоборот. Программы, использующие эти функции будут успешно компилироваться (ведь API – единый!), но при работе программы в среде другой ОС будут возникать ошибки. Подобный эффект достигается за счет того, что неподдерживаемые функции реализованы в виде “заглушек”, возвращающих информацию об ошибке. К счастью, таких функций немного и они достаточно редко используются. В данном пособии мы будем избегать использования подобных функций, а где это необходимо будет приводиться соответствующая информация.

 API WIN32 представляет собой совокупность более 1000 системных функций. В  отличие от MS DOS, где для вызова  функций  ОС  используется  механизм  программных  прерываний,  вызов  системных  функций  WIN32 производится по их  именам. При этом независимо от языка программирования имена функций, а также входные и выходные параметры остаются неизменными (что, в общем, и следовало ожидать). Это делает программы для Windows, написанные на различных языках, очень похожими. Поэтому, несмотря на то, что в данном пособии в примерах используется язык С (все примеры были отлажены в среде программирования Borland C++ 5.01), практически вся информация может быть с успехом применена и при программировании на любом другом языке программирования. Важно также отметить, что при компиляции в загрузочный модуль не включается код системных функций, а помещаются только ссылки на них. Сами функции API содержатся в так называемых библиотеках динамической компоновки (DLL-библиотеках), входящих в состав ОС. Подробнее о DLL-библиотеках мы поговорим в отдельной главе.

    

 Структура приложения Windows

 Прикладная программа, разработанная специально для Windows95 или Windows NT называется приложением WIN32 (WIN32 application). Поэтому в дальнейшем мы будем использовать термин приложение вместо термина “прикладная программа”.

 Архитектура  приложения  представляет собой  архитектуру  программы, управляемой  событиями.  Ключевую  роль в организации  работы  всех  подсистем и приложений  Windows играет система сообщений. Процесс обработки сообщений представлен на рис. 1.

6

  

Рис. 1.  Процесс  обработки  сообщений

7

 В Windows95 и Windows NT реализована вытесняющая мультизадачность на уровне потоков. Поток (thread) описывает последовательность исполнения кода внутри процесса. В соответствии с установленным приоритетом система выделяет каждому потоку кванты процессорного времени, задаваемые системным таймером. Любому потоку в приложении соответствует некоторая функция - функция потока.  При запуске приложения создается процесс, а в его рамках – первичный поток, который и получает управление. Приложение  должно  состоять  как  минимум  из  одной  функции – функции первичного потока,  которая  при  программировании  на  языке  С  заменяет  функцию main и должна быть определена  следующим  образом:

int   WINAPI WinMain  (HINSTANCE   hinstance ,  HINSTANCE   hPrevinstance,

                                LPSTR   lpszCmdLine,        int  nCmdShow)

{ //    Тело    функции    }

Все  параметры ,  получаемые  функцией  WinMain ,  формируются  операционной системой. Параметры  hinstance  и hPrevinstance представляют  собой  соответственно  идентификаторы  текущей  и  предыдущей  копии  приложения. Приложения WIN32 функционируют в изолированных виртуальных адресных пространствах и в силу этого не имеют возможности определить через параметр hPrevinstance, не запущено ли приложение вторично. Этот параметр использовался в предыдущих версиях Windows, для приложений WIN32 он всегда равен нулю.  Параметр lpszCmdLine - дальний  указатель на строку  запуска  приложения ,  параметр nCmdShow указывает рекомендуемый приложению способ представления главного окна  при  запуске. Приложение может игнорировать этот  параметр, однако это не является хорошим стилем программирования. Обычно значение данного параметра передается без изменений функции ShowWindow для отображения на  экране главного окна приложения. Ниже приведены возможные значения параметра nCmdShow, определенные как константы с префиксами SW_ :

SW_HIDE - скрыть окно и активизировать другое окно;

SW_MINIMIZE – минимизировать окно и активизировать окно верхнего уровня в системном списке;

           SW_RESTORE – активизировать и отобразить окно в его первоначальном размере и позиции;

SW_SHOW -  активизировать и отобразить окно в его текущем размере и позиции;

SW_SHOWMAXIMIZED -  активизировать и отобразить окно в полном размере;

SW_SHOWMINIMIZED - активизировать и отобразить окно в виде пиктограммы;

SW_SHOWMINNOACTIVE  - отобразить окно в виде пиктограммы, активное окно остается активным;

SW_SHOWNA - отобразить окно в текущем состоянии, активное окно остается активным;

SW_SHOWNOACTIVATE - отобразить окно в его последнем размере и позиции, активное окно остается активным;

SW_SHOWNORMAL – аналогично SW_RESTORE.

Модификатор вызова WINAPI определяет порядок помещения в стек параметров функции при вызове. Для функций также определены следующие модификаторы:

 #define CALLBACK    __stdcall

#define WINAPI      __stdcall

#define APIENTRY    WINAPI

#define APIPRIVATE  __stdcall

#define PASCAL      __stdcall

Значение, возвращаемое функцией, должно быть равно значению параметра wParam сообщения WM_QUIT или нулю, если выход произошел до запуска цикла обработки сообщений (см. далее).

8

Сделаем одно важное замечание относительно используемых типов данных. Для приложений Windows определено множество новых типов данных, которые выведены из стандартных типов данных языка С. Однако, при программировании следует использовать именно новые типы данных, а не их стандартные эквиваленты. В противном случае будет затруднена модификация программ для их использования в последующих версиях Windows. Например, прототип функции WinMain не изменился при переходе  от API  WIN16 (Windows 3.1) к API WIN32. Однако, в WIN16 тип HINSTANCE представлял собой 16-разрядное целое, а в WIN32 он стал 32-разрядным. Во избежание ошибок рекомендуется в исходном тексте вводить строгий контроль типов при помощи определения :

#define STRICT

Кроме того при именовании переменных следует придерживаться так называемой “венгерской нотации”, согласно которой имя переменной должно начинаться с одной или нескольких строчных букв, отражающих ее тип. Так, в параметре hInstance функции WinMain префикс h  означает идентификатор  (handle), а префикс lpsz в lpszCmdLine означает дальний указатель на ASCIIZ-строку (long pointer on string zero).

Отметим, что в рамках одного процесса программист имеет возможность создать несколько параллельных потоков. Мы отложим рассмотрение мультипотоковых приложений до специальной главы, а пока остановимся на приложениях использующих только первичный поток.

Для нормальной работы приложения функция потока (то есть в нашем случае – функция WinMain) должна обрабатывать сообщения. Сообщение представляет собой сформированную операционной системой информацию о некотором событии во “внешнем” (по отношению к потоку) мире, о котором, по мнению ОС, должен “узнать” поток. Имеется три основных источника сообщений : аппаратура, потоки других процессов (или одного процесса – в случае мультипотоковых приложений) и сама ОС.

При нажатии клавиши на клавиатуре, кнопки мыши, перемещении мыши или при других операциях с аппаратурой драйвер устройства распознает соответствующее аппаратное событие и помещает информацию о нем в очередь системного потока необработанного ввода (RIT – raw input thread). Главной задачей RIT является определение потока, которому необходимо направить информацию об аппаратном событии. Так, в случае сигнала от мыши, RIT определяет поверх какого окна находится курсор мыши, а в случае сигнала от клавиатуры RIT определяет, какой поток является приоритетным потоком (foreground thread), т.е. с каким потоком пользователь работает в данный момент.  Затем RIT направляет информацию о событии в виртуальную очередь ввода выбранного потока. ОС преобразует эту информацию в сообщение, которое направляется в очередь сообщений потока, которому принадлежит виртуальная очередь ввода.

Сообщения от операционной системы и от других потоков помещаются непосредственно в очередь сообщений соответствующего потока. Чаще всего потоки обмениваются сообщениями при помощи функции API SendMessage, которая подробно будет рассмотрена несколько позднее. Кроме того, распространенной практикой является посылка сообщений в собственную очередь сообщений.

Под  сообщением  в  WIN32 понимается структура данных, определенная следующим  образом :

 typedef  struct    tagMSG

{ HWND        hwnd; // идентификатор окна ,  которому  направлено сообщение

 UINT           message; // цифровой  код  (номер) сообщения  

 WPARAM  wParam;// специфическая   для  каждого  сообщения

 LPARAM    lParam;//                         информация                       

 DWORD      time;// время создания  сообщения

 POINT          pt;// положение курсора в момент создания сообщения

}  MSG;

9

Коды сообщений определены как  константы с различными префиксами. Сообщениям,  предназначенным для  управления окнами, соответствуют константы с префиксами WM_. Наиболее часто используемые сообщения будут нами рассмотрены по мере необходимости.  Полный список и описание сообщений можно найти в справочной документации и в электронном справочнике WIN32 Programmer’s Reference системы программирования Borland C++ 5.01.

 Для обработки сообщений функция потока должна  содержать цикл обработки  сообщений (message loop). Основу  этого цикла  составляют  две  функции API WIN32,  имеющие  следующие  прототипы:

BOOL GetMessage (LPMSG lpmsg ,  HWND  hwnd, UINT uMsgFilterMin , UINT uMsgFilterMax);

LONG DispatchMessage(const MSG  * lpmsg);

Простейший  цикл обработки  сообщений выглядит следующим  образом:

           MSG      msg;

      .   .   .

       while (GetMessage(&msg ,  0 ,  0  ,  0 ) ) { DispatchMessage(&msg); }

Функция GetMessage извлекает очередное сообщение из очереди сообщений вызвавшего ее потока. Параметр lpmsg - дальний указатель на структуру  типа MSG. Параметр hwnd (идентификатор окна) определяет конкретное окно из числа окон, созданных потоком, которому  послано  сообщение. Сообщения, инициируемые устройствами ввода, такими как клавиатура или мышь, посылаются окну, владеющему фокусом ввода (активному окну). В каждый момент времени поток либо вообще не владеет фокусом ввода (пользователь работает с окном другого потока), либо фокус ввода принадлежит одному из его окон. Если в качестве второго параметра  функции  GetMessage указан NULL  ( или 0 ) ,  то  сообщения  будут выбираться  для всех  окон, принадлежащих вызвавшему функцию потоку.  Третий  и четвертый параметры функции  GetMessage задают диапазон номеров сообщений ,  извлекаемых  из очереди.  Если  оба  параметра  содержат нуль ,  то извлекаются все  сообщения ,  направленные потоку. Функция GetMessage возвращает значение FALSE в случае, если извлекается сообщение WM_QUIT, во всех остальных случаях возвращается значение TRUE. Выборка сообщения WM_QUIT приводит к выходу из цикла, после чего обычно поток завершается. Следует также отметить важную особенность функции GetMessage : если функция обнаруживает, что очередь сообщений потока пуста, то поток блокируется и ему не выделяется процессорное время до тех пор, пока новое сообщение не поступит в очередь. Если пусты очереди всех прикладных потоков, то обычно процессорное время распределяется между низкоприоритетными системными потоками, в частности, запускается хранитель экрана.

Функция  DispatchMessage определяет окно ,  которому  предназначено сообщение ,  и вызывает  соответствующую  функцию окна .  Функция  окна  относится  к  так  называемым  косвенно вызываемым  ( callback )  функциям  ,  которые  создаются  разработчиком  приложения  ,  а  вызываются  Windows.  Каждому  окну  ,  создаваемому потоком, должна соответствовать  некоторая  функция  окна  (допустимо ,  когда  одна  функция  определяется  как  функция  окна  сразу  для  нескольких  окон).  В  функции  окна  сосредоточены  все  действия  потока  по обработке  сообщений ,  направленных  окну.  Так  как  абсолютное большинство приложений  можно рассматривать как  совокупность  окон,  можно  считать ,  что основная  работа  приложения выполняется  функцией  ( или  функциями)  окна.

Функции  окна  соответствует  следующий  прототип  (имя  функции  выбирается  произвольно):

LRESULT  CALLBACK WndProc ( HWND  hwnd ,  UINT  msg ,

                                                           WPARAM  wParam  ,  LPARAM lParam);

Параметры  функции  полностью  аналогичны  соответствующим  полям  стуктуры  MSG.

Типичная  структура  функции  окна  имеет  следующий  вид :

10

LRESULT  CALLBACK WndProc ( HWND  hwnd ,  UINT  msg ,

                                                                      WPARAM  wParam  ,  LPARAM lParam);

{ //  обработка  сообщений   

 switch (msg)

 {   case  сообщениe  :{   .   .   .   return 0 ; }

                            case  сообщениe  :{   .   .   .   return 0 ; }

      .      .      .

                           case   WM_DESTROY :

                 {

        // это сообщение приходит ,  когда  пользователь закрывает окно

         PostQuitMessage(0);  // посылаем  в очередь  сообщение WM_QUIT

                    return 0 ;

                             }

                         }

            // остальные сообщения  обрабатываются  по умолчанию

            return  DefWindowProc ( hwnd ,  msg ,  wParam  ,  lParam );  

           }

Фактически функция окна представляет собой один большой оператор выбора из обрабатываемых функцией окна сообщений и содержит обработчики этих сообщений. Как правило, программист планирует обработку далеко не всех сообщений, поступающих в функцию окна. Поэтому, перед возвратом из функции необработанные сообщения передаются на обработку по умолчанию функции DefWindowProc. Значение, возвращаемое функцией окна в общем случае определяется тем, какое обрабатывалось сообщение. Обратим также внимание на обработку сообщения WM_DESTROY, которое поступает в функцию окна, когда пользователь закрывает окно. Если речь идет о главном окне приложения, то стандартно после закрытия этого окна приложение должно быть завершено. Для этого при помощи функции PostQuitMessage в очередь сообщений соответствующего потока посылается сообщение WM_QUIT. Это один из примеров посылки сообщения в собственную очередь.

 

 Регистрация класса окна и создание окна

Приложение, использующее графический интерфейс с пользователем , должно иметь, как минимум, одно окно – главное окно приложения. Для  того,  чтобы  создать окно, необходимо вначале зарегистрировать класс  окна .  Класс  окна  определяет  набор  свойств ,  характерных  для  всех  окон  ,  создаваемых  на базе данного класса.  В  Windows  определены  стандартные  классы  окон, используемые  для  создания  стандартных  органов  управления.  Стандартные  классы  не регистрируются   приложением  и для  них  не  определяется  функция окна ,  так  как  эти операции  выполняются  автоматически  при  загрузке  Windows.

Для  регистрации  класса  окна  используется  следующая  функция  API WIN32:

ATOM   WINAPI  RegisterClassEx ( const  WNDCLASSEX  FAR  * lpwcx);

В качестве параметра эта функция  получает дальний указатель на структуру  WNDCLASSEX,  которую  должно  сформировать  приложение  перед  регистрацией  класса  окна.  Структура  WNDCLASSEX определяется следующим образом :

 typedef struct _WNDCLASSEX

          {    // размер структуры WNDCLASSEX

   UINT    cbSize;

// стиль класса окна (если равен нулю используется стиль по умолчанию)

   UINT    style;

11

         // указатель на функцию окна, обрабатывающую  сообщения, предназначенные

        // для всех окон, созданных на основе данного класса

   WNDPROC lpfnWndProc;

        // размер дополнительной области данных,  зарезервированной в описании класса окна

        // (должно быть равно нулю, если не используется

                           int     cbClsExtra;

        // размер дополнительной области данных,  зарезервированной для каждого окна,

        // созданного на основе данного класса ( должно быть равно нулю если не используется )

   int     cbWndExtra;

// идентификатор приложения, зарегистрировавшего данный класс

   HANDLE  hInstance;

        // идентификатор пиктограммы, используемой  для окна данного класса

   HICON   hIcon;

       // идентификатор курсора, используемого для окнa данного класса

   HCURSOR hCursor;

       // идентификатор  кисти  ( цвета )  фона окна

   HBRUSH  hbrBackground;

       // дальний указатель на строку  меню

   LPCTSTR lpszMenuName;

       // дальний  указатель на строку  с  именем  класса

   LPCTSTR lpszClassName;

// идентификатор маленькой пиктограммы для заголовка окна

   HICON   hIconSm;

} WNDCLASSEX;

Рассмотрим более подробно некоторые поля структуры WNDCLASSEX.

Стиль  класса окна  определяет  внешний  вид  и поведение всех  окон  ,  созданных на  базе  данного класса.  Стиль  класса  задается  в  виде  логической  комбинации констант  с  префиксом  CS_ :

CS_BYTEALIGNCLIENT – внутренняя область окна выравнивается по границе байта видеопамяти;

CS_BYTEALIGNWINDOW – все окно выравнивается по границе байта видеопамяти;

CS_CLASSDC – создается единый контекст отображения для всех окон данного класса;

CS_DBLCLKS – функция окна будет получать сообщения о двойном щелчке мышью;

CS_GLOBALCLASS – класс является глобальным и доступен другим приложениям;

CS_HREDRAW – окно должно быть перерисовано при изменении его ширины;

CS_NOCLOSE – в системном меню запрещается функция закрытия окна;

CS_PAREHTDC – окно будет пользоваться родительским контекстом отображения;

CS_OWNDC – для каждого окна данного класса создается отдельный контекст отображения;

CS_SAVEBITS – для данного окна Windows должна хранить его битовое изображение, по которому при необходимости окно восстанавливается на экране;

CS_VREDRAW – окно должно быть перерисовано при изменении его высоты.

Наиболее часто используются  стили  CS_VREDRAW | CS_HREDRAW  (при изменении размера окна ему  посылается  сообщение  WM_PAINT , в ответ на  которое  функция  окна должна обновить окно)  и  CS_DBLCLKS  (для окна  отслеживаются  двойные  щелчки мышью). Некоторые другие стили, в частности связанные с контекстом отображения, будут описаны в дальнейшем в соответствующих главах.

Курсор мыши  и  пиктограмма  относятся  к  ресурсам  приложения ,  доступ к ним  осуществляется через идентификаторы  ресурсов.  Ресурсы  мы подробно изучим далее в этой главе. Здесь  лишь  отметим  ,  что   идентификаторы   пиктограммы   и   курсора  возвращаются

12

соответственно функциями  LoadIcon  и LoadCursor, причем, если  в  качестве первого параметра  этих  функций  указан  NULL , то загружается  системный  ресурс,  заданный  в виде

предопределенной  константы  с  префиксом  IDC_  для  курсора  и  IDI_  для  пиктограммы.  

Для  заполнения  фона  окна  приложение  должно  создать  кисть и  передать  ее  идентификатор  функции  RegisterClassEx.  В  простейшем  случае  идентификатор  кисти  может  быть  получен  преобразованием  к  типу   HBRUSH  одной  из  предопределенных  констант ,  задающих   системные  цвета .  Эти  константы  имеют  префикс  COLOR_.  Константа  COLOR_WINDOW  задает  системный  цвет  фона.   Кроме  того ,  для  заполнения  фона  окна  можно  использовать  еще  один  ресурс  приложения  -  изображение  bitmap.  Для  этого  оно  должно быть загружено  при  помощи  функции  LoadBitmap  ,  а  затем  превращено  в кисть  с  помощью  функции CreatePatternBrush.  Если  в  качестве  первого  параметра  функции LoadBitmap  передается  NULL  ,  то  загружается  предопределенное  изображение bitmap, заданное в качестве второго параметра  константой  с  префиксом  OBM_ . Более подробно эти вопросы рассматриваются в главе, посвященной интерфейсу графических устройств GDI.

Важно также отметить, что именно при регистрации класса окна через поле lpfnWndProc классу окна ставится в соответствие функция окна, которая будет обрабатывать сообщения для всех окон, созданных на базе данного класса.

Функция RegisterClassEx возвращает уникальный идентификатор класса окна типа ATOM или нуль при ошибке.

Для  создания  окна  используется  функция  CreateWindow :

          HWND    WINAPI  CreateWindow(

                      LPCSTR            lpszClassName ,  // указатель на строку с именем класса

                      LPCSTR            lpszWindowName , // указатель на заголовок  окна

                      DWORD           dwStyle , // стиль окна

                       int                      x , //            координаты  верхнего

                       int                      y , //            левого  угла  окна

                       int                      nWidth , // ширина окна

                       int                      nHeight , // высота окна

                       HWND             hwndParent , // идентификатор родителького окна или 0

                       HMENU          hmenu , // идентификатор ресурса - меню или  0

                       HINSTANCE  hinst , // идентификатор приложения , создавшего окно

                       LPVOID      lpvParam // указатель на область данных для окна или NULL

                       // этот указатель передается окну вместе с сообщением WM_PAINT );

 Функция  CreateWindow не отображает окно на экране (для этого предназначена  функция ShowWindow). Функция CreateWindow создает  в оперативной  памяти структуру типа WND ,  на которую фактически  указывает идентификатор  окна  типа HWND , возвращаемый  этой  функцией .  Если  создать окно не удалось ,  функция  возвращает значение NULL .

Стиль окна используется для дальнейшего уточнения внешнего вида и поведения конкретного окна ,  созданного на базе некоторого класса.  Стиль окна указывается в виде логической  комбинации предопределенных  констант  с  префиксом  WS_ . Для экономии места мы не будем здесь рассматривать все возможные стили окна, при необходимости соответствующую информацию можно найти в справочной документации и в электронном справочнике WIN32 Programmer’s Reference системы программирования Borland C++ 5.01 (описание функции CreateWindow). Остановимся на наиболее часто используемых классах.

Все многообразие стилей окон сводится к  трем  основным  стилям :  перекрывающиеся  (overlapped)  окна ,  временные (pop-up) окна и дочерние (child) окна .                                 Перекрывающиеся  окна  обычно используются  в качестве главного окна приложения. Константа  для  стиля  перекрывающегося  окна определена следующим образом:

#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION |                                                                                                                                    WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)

13

Константа WS_OVERLAPPED определяет базовый стиль окна как перекрывающееся окно, а остальные константы в указанном определении уточняют внешний вид окна. Оно должно иметь

заголовок (константа WS_CAPTION), системное меню (WS_SYSMENU), толстую рамку (WS_THICKFRAME), кнопки минимизации и максимизации (WS_MINIMIZEBOX | WS_MAXIMIZEBOX). Приложение  может создавать несколько перекрывающихся  окон ,  связанных  между  собой  “отношениями  собственности” .  В  качестве параметра hwndParent  при создании окна можно указать идентификатор ранее созданного окна , которое  становится владельцем  создаваемого окна.  При уничтожении окна-владельца уничтожаются и все принадлежащие ему  окна.  Обычное перекрывающееся окно ,  не имеющее окна-владельца ,  может  располагаться  в любом месте  экрана.  Подчиненные  окна  располагаются всегда над поверхностью  окна-владельца.

Временные  окна обычно используются  для  вывода  на экран  информационных  сообщений и остаются  на  экране  непродолжительное  время.  Стиль таких  окон  определен следующим  образом :

#define WS_POPUPWINDOW      (WS_POPUP | WS_BORDER | WS_SYSMENU)

Константа WS_POPUP определяет базовый стиль окна как временное окно, а остальные константы в указанном определении уточняют внешний вид окна. Оно должно иметь рамку и системное меню. При необходимости иметь заголовок следует указать этот стиль в комбинации со стилем WS_CAPTION. Временные  окна  могут  иметь окно-владельца  и  сами  владеть  другими  окнами.  В общем  случае ,  можно рассматривать  временные  окна  как  частный  случай  перекрывающихся  окон.  Все  замечания ,  сделанные  выше  относительно  перекрывающихся  окон  ,  справедливы  и  для  временных  окон.

Дочерние  окна  чаще  всего  используются  приложениями  для  создания  органов  управления  на  основе  зарегистрированных  в  системе  стандартных  классов.  Окна  этого стиля  ,  как  правило ,  не  имеют  рамки  и  заголовка  и  сами  рисуют  все ,  что должно  быть  в  них  изображено.  Стиль этих  окон  определен следующим  образом :

#define WS_CHILDWINDOW      (WS_CHILD)

Дочернее окно всегда имеет окно-родителя ,  идентификатор  которого  передается  в качестве  параметра hwndParent  при  создании  окна.  Кроме того ,  при создании  дочернего  окна  в качестве  параметра  hmenu  следует  указать  присвоенный  создаваемому  окну  целый  идентификатор.  Этот  идентификатор используется дочерним  окном при  отправлении  сообщений  родительскому  окну .  Дочернее  окно  как бы “прилипает” к  поверхности  родительского окна и  перемещается  вместе с  ним ,  все дочерние  окна  исчезают при сворачивании родительского окна в пиктограмму  и  появляются вновь при восстановлении родительского окна.

Координаты  ,  используемые  функцией  CreateWindow  для  создания  перекрывающихся  и  временных  окон совпадают с  физическими  координатами  экрана ,  начало координат  находится  в  верхнем  левом  углу  экрана ,  ось Х  направлена  вправо ,  ось Y - вниз.  Для  дочерних  окон  начало координат  расположено  в  верхнем  левом  углу  родительского  окна.  Единицей  измерения  физических  координат  является  один  пиксел. Более подробно эти вопросы рассматриваются в главе, посвященной интерфейсу графических устройств GDI.

 Простейшее приложение WIN32

 Рассмотрим пример простейшего приложения WIN32. Это приложение отображает при запуске главное окно, при нажатии над его поверхностью левой или правой кнопок мыши , а также при нажатии клавиши на клавиатуре на экран выдается соответствующая информация.

// =======================================================================

// Простейшее приложение Windows

// =======================================================================

14

#define STRICT       // включаем строгую проверку типов

#include <windows.h> // главный include-файл для приложений Windows

#include <mem.h>

// Прототипы функций

// Функция окна

LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Имя класса окна

char const szClassName[]   = "Application";

// Заголовок окна

char const szWindowTitle[] = "Hello , world !";

// =======================================================================

// Функция WinMain

// Получает управление при запуске приложения

// =======================================================================

#pragma argsused // запрещаем предупреждение о неиспользуемых  в теле функции параметрах

int APIENTRY

WinMain(       HINSTANCE hInstance,     // идентификатор текущей копии приложения

HINSTANCE hPrevInstance, // идентификатор предыдущей копии приложения, для приложений WIN32 всегда NULL

                       LPSTR     lpszCmdLine,   // указатель на командную строку

 int       nCmdShow)// способ отображения главного окна приложения

{MSG  msg;   // структура для работы с сообщениями

 HWND hwnd;  // идентификатор главного окна приложения

 WNDCLASSEX wc;    // структура для регистрации  класса окна

 // ----------------------------------------------------

 // Выполняем регистрацию класса окна

 // ----------------------------------------------------

 // Записываем нулевые значения во все поля структуры

 memset(&wc, 0, sizeof(wc));

 // Размер структуры WNDCLASSEX

 wc.cbSize = sizeof(WNDCLASSEX);

 // Стиль класса окна - по умолчанию

 wc.style = 0;

 // Указатель на функцию окна, обрабатывающую сообщения, предназначенные для всех окон,

 // созданных на основе данного класса

 wc.lpfnWndProc = (WNDPROC) WndProc;

 // Размер дополнительной области данных,  зарезервированной в описании класса окна

 wc.cbClsExtra = 0;

 // Размер дополнительной области данных,  зарезервированной для каждого окна,

 // созданного на основе данного класса

 wc.cbWndExtra = 0;

 // Идентификатор приложения, которое создало данный класс

 wc.hInstance = hInstance;

 // Идентификатор пиктограммы, используемой для окна данного класса

 // загружается стандартная пиктограмма приложения из ресурсов Windows

 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

 // Идентификатор курсора, используемого  для окна данного класса

 // загружается стандартный курсор из ресурсов Windows

 wc.hCursor = LoadCursor(NULL, IDC_ARROW);

 // Цвет фона окна

 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);

15

// Идентификатор меню

 wc.lpszMenuName = (LPSTR)NULL;

// Имя, которое присваивается создаваемому классу и используется при создании

// окон данного класса

 wc.lpszClassName = (LPSTR)szClassName;

 // Идентификатор маленькой пиктограммы  в заголовке окна данного класса

 // загружается стандартная пиктограмма – логотип  из ресурсов Windows

 wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);

 // Регистрация класса , завершение работы в случае ошибки

 if (!RegisterClassEx(&wc)) return FALSE;

 // ----------------------------------------------------

 // Создаем главное окно приложения

// ----------------------------------------------------

 hwnd = CreateWindow(

   szClassName,         // имя класса окна

   szWindowTitle,       // заголовок окна

   WS_OVERLAPPEDWINDOW, // стиль окна

   CW_USEDEFAULT,       // задаем размеры и расположение

   CW_USEDEFAULT,       // окна, принятые по умолчанию

   CW_USEDEFAULT,

   CW_USEDEFAULT,

   NULL ,              // идентификатор родительского окна

   NULL ,               // идентификатор меню

   hInstance,           // идентификатор приложения

   NULL);               // указатель на дополнительные параметры

 // Если создать окно не удалось, завершаем приложение

 if(!hwnd)return FALSE;

// Рисуем окно. Для этого после функции ShowWindow, рисующей  окно, вызываем функцию //UpdateWindow, посылающую сообщение WM_PAINT в функцию окна 

 ShowWindow(hwnd, nCmdShow);

 UpdateWindow(hwnd);

 // Запускаем цикл обработки сообщений

 while(GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); }

 // Возвращаем значение wParam, переданное  в качестве параметра функции PostQuitMessage

 // в процессе инициирования завершения работы приложения из функции окна.

 // Затем завершаем работу приложения

 return msg.wParam;

}

// =======================================================================

// Функция WndProc , НЕ ВЫЗЫВАЕТСЯ ни из одной функции приложения.

// Эту функцию вызывает Windows в процессе обработки сообщений.

// Для этого адрес функции WndProc указывается при регистрации класса окна.

// Функция выполняет обработку сообщений для главного окна приложения

// =======================================================================

LRESULT WINAPI

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{ // Выполняем обработку сообщений. Идентификатор

 // сообщения передается через параметр msg

 switch (msg)

 {

16

// Это сообщение приходит, когда вы поместили курсор мыши в область главного окна //приложения и нажали левую кнопку мыши

   case WM_LBUTTONDOWN:

{ MessageBox(NULL, "Нажата левая кнопка мыши", "Сообщение WM_LBUTTONDOWN", MB_OK | MB_ICONINFORMATION); return 0;}

// Это сообщение приходит, когда вы поместили курсор мыши в область главного окна //приложения и нажали правую кнопку мыши

   case WM_RBUTTONDOWN:

{MessageBox(NULL, "Нажата правая кнопка мыши", "Сообщение WM_RBUTTONDOWN",  MB_OK | MB_ICONINFORMATION); return 0;}

// Это сообщение приходит, когда вы нажали клавишу на клавиатуре

   case WM_KEYDOWN:

{MessageBox(NULL, "Нажата клавиша на клавиатуре", "Сообщение WM_KEYDOWN",  MB_OK | MB_ICONINFORMATION); return 0;}

// Это сообщение приходит, когда вы завершаете  работу приложения стандартным для //Windows способом

   case WM_DESTROY:

   {// Инициируем завершение работы приложения, помещая в очередь приложения сообщение

     // WM_QUIT. Это приведет к завершению цикла обработки сообщений в функции WinMain

     PostQuitMessage(0); return 0;}

 }

 // Все сообщения, которые не обрабатываются нашей функцией окна, ДОЛЖНЫ передаваться // функции  DefWindowProc

 return DefWindowProc(hwnd, msg, wParam, lParam);

}

 Функция WinMain регистрирует класс окна при помощи функции RegisterClassEx, затем создает окно при помощи функции CreateWindow и отображает его на экране при помощи функции ShowWindow. После этого запускается цикл обработки сообщений. WinMain не проверяет, не запущено ли приложение повторно, так как запуск нескольких копий приложения является обычной практикой для многих приложений. Однако, иногда может возникнуть необходимость в запрете повторного запуска. Как уже говорилось выше, для этого в WIN32 нельзя воспользоваться параметром hPrevInstance функции WinMain. Ниже приведен фрагмент кода, который при запуске приложения пытается найти окно своего приложения, и если окно присутствует в системе активизирует его и выдвигает на передний план. Собственно именно такой реакции более всего ожидает пользователь, когда запускает приложение.

  // Проверяем, не было ли это приложение запущено ранее

hwnd = FindWindow(szClassName, NULL);

   if(hwnd)

  {// Если окно приложения было свернуто в пиктограмму,  восстанавливаем его

if(IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE);

 // Выдвигаем окно приложения на передний план

   SetForegroundWindow(hwnd);

// Завершаем работу запущенной копии

return FALSE;}

Функция окна WndProc обрабатывает два сообщения от мыши (WM_LBUTTONDOWN и WM_RBUTTONDOWN), а также сообщение от клавиатуры (WM_KEYDOWN). Обработка этих сообщений заключается в отображении на экране стандартной информационной диалоговой панели при помощи функции API MessageBox. Кроме того, обрабатывается сообщение WM_DESTROY, поступающее в функцию окна, когда пользователь закрывает окно. Обработчик этого сообщения посылает в очередь сообщений приложения сообщение WM_QUIT, что приводит к выходу из цикла обработки сообщений и завершению приложения.

17

 Обзор сообщений

 Рассмотрим более подробно некоторые наиболее часто встречающиеся сообщения.

Сообщение  WM_CREATE  поступает в функцию окна при создании окна функцией CreateWindow и может  обрабатываться приложением  ,  например  для  инициализации  структур  данных  ,  связанных   с   окном.   Это  сообщение  сигнализирует  приложению  о  том ,  что  создание  окна  завершено. Параметр lParam этого сообщения содержит указатель на структуру CREATESTRUCT, члены который аналогичны параметрам функции CreateWindowEx (см. справочную документацию или электронный справочник WIN32 Programmer’s Reference системы программирования Borland C++ 5.01). Обработчик этого сообщения должен вернуть значение 0 для продолжения процедуры создания окна или –1  для прекращения этой процедуры (в этом случае CreateWindow вернет значение NULL).

Для  того ,  чтобы  сделать  окно  видимым ,  приложение   вызывает  функцию  ShowWindow.  Эта  функция порождает множество сообщений, большинство из которых обычно направляется функции DefWindowProc. Исключение составляют сообщения WM_SIZE и  WM_MOVE, которые довольно часто обрабатываются приложением.

Сообщение  WM_SIZE  информирует  приложение  о  новых  размерах  окна  и  о  способе ,  при  помощи  которого  окно  изменило  свой  размер. Параметр wParam содержит константу, информирующую о способе изменения размера окна :

SIZE_MAXHIDEмаксимизировано какое-либо другое окно (посылается временным окнам); 

SIZE_MAXIMIZED – максимизировано данное окно;

SIZE_MAXSHOW – какое-либо другое окно восстановлено в прежних размерах (посылается временным окнам); 

SIZE_MINIMIZED - минимизировано данное окно;

SIZE_RESTORED – окно изменило размеры, но не было максимизировано или минимизировано.

Младшее слово параметра lParam задает новую ширину рабочей области окна (client area), а старшее слово этого параметра задает ее новую высоту. Обработчик сообщения должен вернуть нулевое значение.

Сообщение  WM_MOVE  поступает  при  перемещении окна по экрану.  В  ответ  на  эти  сообщения  приложение  обычно изменяет  значение  собственных  переменных  ,  определяющих  координаты  окна ,  и  выполняет  другие  действия  ,  связанные  с  изменением  размера  и  местоположения  окна.  Младшее слово параметра lParam данного сообщения задает новую горизонтальную, а старшее слово – новую вертикальную координату верхнего левого угла рабочей области окна. Обработчик сообщения должен вернуть нулевое значение.

Сообщение  WM_PAINT  генерируется  Windows  при возникновении  необходимости  перерисовать  окно  (например ,  при  увеличении  его  размера)  или  при  вызове  приложением  некоторых  функций  API ( UpdateWindow ). Обычно , все операции по выводу  на экран  выполняются  обработчиком сообщения WM_PAINT. Подробно об этом сообщении говорится в главе, посвященной интерфейсу графических устройств GDI.

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

Сообщение  WM_DESTROY  поступает  в  функцию  окна ,  когда  пользователь  закрывает  окно.  Типичный  обработчик  этого сообщения для главного окна приложения вызывает  функцию  PostQuitMessage,  которая  генерирует  сообщение  WM_QUIT.  В  ответ  на  это  сообщение ,  как  указывалось  выше ,  происходит  выход  из  цикла  обработки  сообщений  и  завершение  приложения.

18

От  клавиатуры  могут поступать  четыре  сообщения :  WM_KEYDOWN ,  WM_KEYUP,  WM_SYSKEYDOWN  ,  WM_SYSKEYUP.  При  нажатии  клавиши  генерируется  сообщение   WM_KEYDOWN  или  WM_SYSKEYDOWN  ,  в  зависимости  от  того  ,  какая  нажата  клавиша и  была ли эта  клавиша  нажата  в  комбинации  с  <Alt>.  Соответственно при  отпускании  клавиши  генерируется  сообщение  WM_KEYUP или  WM_SYSKEYUP.  Параметр wParam  этих  сообщений  содержит ,  так  называемый  , код  виртуальной  клавиши  ,  соответствующий  некоторой  физической клавише.  Коды  виртуальных  клавиш  определены как константы  с  префиксом  VK_ .  Параметр  lParam  содержит  OEM  scan-код  клавиши (тот самый код ,  который  получают  в  регистре AH  программы  MS DOS  при вызове  INT  16h) ,  счетчик  повторов (при  удерживании  нажатой  клавиши  несколько сообщений  сливаются  в  одно  с  соответствующим  счетчиком  повторов),  флаг  расширенной  клавиатуры  (устанавливается  для  клавиш ,  имеющихся  только  на  101-клавишной  клавиатуре)  ,  код контекста ( 1 - комбинация с <Alt> ,  0 - нет) ,  флаги  предыдущего состояния  клавиши  и  флаг  изменения  состояния  клавиши (см. справочную документацию или электронный справочник WIN32 Programmer’s Reference системы программирования Borland C++ 5.01). Для  получения  информации  о  состоянии  клавиш  используются  также  функции  GetKeyState , GetAsyncKeyState ,  GetKeyboardState , GetKeyboardType  ,  GetKeyNameText.

Функция TranslateMessage  преобразует клавиатурные сообщения  в  символьные сообщения WM_CHAR ,  WM_SYSCHAR ,  WM_DEADCHAR  ,  WM_SYSDEADCHAR.  Как правило, приложение обрабатывает только сообщение WM_CHAR. Образованные  символьные  сообщения  помещаются  в  очередь сообщений приложения ,  причем оригинальные клавиатурные сообщения  из  этой  очереди  не  удаляются.  Символьные  сообщения  используются  для  непосредственного определения  символа по нажатой  клавише (код символа в стандарте ANSI заносится в параметр wParam сообщения, параметр lParam содержит ту же информацию, что и для рассмотренных выше оригинальных клавиатурных сообщений).  Следует  отметить ,  что Windows  использует  кодировку  символов ANSI , отличную от кодировки OEM  MS DOS.  Для  перекодировки  используются  функции  CharToOem ,  CharToOemBuff ,  OemToChar ,  OemToCharBuff , CharLower , CharLowerBuff , CharUpper , CharUpperBuff , CharNext , CharPrev ,  VkKeyScanEx. Для более подробной информации следует обратиться к справочной документации или электронному справочнику WIN32 Programmer’s Reference системы программирования Borland C++ 5.01. Для  использования  функции TranslateMessage ее необходимо включить в цикл обработки сообщений :

        while (GetMessage(&msg ,  0 ,  0  ,  0 ) ) {TranslateMessage(&msg); DispatchMessage(&msg);}

Обработчики всех клавиатурных сообщений должны возвращать нулевое значение.

 При  любом  перемещении  мыши по экрану  генерируется  сообщение WM_NCHITEST. Старшее слово параметра lParam этого сообщения содержит вертикальную, а младшее слово – горизонтальную координату курсора мыши в момент появления сообщения. Обычно это сообщение не обрабатывается  приложением , а передается  функции  DefWindowProc .  После обработки  этого сообщения Windows анализирует положение курсора,  а  также  наличие нажатия клавиши  мыши  и генерирует одно из нижеперечисленных  сообщений. В случае, если курсор находится в рабочей области окна генерируются сообщения : 

WM_LBUTTONDBLCLK – двойной щелчок левой кнопкой мыши;

WM_LBUTTONDOWN – нажата левая кнопка мыши;

WM_LBUTTONUP – отпущена левая кнопка мыши;

WM_MBUTTONDBLCLK – двойной щелчок средней кнопкой мыши;

WM_MBUTTONDOWN – нажата средняя кнопка мыши;

WM_MBUTTONUP – отпущена средняя кнопка мыши;

WM_RBUTTONDBLCLK – двойной щелчок правой кнопкой мыши;

WM_RBUTTONDOWN – нажата правая кнопка мыши;

19

WM_RBUTTONUP – отпущена правая кнопка мыши;

WM_MOUSEMOVE – перемещение курсора мыши;

WM_MOUSEACTIVATE – нажата клавиша мыши над неактивным окном.

Для всех перечисленных сообщений параметр lParam содержит координаты курсора мыши (старшее слово – вертикальную, младшее – горизонтальную), а параметр wParam представляет логическую комбинацию следующих битовых флагов :

MK_CONTROL – нажата клавиша CTRL;

MK_LBUTTON – левая кнопка мыши находится в нажатом состоянии;

MK_MBUTTON – средняя кнопка мыши находится в нажатом состоянии;

MK_RBUTTON – правая кнопка мыши находится в нажатом состоянии;

MK_SHIFT – нажата клавиша SHIFT.

Обработчики этих сообщений должны возвращать нулевое значение. Отметим также, что сообщения о двойных щелчках мышью будут поступать в функцию окна только в случае, если класс окна определен со стилем CS_DBLCLKS.

 В случае, если курсор мыши находится во внешней области окна (пространстве между внешним контуром окна и его рабочей областью – non-client area) генерируются следующие сообщения, смысл которых аналогичен только что рассмотренным : WM_NCLBUTTONDBLCLK,  WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMBUTTONDBLCLK, WM_NCMBUTTONDOWN, WM_NCMBUTTONUP, WM_NCMOUSEMOVE, WM_NCRBUTTONDBLCLK,  WM_NCRBUTTONDOWN, WM_NCRBUTTONUP. Параметр  wParam cообщений WM_NC* содержит значение, возвращаемое функцией DefWindowProc как результат обработки сообщения WM_NCHITEST, а параметр lParam – координаты курсора в структуре типа POINTS :

 typedef struct tagPOINTS { SHORT x; SHORT y; } POINTS;

 Реакция на события от мыши во внешней области окна предопределена в Windows, так как в этом пространстве находятся такие элементы, как рамка окна, заголовок окна, строки меню и т.п. Поэтому в абсолютном большинстве случаев приложение не обрабатывает сообщения WM_NC*, поручая это Windows посредством вызова функции DefWindowProc. Если все же необходима обработка приложением названных сообщений, то их обработчик должен по окончании обработки вызвать функцию DefWindowProc для сохранения стандартной реакции Windows на соответствующее событие.

Мы кратко рассмотрели некоторые наиболее часто используемые сообщения. Еще целый ряд сообщений будет рассмотрен при изучении соответствующих тем. Для более полной информации следует обратиться к справочной документации или электронному справочнику WIN32 Programmer’s Reference системы программирования Borland C++ 5.01. Всего в WIN32 определено более 200 сообщений и, естественно, подробное их рассмотрение выходит за рамки данного пособия.

 

 Макросы – распаковщики сообщений

В файле windowsx.h системы программирования Borland C++ 5.01 определены макросы– распаковщики сообщений, упрощающие обработку сообщений в функции окна. Как уже отмечалось выше, функция окна фактически представляет собой огромный оператор switch, который может занимать не одну сотню строк исходного текста. Написание подобных операторов противоречит принципам структурного программирования и может приводить к трудно обнаруживаемым ошибкам. Второй  серьезной трудностью при программировании функции окна является необходимость обработки параметров сообщений, смысл которых меняется от сообщения к сообщению. Более того, возможно изменение смысла параметров одного и того же сообщения при переходе к новым версиям Windows, как это, например, произошло с параметрами сообщения WM_COMMAND при переходе от WIN16 к WIN32.

 

20

Легко обойти все эти трудности позволяют распаковщики сообщений. Рассмотрим некоторые из них.

Макрос HANDLE_MSG определен следующим образом :

#define HANDLE_MSG(hwnd, message, fn)    \

                        case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

Вот как в случае сообщения WM_LBUTTONDOWN после обработки препроцессором будет выглядеть строка HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Cls_OnLButtonDown) :

case (WM_LBUTTONDOWN):

     return HANDLE_WM_LBUTTONDOWN((hwnd), (wParam), (lParam), (Cls_OnLButtonDown));

Таким образом, макрос HANDLE_MSG применяется в функции окна и назначает обрабатываемому сообщению  отдельную функцию – обработчик.

Макросы HANDLE_WM* непосредственно вызывают указанную функцию, после чего производится возврат из функции окна. Вот как, например, определен макрос HANDLE_WM_LBUTTONDOWN:

#define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \

((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam),             (UINT)(wParam)), 0L)

Результатом раскрытия препроцессором любого из макросов HANDLE_WM_* является вызов функции Cls_On*, которой передаются после соответствующих преобразований типов распакованные части параметров wParam и lParam. Чтобы использовать распаковщик для обработки сообщения следует найти в файле windowsx.h макрос HANDLE_WM* для сообщения, которое необходимо обработать. В строке-комментарии перед макросом указан прототип функции – обработчика сообщения. В случае WM_LBUTTONDOWN эта строка выглядит следующим образом :

/* void Cls_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y,

                          UINT keyFlags) */

 Макросы FORWARD_WM* решают обратную задачу – принимают распакованные параметры сообщения, воссоздают  по ним исходные параметры wParam и lParam, а затем вызывают функцию, указанную в качестве последнего параметра макроса, передавая ей воссозданные параметры. Вот как это выглядит в случае сообщения WM_LBUTTONDOWN:

#define FORWARD_WM_LBUTTONDOWN(hwnd, fDoubleClick, x, y, keyFlags, fn) \

(void)(fn)((hwnd), (fDoubleClick) ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN,         (WPARAM)(UINT)(keyFlags), MAKELPARAM((x), (y)))

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

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

// =======================================================================

// Функция WndProc , выполняет обработку сообщений для главного окна приложения

// =======================================================================

LRESULT WINAPI

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{ // Выполняем обработку сообщений. Идентификатор

 // сообщения передается через параметр msg

 switch (msg)

 { // Для сообщения WM_LBUTTONDOWN назначаем обработчик,

   //расположенный в функции Cls_OnLButtonDown

   HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Cls_OnLButtonDown);

   // Для сообщения WM_RBUTTONDOWN назначаем обработчик,

   // расположенный в функции Cls_OnRButtonDown

21

  HANDLE_MSG(hwnd, WM_RBUTTONDOWN, Cls_OnRButtonDown);

  // Для сообщения WM_KEYDOWN назначаем обработчик,

  // расположенный в функции Cls_OnKey

  HANDLE_MSG(hwnd, WM_KEYDOWN, Cls_OnKey);

  // Для сообщения WM_DESTROY назначаем обработчик,

  // расположенный в функции Cls_OnDestroy

  HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);

  default:

   return DefWindowProc(hwnd, msg, wParam, lParam);

 }

}

//========================================================================

// Обработчик сообщения WM_LBUTTONDOWN

//========================================================================

#pragma argsused

void Cls_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)

    { MessageBox(NULL, "Нажата левая кнопка мыши",

   "Сообщение WM_LBUTTONDOWN", MB_OK | MB_ICONINFORMATION); }

//========================================================================

// Обработчик сообщения WM_RBUTTONDOWN

//========================================================================

#pragma argsused

void Cls_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)

    { MessageBox(NULL, "Нажата правая кнопка мыши",

  "Сообщение WM_RBUTTONDOWN", MB_OK | MB_ICONINFORMATION); }

//========================================================================

// Обработчик сообщения WM_KEYDOWN

//========================================================================

#pragma argsused

void Cls_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)

    { MessageBox(NULL, "Нажата клавиша на клавиатуре",

  "Сообщение WM_KEYDOWN", MB_OK | MB_ICONINFORMATION); }

//========================================================================

// Обработчик сообщения WM_DESTROY

//========================================================================

#pragma argsused

void Cls_OnDestroy(HWND hwnd) { PostQuitMessage(0);}

 Ресурсы приложения

 Загрузочный модуль приложения, кроме сегментов кода и данных, может также содержать дополнительные данные в специальном формате, называемые ресурсами приложения. К числу наиболее часто используемых типов ресурсов относятся следующие :

  •  пиктограмма ( ICON );
  •  курсор ( CURSOR );
  •  таблица текстовых строк ( STRINGTABLE );
  •  графическое изображение ( BITMAP );
  •  произвольные данные ( RCDATA );
  •  меню ( MENU и MENUEX );
  •  акселераторы меню ( ACCELERATORS );
  •  шаблон диалога ( DIALOG и DIALOGEX ).

22

Для  включения  ресурсов  в  загрузочный  модуль  приложения  необходимо  создать  текстовый  файл  описания  ресурсов (*.rc)  и  включить  его  в  состав  создаваемого  программного  проекта.  В  процессе  сборки  загрузочного модуля  этот  файл  обрабатывается  компилятором  ресурсов  ,  в  результате  чего  ресурсы  включаются  в состав  exe-файла  приложения  ,  там  же  формируется таблица  ресурсов ,  которая  используется  для  поиска  и загрузки  ресурсов  в  оперативную  память. Промежуточным результатом данного процесса является скомпилированный двоичный файл описания ресурсов (*.res).  

Файл  описания  ресурсов  может  быть  создан  простым  текстовым  редактором.  Кроме  того подобный  файл  является  результатом  работы  специальных  приложений,  например ,  Resource  Workshop. Редактор ресурсов Resource  Workshop встроен в систему программирования Borland C++ 5.01 и автоматически запускается при выборе пункта меню File|New|Resource project или при загрузке файла одного из типов, обрабатываемых редактором ресурсов. Кроме текстового файла описания ресурсов, Resource  Workshop работает также с двоичным файлом описания ресурсов (.res), а также файлами, содержащими отдельные типы ресурсов (*.ico, *.cur, *.bmp). Более того, возможно редактирование ресурсов непосредственно в загрузочном модуле (*.exe, *.dll) без повторной компиляции. Это позволяет  легко  создавать  локализованные  (переведенные  на  национальный  язык)  версии  приложений ,  редактировать  графические  изображения  или  любые  другие  ресурсы ,  даже не имея  исходных  текстов  приложения.

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

Для  создания  таблицы  текстовых  строк  файл  описания  ресурсов  должен  включать  оператор STRINGTABLE :

STRINGTABLE   [параметры загрузки] [тип  памяти]

BEGIN

StringID ,  ASCIIZ - строка

.   .   .

END

Операторы  BEGIN  и END  определяют границы  таблицы  строк  в  файле  определения  ресурсов.  Между ними  находятся строки  с  идентификаторами StringID .  Идентификаторы  строк, как и любых других ресурсов, являются  целыми константами,  заданными  непосредственно в операторе STRINGTABLE  или  через #define (при этом обычно используется префикс IDS_ перед именем). В качестве параметров загрузки  можно указывать значения  PRELOAD (ресурс   загружается   в  память одновременно с загрузкой  приложения) или LOADONCALL (используется  по  умолчанию ,  ресурс  загружается  в память  при обращении к  нему  со  стороны  приложения). Тип памяти,  выделяемой  при загрузке  ресурса ,  может быть FIXED (ресурс будет находиться в памяти  по постоянному  адресу) или MOVEABLE (ресурс может перемещаться  при выполнении Windows дефрагментации  памяти).  Дополнительно для перемещаемого  ресурса  можно указать тип DISCARDABLE (ресурс  может быть размещен  в  виртуальной  памяти). По умолчанию используется тип MOVEABLE DISCARDABLE. Возможно также указание параметра PURE, блокирующего возможность модификации участка памяти, занятого ресурсом (read only).

Для  загрузки  строки в  оперативную  память необходимо  использовать функцию LoadString, имеющую следующий прототип :

int LoadString(

   HINSTANCE  hInstance, // идентификатор приложения, содержащего ресурс 

   UINT  uID, // идентификатор ресурса

   LPTSTR  lpBuffer, // адрес буфера для ресурса

   int  nBufferMax  // размер буфера );  

23

Во всех функциях API, работающих с ресурсами, и, в частности, в функции LoadString, в качестве параметра – идентификатора ресурса следует использовать константу, заданную символическим именем. Если же для идентификатора используется целое число, этот параметр должен быть обработан макросом MAKEINTRESOURCE :

#define MAKEINTRESOURCE(i) (LPSTR)((DWORD)((WORD)(i))).

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

В загрузочный  модуль приложения  можно добавить ресурс  -  пиктограмму. Пиктограммы  хранятся  в  файлах  с  расширением  . ico ,  хотя  может  использоваться  и любое  другое  расширение.  Файл  с  пиктограммой  должен быть включен  в  состав компилируемого  программного  проекта.  Приложение Resource Workshop позволяет  создавать  пиктограммы  различного размера  ,  использующие  различное  число  цветов.  В одном  файле *.ico можно хранить несколько  пиктограмм  различного размера и с разным  количеством  цветов.  В этом случае  Windows при  выводе пиктограммы сделает правильный  выбор для  текущего режима  работы  видеоадаптера.

Для  включения  файла с  пиктограммой  в  состав  компилируемого проекта ,  необходимо  включить в  файл  описания  ресурсов  оператор ICON  :

IconID    ICON   [параметры загрузки] [тип  памяти]  имя  файла

Идентификатор пиктограммы  IconID можно указывать либо как символическое  имя  (рекомендуется использовать префикс IDI_ перед именем константы),   либо  как  целое число -  идентификатор  ресурса .  В качестве имени файла необходимо указать имя файла ,  содержащего  пиктограмму. После сборки проекта файл пиктограммы будет встроен в exe-файл приложения. Остальные  параметры  оператора ICON аналогичны  параметрам  оператора  STRINGTABLE .

Для  загрузки  пиктограммы  из файла приложения  используется  функция  LoadIcon :

HICON LoadIcon(

   HINSTANCE  hInstance, // идентификатор приложения, содержащего ресурс

   LPCTSTR  lpIconName  // идентификатор ресурса );

Функция  LoadIcon возвращает  идентификатор загруженной  пиктограммы  типа HICON,  который затем  используется  как  параметр для  функций  ,  работающих  с  данной  пиктограммой, или NULL при ошибке. Если первый параметр функции указан как NULL, функция загружает одну из пиктограмм, заданных вторым параметром и входящих в системные ресурсы :

IDI_APPLICATION – пиктограмма приложения по умолчанию; 

IDI_ASTERISK – информационное сообщение ( i );

IDI_EXCLAMATION – предупреждающее сообщение ( ! );

IDI_HAND – критическое предупреждающее сообщение ( STOP );

IDI_QUESTION – вопрос или запрос данных ( ? );

IDI_WINLOGO – логотип Windows.

Функция LoadIcon используется для загрузки пиктограмм из ресурсов приложения или системных ресурсов при регистрации класса окна. Кроме того, пиктограмма может быть отображена в окне приложения при помощи функции DrawIcon :

BOOL DrawIcon(

   HDC  hDC, // контекст отображения

   int  X, // x-координата верхнего левого угла

   int  Y, // y-координата верхнего левого угла

   HICON  hIcon  // идентификатор пиктограммы, возвращаемый LoadIcon ).

Предварительно следует получить контекст отображения. Операции с контекстом отображения будут рассмотрены при изучении интерфейса графических устройств GDI.

24

Вместо имени файла с пиктограммой в операторе ICON может в фигурных скобках быть задан непосредственно битовый образ пиктограммы в шестнадцатиричном виде.

Функция DestroyIcon используется  для  освобождения  оперативной  памяти ,  занятой  пиктограммой :

BOOL DestroyIcon( HICON  hIcon  // идентификатор пиктограммы, возвращаемый LoadIcon );

В  качестве  еще  одного  ресурса  приложение  может  использовать  курсор мыши -  битовое  изображение ,  аналогичное  пиктограмме.  Изображение  курсора  формируется  приложением  Resource  WorkShop в  файле с расширением  .cur . Аналогично  пиктограмме  ,  для  включения  курсора  в  файл  описания  ресурсов  используется  специальный  оператор CURSOR ,  все  параметры  которого имеют тот же смысл ,  что  и  параметры  оператора  ICON (для констант – идентификаторов ресурса рекомендуется использовать префикс IDC_ ):

CursorID    CURSOR   [параметры загрузки] [тип  памяти]  имя  файла

Для  загрузки курсора      из файла приложения  используется  функция LoadCursor :

HCURSOR LoadCursor(

   HINSTANCE  hInstance, // идентификатор приложения, содержащего ресурс

   LPCTSTR  lpCursorName  // идентификатор ресурса );

Функция  LoadCursor возвращает  идентификатор загруженного курсора типа HCURSOR или NULL при ошибке. Если первый параметр функции указан как NULL, функция загружает один из стандартных курсоров, заданных вторым параметром и входящих в системные ресурсы :

IDC_APPSTARTING – стандартная стрелка и маленькие песочные часы;

IDC_ARROW  - стандартная  стрелка;

IDC_CROSS  - перекрестие;

IDC_IBEAM  –  текстовый курсор;

IDC_ICON – пустая пиктограмма;

IDC_NO – перечеркнутая окружность;

IDC_SIZE – перекрестие из стрелок;

IDC_SIZEALL – аналогично IDC_SIZE

IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE – индикация изменения размера;

IDC_UPARROW – вертикальная стрелка;

IDC_WAIT – песочные часы.

Курсор, используемый приложением, определяется при регистрации класса окна через соответствующее поле структуры WNDCLASSEX. Функция SetCursor  позволяет  динамически  изменять форму  курсора ,  делая  текущим  загруженный в память курсор ,  идентификатор которого передан ей в качестве  параметра:

HCURSOR SetCursor( HCURSOR  hcur );

  Функция  ShowCursor позволяет  делать  курсор невидимым:

int ShowCursor( BOOL  bShow  // флаг видимости курсора );

Специальной  функции  для  рисования  курсора нет ,  так как  эта  операция выполняется  Windows  автоматически.  Однако ,  можно отобразить курсор в окне приложения,  передав  функции  DrawIcon идентификатор курсора  ,  возвращаемый  функцией  LoadCursor.  Эта  возможность не документирована и  может  не работать  в  последующих  версиях  Windows.

Функция DestroyCursor используется  для  освобождения  оперативной  памяти ,  занятой  курсором:

 BOOL DestroyCursor( HCURSOR  hCursor );

Вместо имени файла с курсором в операторе CURSOR может в фигурных скобках быть задан непосредственно битовый образ курсора в шестнадцатиричном виде.

К  ресурсам  приложения  также  относится  графическое изображение  типа bitmap , которое  может быть  сформировано  ResourceWorkshop в  файле  с  расширением .bmp .  

25

Для  включения bitmap в файл  описания  ресурсов  используется  оператор BITMAP ,  полностью  аналогичный  оператору  ICON  :

BitmapID    BITMAP   [параметры загрузки] [тип  памяти]  имя  файла   .

Для  загрузки изображения bitmap используется  функция  LoadBitmap ,  назначение  параметров  которой  аналогично  назначению  параметров функций  LoadIcon  и LoadCursor :

HBITMAP LoadBitmap( HINSTANCE  hInstance, LPCTSTR  lpBitmapName );

Если первый параметр функции указан как NULL, функция загружает один из стандартных битовых образов, заданных вторым параметром в виде  константы с префиксом OBM_ и входящих в системные ресурсы. Этих констант более 30 и мы не будем их здесь подробно описывать. При необходимости соответствующую информацию можно получить из описания функции LoadBitmap в справочной документации или в электронном справочнике WIN32 Programmer’s Reference.

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

BOOL DeleteObject( HGDIOBJ  hObject );

В  API WIN32 отсутствует  функция ,  позволяющая  прямо  отобразить  изображение  bitmap  в окне приложения. Изображение bitmap является графическим объектом GDI и для  его отображения  используется  ряд  функций  ,  работающих  с  контекстом  отображения .  Кроме того ,  следует  учитывать  наличие  двух  форматов  битовых  изображений  в Windows  (аппаратно-зависимый  формат DDB  и  аппаратно-независимый  формат DIB ).  Эти  вопросы  будут  рассмотрены  при  изучении  графического  интерфейса GDI в  следующих  главах.  Здесь  лишь напомним ,  что  произвольное  изображение  bitmap может  быть использовано в качестве  кисти для  фона  окна.  Идентификатор кисти  возвращается  функцией  CreatePatternBrush,  которой  в  качестве  параметра  передается  идентификатор  загруженного  изображения  bitmap :

HBRUSH CreatePatternBrush( HBITMAP  hbmp );

Вместо имени файла с битовым образом в операторе BITMAP может в фигурных скобках быть задан непосредственно битовый образ  в шестнадцатиричном виде.

Для загрузки из загрузочного модуля приложения пиктограммы, курсора или изображения bitmap в WIN32 введена универсальная функция LoadImage, обладающая более широкими возможностями, чем функции LoadIcon, LoadCursor и LoadBitmap:

HANDLE LoadImage(

   HINSTANCE  hinst,  // идентификатор приложения, содержащего ресурс

   LPCTSTR  lpszName, // идентификатор ресурса

   UINT  uType, // тип образа

   int  cxDesired, // желаемая ширина

   int  cyDesired, // желаемая высота

   UINT  fuLoad // флаги загрузки );

Подробное описание параметров этой функции можно получить из справочной документации или в электронном справочнике WIN32 Programmer’s Reference.

В качестве еще одного ресурса приложения могут использоваться произвольные данные. Для этого файл описания ресурсов должен содержать оператор следующего вида :

RId  [тип ресурса][параметры загрузки][тип памяти] имя файла

Параметры загрузки, тип памяти и идентификатор ресурса RId имеют тот же смысл, что и для ранее рассмотренных ресурсов. Тип ресурса указывается как произвольная строка символов (разумеется, нельзя использовать предопределенные типы ресурсов, например, ICON или BITMAP). Как вы уже наверное догадались, рассмотренные нами ранее операторы для включения ресурсов являются частными случаями данного оператора. Последним параметром указывается имя файла произвольного формата, содержащего ресурс.

26

Для загрузки ресурса произвольного типа в память вначале следует вызвать функцию FindResource :

HRSRC FindResource(

HMODULE  hModule,// идентификатор приложения

// (тип HINSTANCE совместим с HMODULE)

    LPCTSTR  lpName, // указатель на строку с именем ресурса  

   LPCTSTR  lpType  // указатель на строку с типом ресурса );

Эта функция может быть использована для поиска ресурса любого типа в загрузочном модуле. Для стандартных ресурсов в качестве третьего параметра должна быть указана одна из констант с префиксом RT_ . Подробное описание параметров этой функции можно получить из справочной документации или в электронном справочнике WIN32 Programmer’s Reference.

 Возвращаемое данной функцией значение используется в качестве второго параметра функции LoadResource для загрузки ресурса в память :

HGLOBAL LoadResource( HMODULE  hModule, HRSRC  hResInfo );

 Возвращаемое функцией значение является идентификатором блока памяти в куче (heap) приложения, в который загружен ресурс. Windows95, в отличие от Windows NT, автоматически не освобождает память, занятую ресурсом. Поэтому, по завершении работы с ресурсом его следует выгрузить из памяти при помощи функции FreeResource :

BOOL WINAPI FreeResource (HGLOBAL hGlb);

 

 Файл определения модуля

 В  состав  проекта в  системе  программирования  Borland  C++ , кроме файлов с исходным текстом программы (*.cpp) и файла описания ресурсов (*.rc), может  включаться  файл  определения  модуля ( *. def ) ,  в  котором  указывается имя загрузочного модуля  приложения , тип  exe-файла , атрибуты  сегментов  кода и  данных ,  объем  оперативной  памяти  для  стека и кучи. Если *.def файл не включен в проект, компилятор и компоновщик используют значения параметров, заданные по умолчанию. Ниже  приведен  пример файла  определения  модуля для рассмотренного выше примера приложения :

; =============================================================

; Файл определения модуля

; =============================================================

; Имя приложения

NAME HELLO1

; Описание приложения

DESCRIPTION 'Приложение HELLO1, (C) 1997, Sergey O. Derevenskov'

; Определение типа загрузочного модуля как приложения Windows

EXETYPE windows

; Программа-заглушка, которая будет записана в начало файла

; приложения. Эта программа получит управление

; при попытке запуска приложения в среде MS-DOS

STUB 'winstub.exe'

; Размер стека в байтах (минимальное значение 64К, по умолчанию – 1М)

STACKSIZE 65536

; Размер кучи (heap) приложения в байтах (минимальное значение 64К, по умолчанию – 1М )

HEAPSIZE 65536

; Атрибуты сегмента кода

CODE preload moveable discardable

; Атрибуты сегмента данных

DATA preload moveable multiple




1. Виды клинико-психологической диагностики
2. друг пойдем вместе
3. Катунское ул. Алтайская 2а
4. Хронический бронхит и эмфизема легких
5. Тема 3. Україна в ХІХ ст.
6.  Тікт~ртб~рыш б~лшектеуі 2
7. Проведение маркетинговых исследований на ЗАО Тульская обувная фабрика
8. МАГАРАЧ ГНИЛОМЕДОВА Нонна Володимирівна УДК 663
9. контрольная работа по дисциплине Муниципальное право РФ Выполнила студентка 4 курс ЮБ ~ 46 Б.html
10. ТЕМА РОБОТИ- Кількісна оцінка небезпек та ступінь прийнятності ризику
11. Полезные ископаемые
12. 313 время неделя Дисциплина Преподаватель
13. зарождение 199293 ~ воор
14. .1. ОЩУЩЕНИЯ КАК ПОЗНАВАТЕЛЬНЫЙ ПРОЦЕСС План
15. Тема 4. Методика производства СБЭ.
16. Реферат- Программа-имитатор
17. Дефекты, эффекты в стереотипах рекламной продукции
18. Федор Волков - отец русского театра
19. ламаизм в России
20. Гегелевский феномен современности или Насколько Гегель близок к модерн