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

Меню

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

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

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

Например метод CWnd* CWnd::FromHandle( HWND ), существующий в MFC, создает специальный объект, описывающий окно, связывает его с указанным хендлом и возвращает указатель на него. Этот объект считается “временным спустя некоторое время MFC сама его уничтожит. В OWL аналогичного эффекта можно добиться используя специальную форму конструктора объекта TWindow.

Случай 3 существенно более сложный. Когда окно получает сообщение, Windows вызывает зарегистрированную оконную процедуру, причем для этой процедуры передается только хендл окна и параметры сообщения. Указатель на объект приложения, описывающего это окно, остается в момент получения сообщения неизвестным!

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

·   находит связанное с данным хендлом окно — для этого библиотеки классов поддерживают специальные таблицы соответствия хендлов окон описаниям этих окон в приложении

·   распределяет пришедшее сообщение соответствующему (обычно виртуальному) методу–обработчику этого сообщения.

Для задания методов–обработчиков конкретных сообщений вводятся специальные таблицы отклика или таблицы трансляции сообщений (response table, message map table). Когда вы разрабатываете новый класс окон, вы для него должны разработать такую таблицу, в которой должны быть указаны соответствия приходящих сообщений и вызываемых методов (конечно, если это не сделано в классе–предке).

Случай 4 вообще самый сложный — в нормальных условиях библиотеки его не обрабатывают, так как для получения сообщений окна, не являющегося объектом класса, необходимо подменить процедуру обработки сообщений.

Это накладывает ограничения на применение методов–обработчиков сообщений — для окон, не созданных как объекты класса, эти методы вызываться не будут. В случае MFC названия таких методов обычно начинаются на On..., например OnDestroy; а в случае OWL — на Ev..., например EvDestroy. Часто можно так организовать приложение, что переопределять эти методы просто не потребуется, однако это не всегда удобно и возможно.

При необходимости как–либо изменить реакцию окна на внешние события (переопределить принятую обработку сообщений) надо, во–первых, создать соответствующий объект класса (как в случае 2). Во–вторых обычное окно, создаваемое Windows (например, какой–либо элемент управления диалогом — кнопка, флажок и пр.) или другим приложением, использует собственную оконную процедуру. Эта процедура, естественно, никак не связана с библиотекой ООП, применяемой вашим приложением. Таким образом, при получении окном сообщений, вызывается только лишь его собственная оконная процедура, не обращающаяся к методам класса. То есть необходимо осуществить подмену оконной процедуры (в Windows это называется порождением подкласса окон subclass) с помощью специальных методов библиотек, выполняющих эту операцию: SubclassWindowFunction в OWL или SubclassWindow в MFC. После этого новая оконная функция будет обращаться к методам класса для обработки сообщений, а в качестве стандартной обработки будет использоваться та оконная функция, которая использовалась окном до ее подмены.

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

·   при создании объекта класса лучше использовать один из базовых классов (CWnd или TWindow), так как все порожденные от них классы переопределяют значительно большее число методов, предполагая стандартную обработку сообщений, реализованную в DefWindowProc, а не в той процедуре, которую вы подменили. Это может привести к конфликтам между новой обработкой событий и прежней оконной процедурой. Особенно опасна ошибка в назначении класса библиотека классов и компилятор никак не смогут проверить вас и предупредить, если вы, скажем, для кнопки, создадите объект класса “список” (LISTBOX). При такой ошибке конфликт практически неизбежен. В любом случае надо хорошо представлять себе, для какой стандартной оконной процедуры реализован какой класс библиотеки ООП и обработку каких сообщений он переопределяет, прежде чем решиться на подмену оконной процедуры.

·   в случае Win32 для окон, созданных другим приложением, оконные процедуры (используемая окном и назначаемая вами) размещается в различных адресных пространствах разных процессов. Обращение из другого процесса по новому адресу функции приведет, скорее всего, к ошибке — так как этот адрес задан в адресном пространстве вашего приложения, а что находится в адресном пространстве другого процесса по этому адресу вам неизвестно. Решить эту проблему можно, выделяя описание объекта класса и его процедуры в отдельную DLL, а затем внедряя ее в адресное пространство процесса, создавшего окно. Однако этот прием существенно сложнее.

Пример 1C — использование собственных классов

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

