скачать рефераты
  RSS    

Меню

Быстрый поиск

скачать рефераты

скачать рефератыУчебное пособие: Ознакомление с приложениями Windows

Описание функции Cls_OnCreate мы и ищем. Далее нам надо его просто скопировать в наше приложение и исправить при желании имя функции. Единственное, что остается не слишком удобным — так это вызов макроса–распаковщика — уж очень длинная строка получается. Для этого в windowsx.h содержится отдельный небольшой макрос:

  HANDLE_MSG( hWnd, uMsg, fn )

Используется он таким способом:

  switch ( uMsg ) {
      HANDLE_MSG( hWnd, WM_CREATE, Cls_OnCreate );
      // ...
      }

При этом он сам вставляет “case WM_xxx: return ...” и прочее. Важно следить, что бы в описании оконной процедуры параметры wParam и lParam назывались именно так и не иначе. Дело в том, что HANDLE_MSG при обращении к макросу HANDLE_WM_xxx указывает ему именно эти имена.

Чтобы закончить разговор о распаковщиках сообщений надо ответить только на два вопроса — зачем нужны макросы FORWARD_WM_xxx, определенные в том–же windowsx.h и как можно добавить распаковщики для каких–либо сообщений, там не определенных (например, нестандартных).

Рассмотренные пока макросы–распаковщики позволяли нам вызвать функцию–обработчик сообщения, имея для нее данные, упакованные в wParam и lParam. Однако иногда возникает другая необходимость имея параметры функции передать какое–либо сообщение. Для этого предназначены макросы FORWARD_WM_xxx. Для использования этих макросов, необходимо, что–бы функция, получающая параметры сообщения, имела следующий вид:

  LRESULT proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

Макросы FORWARD_WM_xxx получают в качестве параметров распакованные данные (как и функция–обработчик), упаковывают их в параметры сообщения и вызывают указанную функцию. По счастью практически все функции, которые придется вызывать с помощью макросов FORWARD_WM_xxx (SendMessage, PostMessage, DefWindowProc и пр.) соответствуют приведенному описанию.

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

  void Cls_OnSetFont( HWND hwnd, HFONT hfont, BOOL fRedraw );

  LRESULT WINAPI _export proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
      {
           switch ( uMsg ) {
           // ...
           HANDLE_MSG( hWnd, WM_SETFONT, Cls_OnSetFont );
           // ...
           }
      }

  // ...

  void Cls_OnSetFont( HWND hwnd, HFONT hfont, BOOL fRedraw )
      {
           HWND hwndChild = ...; // определение хендла дочернего окна
           FORWARD_WM_SETFONT( hwndChild, hfont, fRedraw, SendMessage );
      }

Здесь, кстати, можно было бы воспользоваться макросом SetWindowFont из того же windowsx.h. Этот макрос обращается к FORWARD_WM_SETFONT, как в рассмотренном примере, однако текст при этом становится более читаемым:

  void Cls_OnSetFont( HWND hwnd, HFONT hfont, BOOL fRedraw )
      {
           HWND hwndChild = ...; // определение хендла дочернего окна
           SetWindowFont( hwndChild, hfont, fRedraw );
      }

Добавление собственных распаковщиков не должно вызвать больших затруднений — достаточно только разработать реализации макросов HANDLE_WM_xxx и FORWARD_WM_xxx аналогично уже сделанному в windowsx.h.

Пример 1B использование распаковщиков сообщений

Этот пример иллюстрирует применение распаковщиков сообщений на примере простейшего приложения. Фактически он соответствует слегка измененному примеру 1A, в котором оконная процедура переписана для использования распаковщиков сообщений. Функция WinMain в этом примере осталась без изменений.

#define STRICT
#include <windows.h>
#include <windowsx.h>

#define        UNUSED_ARG(arg)      (arg)=(arg)

static char szWndClass[] = "test window";

BOOL Cls_OnCreate( HWND hwnd, LPCREATESTRUCT lpCreateStruct )
{
      UNUSED_ARG( hwnd );
      UNUSED_ARG( lpCreateStruct );
      return TRUE;
}

void Cls_OnPaint( HWND hwnd )
{
      PAINTSTRUCT    ps;

  BeginPaint( hwnd, &ps );
      TextOut( ps.hdc, 0, 0, "Hello, world!", 13 );
      EndPaint( hwnd, &ps );
}

void Cls_OnDestroy( HWND hwnd )
{
      UNUSED_ARG( hwnd );

  PostQuitMessage( 0 );
}

LRESULT WINAPI _export WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
      switch ( uMsg ) {
      HANDLE_MSG( hWnd, WM_CREATE, Cls_OnCreate );
      HANDLE_MSG( hWnd, WM_PAINT, Cls_OnPaint );
      HANDLE_MSG( hWnd, WM_DESTROY, Cls_OnDestroy );
      default: break;
      }
      return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

