на тему рефераты Информационно-образоательный портал
Рефераты, курсовые, дипломы, научные работы,
на тему рефераты
на тему рефераты
МЕНЮ|
на тему рефераты
поиск
Ознакомление с приложениями Windows
p align="left">При необходимости как-либо изменить реакцию окна на внешние события (переопределить принятую обработку сообщений) надо, во-первых, создать соответствующий объект класса (как в случае 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 );
}

При нормальной работе первый вызов функции GetWindowLong вернет указатель на объект, так что следующий шаг -- вызов функции-диспетчера. Таким образом дополнительные затраты ресурсов на реализацию ООП таким способом оказываются минимальными. В случае разработки классов-наследников от Win0 надо разработать собственную функцию-диспетчер, которая будет вместо процедуры DefWindowProc вызывать диспетчер класса-предка.

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



© 2003-2013
Рефераты бесплатно, курсовые, рефераты биология, большая бибилиотека рефератов, дипломы, научные работы, рефераты право, рефераты, рефераты скачать, рефераты литература, курсовые работы, реферат, доклады, рефераты медицина, рефераты на тему, сочинения, реферат бесплатно, рефераты авиация, рефераты психология, рефераты математика, рефераты кулинария, рефераты логистика, рефераты анатомия, рефераты маркетинг, рефераты релиния, рефераты социология, рефераты менеджемент.