Коротко рассмотрим реализацию этого способа: вместо ведения таблиц соответствия хендлов объектам приложения можно хранить необходимые данные непосредственно в структуре описания окна в Windows (см. “Регистрация класса окон”). Так как доступ к этим данным осуществляется только с помощью функций, то размещать там все описание окна нецелесообразно, зато в этой структуре можно разместить указатель на связанный объект. Отсюда следует ограничение — этот метод будет работать только с теми окнами, в структуре описания которых в Windows зарезервировано специальное поле для указателя. Это могут быть только окна, созданные нами.

Рисунок 7. Поиск метода–обработчика сообщения в примере.

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

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

Рассматриваемый пример состоит из 3х файлов: 1c.h — общий заголовочный файл, содержащий описания базовых классов; 1c_cls.cpp — методы и статические данные базовых классов; 1c_main.cpp — собственно само приложение: описание собственных классов и их методов, а также функция WinMain.

Файл 1c.h

#define STRICT
#include <windows.h>

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

class Win0 {
 protected:
      HWND     hwnd;  

  virtual LRESULT      dispatch( UINT, WPARAM, LPARAM );
      virtual BOOL       OnCreate( LPCREATESTRUCT );
      virtual void          OnDestroy( void ) = 0;
      virtual void          OnPaint( HDC hdc ) = 0;  

 public:
      Win0( void );
      ~Win0( void );
      BOOL            create( char* );
      void                destroy( void );
      void                update( void ) { UpdateWindow( hwnd ); }
      void                show( int nCmdShow ) { ShowWindow( hwnd, nCmdShow ); }

  friend LONG WINAPI _export Win0proc( HWND, UINT, WPARAM, LPARAM );
};

class App0 {
 public:
      static HINSTANCE   hInstance;
      static HINSTANCE   hPrevInstance;
      static LPSTR       lpszCmdLine;
      static int               nCmdShow;

  App0( HINSTANCE, HINSTANCE, LPSTR, int );
      ~App0( void );

  BOOL            init( void );
      int                   run( void );
      void                release( void );
};

Файл 1c_cls.cpp

#include "1c.h"
HINSTANCE App0::hInstance;
HINSTANCE App0::hPrevInstance;
LPSTR           App0::lpszCmdLine;
int             App0::nCmdShow;

static char        szWndClass[]= "test window class";
static Win0*   on_create_ptr;

Win0::Win0( void )
{
      hwnd = NULL;
}

Win0::~Win0( void )
{
      destroy();
}

LRESULT WINAPI _export Win0proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
      Win0* pwin;

  pwin = (Win0*)GetWindowLong( hWnd, 0 );
      if ( !pwin ) {
           SetWindowLong( hWnd, 0, (LONG)(Win0 FAR*)(pwin = on_create_ptr) );
           pwin->hwnd = hWnd;
      }
      return pwin->dispatch( uMsg, wParam, lParam );
}

LRESULT Win0::dispatch( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
      PAINTSTRUCT        ps;  

  switch ( uMsg ) {
      case WM_CREATE: return OnCreate( (LPCREATESTRUCT)lParam ) ? 0L : -1L;
      case WM_PAINT:     OnPaint( BeginPaint( hwnd, &ps ) ); EndPaint( hwnd, &ps ); return 0L;
      case WM_DESTROY:    OnDestroy(); return 0L;
      default: break;
      }
      return DefWindowProc( hwnd, uMsg, wParam, lParam );
}

void Win0::destroy( void )
{
      if ( IsWindow( hwnd ) ) DestroyWindow( hwnd );
      hwnd = (HWND)NULL;
}

BOOL Win0::create( char* title )
{
      on_create_ptr = this;
      CreateWindow(
           szWndClass,                               // class name
           title,                                      // window name
           WS_OVERLAPPEDWINDOW,                 // window style
           CW_USEDEFAULT,CW_USEDEFAULT,     // window position
           CW_USEDEFAULT,CW_USEDEFAULT,     // window size
           NULL,                                        // parent window
           NULL,                                        // menu
           hInstance,                             // current instance
           NULL                                   // user-defined parameters
      );
      on_create_ptr = (Win0*)NULL;
      return IsWindow( hwnd );
}

BOOL Win0::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
      UNUSED_ARG( lpCreateStruct );

  return TRUE;
}