static BOOL init_instance( HINSTANCE hInstance )
{
      WNDCLASS             wc;

  wc.style =            0;
      wc.lpfnWndProc =    WinProc;
      wc.cbClsExtra =        0;
      wc.cbWndExtra =           0;
      wc.hInstance =          hInstance;
      wc.hIcon =                 LoadIcon( NULL, IDI_APPLICATION );
      wc.hCursor =       LoadCursor( NULL, IDC_ARROW );
      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
      wc.lpszMenuName = NULL;
      wc.lpszClassName = szWndClass;
      return RegisterClass( &wc ) == NULL ? FALSE : TRUE;
}

int PASCAL WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow )
{
      UNUSED_ARG( lpszCmdLine );
      MSG         msg;
      HWND     hWnd;

  if ( !hPrevInst ) {
           if ( !init_instance( hInst ) ) return 1;
      }

  hWnd= CreateWindow(
           szWndClass,                               // class name
           "window header",                       // window name
           WS_OVERLAPPEDWINDOW,                 // window style
           CW_USEDEFAULT,CW_USEDEFAULT,     // window position
           CW_USEDEFAULT,CW_USEDEFAULT,     // window size
           NULL,                                        // parent window
           NULL,                                        // menu
           hInst,                                    // current instance
           NULL                                   // user-defined parameters
      );

  if ( !hWnd ) return 1;

  ShowWindow( hWnd, nCmdShow );
      UpdateWindow( hWnd );

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

  return msg.wParam;
}

 

Немного об объектах

Здесь мы рассмотрим некоторые основные особенности реализации объектно–ориентированного программирования в Windows. В последнее время получили огромное распространение библиотеки объектов для создания приложений в среде Windows. Особенно широко они стали распространяться с развитием систем визуального программирования. Наибольшее распространение получили библиотеки объектов компаний

·   Borland — Object Windows Library (OWL), поддерживается компиляторами Borland C++ (рассматривается версия v2.5, сопровождающая компилятор Borland C/C++ v4.5).

·   Microsoft — Microsoft Foundation Classes (MFC), поддерживается наибольшим количеством компиляторов, среди которых Microsoft Visual C++, Watcom C++, Symantec C++ и другие (рассматривается версия v4.0, сопровождающая Visual C/C++ v4.0).

Такие библиотеки достаточно многофункциональны и громоздки, размер исполняемого файла, созданного с их помощью редко бывает меньше 300–400K. Конечно, при разработке больших систем, поддерживающих такие инструменты как OLE, DAO или WOSE, регистрирующих свои собственные типы файлов и т.д., использование этих библиотек может существенно сократить время, необходимое для разработки приложения.

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

В этом разделе мы рассмотрим простейшее приложение в среде Windows, построенное средствами ООП, причем все классы будут оригинальными — ни MFC, ни OWL не применяется. Это сделано для того, что бы “извлечь” на поверхность некоторые аспекты разработки классов для Windows–приложений. Здесь будут использоваться существенно упрощенные методы реализации объектов, по сравнению с “большими библиотеками.

Возможно, что в некоторых частных случаях использование такого подхода может оказаться и более продуктивным, чем применение MFC или OWL. Особенно, если ваше приложение похоже на  простейшее “Hello, world!” (в этом случае, правда, еще удобнее может быть обойтись совсем без классов).

 

Особенности ООП в Windows

На самом деле Windows не является настоящей объектно–ориентированной средой. Хотя окно и может быть названо объектом ООП, но лишь с достаточной натяжкой. Самое существенное отличие окна в Windows от объекта ООП заключается в том, что сообщение, обрабатываемое оконной функцией, во многих случаях не выполняет действий, а является “информационным”, указывая на то, что над окном выполняется та или иная операция какой–либо внешней функцией.

Поясним это на примере создания окна. В случае ООП для уничтожения объекта он должен получить сообщение “destroy”, обработка которого приведет к его уничтожению. В Windows сообщение WM_DESTROY не выполняет никаких функций по уничтожению окна. Оно только информирует окно о том, что в это время окно уничтожается средствами обычной функциональной библиотеки, например посредством функции DestroyWindow. Вы можете вообще игнорировать это сообщение, возвращать любое значение, вызывать или не вызывать функцию обработки по умолчанию — окно все равно будет уничтожено.

С точки зрения реализации объектов это приводит к тому, что большая часть методов представлена в двух вариантах — один вызывает соответствующую функцию API, а другой вызывается при обработке соответствующего сообщения. Так в случае функции DestroyWindow и сообщения WM_DESTROY для класса, представляющего окно, будет существовать два метода: метод Destroy и метод OnDestroy (названия методов условны, в реальных библиотеках они могут отличаться). Метод Destroy будет соответствовать вызову функции DestroyWindow, а метод OnDestroy — обработчику сообщения WM_DESTROY.

На этом, к сожалению, сложности не кончаются. Допустим, что вы хотите сделать так, что бы окно не уничтожалось. На первый взгляд надо переопределить метод Destroy, что бы он не вызывал функцию DestroyWindow; при этом вызов метода Destroy не будет уничтожать окно. Однако все гораздо сложнее:  окно по–прежнему может быть уничтожено прямым обращением к функции API — DestroyWindow. Более того, стандартные обработчики сообщений (то есть принадлежащие Windows, а не библиотеке классов) так и делают. Так стандартная обработка сообщения WM_CLOSE приводит к вызову функции DestroyWindow (а не метода Destroy). То есть для решения подобной задачи надо переопределить все методы объекта и все обработчики сообщений, которые ссылаются на соответствующую функцию API. Задача фактически не решаемая — особенно с учетом недостаточно подробной и точной документации.

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

 

Базовые классы приложения

Когда разрабатывается обычное приложение на C или C++ без использования классов, то надо разработать как функцию WinMain, определяющую работу приложения в целом, так и оконную процедуру, определяющую реакцию окна на внешние воздействия. При применении объектов эти функции возлагаются на методы классов. Естественно, что и в MFC, и в OWL существуют классы, предназначенные как для описания приложения в целом, так и для описания окон. Эти классы должны использоваться в качестве классов–предков, от которых порождаются классы, описывающие ваше приложение и его главное окно.

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

MFC

В библиотеке Microsoft Foundation Classes для описания приложения используются следующие классы:

Рисунок 5. Классы MFC, описывающие окно и приложение.

Данная версия MFC рассчитана на разработку многопотоковых приложений для Win32. Это наложило свой отпечаток на иерархию классов — в качестве одного из базовых выступает класс CWinThread, описывающий отдельный поток. И только на его основе построен класс CWinApp, описывающий приложение (в Win32 существует понятие основного потока, который обслуживает функцию WinMain, именно он и будет представлен объектом класса CWinThread, на основе которого порождается объект класса CWinApp).

OWL

В этой библиотеке иерархия классов несколько иная — библиотека рассчитана на разработку 16ти разрядных приложений Windows 3.x, не поддерживающую потоков.

Рисунок 6. Классы OWL, описывающие окно и приложение.

Окна ООП и окна Windows

При использовании библиотек классов надо как–то связывать экземпляры объектов, описывающих окна в приложении, с описанием того–же окна в Windows. Здесь надо учитывать, что, с одной стороны:

·   существуют методы классов, соответствующие вызову функций API;

·   существуют методы классов, соответствующие обработчикам сообщений;

а, с другой стороны:

·   существуют окна, созданные с помощью классов ООП;

·   существуют окна, созданные другими приложениями, модулями а также стандартными средствами Windows, не имеющими отношения к применяемой библиотеке.

Так, например, диалог “Открыть Файл” является стандартным диалогом Windows. Он создается и выполняется посредством вызова одной функции API — FileOpen. Эта функция сама, независимо от приложения и его библиотеки классов, создает необходимые окна и работает с ними. Однако у программиста может возникнуть необходимость как–то взаимодействовать с этим диалогом в процессе его работы.

Можно выделить четыре возможных ситуации, с которыми придется столкнуться во время работы приложения:

1.  должна быть вызвана функция API для окна, реализованного как объект класса;

2.  должна быть вызвана функция API для окна, не являющегося объектом класса;

3.  окно, реализованное как объект класса, получает сообщение — то есть надо вызвать соответствующий метод–обработчик этого сообщения;

4.  окно, не являющееся объектом класса, получает сообщение.

Случаи 1 и 2 решаются сравнительно просто — среди членов–данных класса должен присутствовать член, задающий хендл окна в Windows. В таком случае вызов функций API, нуждающихся в хендле, происходи элементарно. Небольшой нюанс связан с окнами, не являющимися объектами класса. Например, диалоги, включая стандартные и их элементы управления — кнопки, флажки и прочее, часто создаются как окна, принадлежащие Windows. То есть первоначально, в момент их создания, не существует объектов приложения, соответствующих этим окнам. Для этого в библиотеку вводятся средства создания объектов по хендлу. Эти средства могут несколько различаться в разных библиотеках.

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11


Новости

Быстрый поиск

Группа вКонтакте: новости

Пока нет

Новости в Twitter и Facebook

  скачать рефераты              скачать рефераты

Новости

скачать рефераты

© 2010.