App0::App0( HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmd, int nShow )
{
      hInstance =          hInst;
      hPrevInstance =  hPrev;
      lpszCmdLine =          lpszCmd;
      nCmdShow =            nShow;
}

App0::~App0( void )
{
}

BOOL App0::init( void )
{
      static BOOL         done;
      WNDCLASS       wc;

  if ( !done && !hPrevInstance ) {
           wc.style =            0;
           wc.lpfnWndProc =    Win0proc;
           wc.cbClsExtra =        0;
           wc.cbWndExtra =     sizeof(LONG);
           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;
           done = RegisterClass( &wc ) ? TRUE : FALSE;
      }

  return done;
}

int App0::run( void )
{
      MSG  msg;

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

   return msg.wParam;
}

void App0::release( void )
{
}

Файл 1c_main.cpp

#include "1c.h"

class MainWindow : public Win0 {
 protected:
      virtual void    OnDestroy( void );
      virtual void    OnPaint( HDC hdc );

 public:
      MainWindow( void );
      ~MainWindow( void );
};

class MyApp : public App0 {
 protected:
      MainWindow       wnd;

 public:
   MyApp( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow );
   ~MyApp( void );
   BOOL init( void );
};

MainWindow::MainWindow( void ) : Win0()
{
}

MainWindow::~MainWindow( void )
{
}

void MainWindow::OnDestroy( void )
{
      PostQuitMessage( 0 );
}

void MainWindow::OnPaint( HDC hdc )
{
      TextOut( hdc, 0, 0, "Hello, world!", 13 );
}

MyApp::MyApp( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow )
      : App0( hInst, hPrevInst, lpszCmdLine, nCmdShow )
{
}

MyApp::~MyApp( void )
{
}

BOOL MyApp::init( void )
{
      if ( App0::init() ) {
           if ( wnd.create( "window header" ) ) {
                 wnd.show( nCmdShow );
                 wnd.update();
                 return TRUE;
           }
      }
      return FALSE;
}

int PASCAL WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow )
{
      int       a;
      MyApp    app( hInst, hPrevInst, lpszCmdLine, nCmdShow );

  if ( app.init() ) {
           a = app.run();
      } else a = -1;
      app.release();

  return a;
}

Обзор примера 1C

Пример содержит два базовых класса: App0 описывает приложение и Win0 — описывает окно.

Класс App0 содержит 4 члена–данных: hInstance, hPrevInstance, lpszCmdLine и nCmdShow, которые являются аргументами функции WinMain. Интереснее разобраться с методами, описанными в этом классе. Конструктор просто инициализирует члены–данные для использования в последующем; деструктор вообще ничего не делает. Пара методов init и release предназначена для переопределения в дальнейшем — метод init должен выполнять специфичную инициализацию приложения, а метод release — операции при завершении. В классе App0 метод init осуществляет регистрацию оконной процедуры (в терминологии Windows — класса), которая будет применяться данным приложением. Метод run выполняет цикл обработки сообщений.

Класс Win0 содержит только один член–данные hwnd — хендл окна. Конструктор устанавливает значение хендла окна равным NULL (окно не создано), деструктор проверяет существование окна и, при необходимости, закрывает его. Методы create, destroy, update и show соответствуют функциям API: CreateWindow, DestroyWindow, UpdateWindow и ShowWindow. Методы OnCreate, OnDestroy и OnPaint соответствуют обработчикам сообщений WM_CREATE, WM_DESTROY и WM_PAINT. Метод dispatch является диспетчером, который распределяет пришедшие сообщения по соответствующим методам–обработчикам.

В том–же классе декларирована дружественная функция Win0proc, которая является собственно оконной процедурой.

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

  LONG WINAPI _export Win0proc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
      {
           Win0* pwin;

        pwin = (Win0*)GetWindowLong( hWnd, 0 );   // получаем указатель на объект
           if ( !pwin ) {              // указатель равен NULL — объект только создается
                 // инициализируем объект и указатель на него
                 SetWindowLong( hWnd, 0, (LONG)(Win0 FAR*)(pwin = on_create_ptr) );
                 pwin->hwnd = hWnd;
           }
           // вызываем виртуальную функцию-диспетчер
           return pwin->dispatch( uMsg, wParam, lParam );
      }

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


Новости

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

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

Пока нет

Новости в Twitter и Facebook

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

Новости

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

© 2010.