47816 (597361), страница 12
Текст из файла (страница 12)
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 вызывать диспетчер класса–предка.
В примерах, сопровождающих компиляторы инициализация объекта и указателя на объект в структуре описания окна выполняется при обработке WM_CREATE. Это решение не является наилучшим — сообщение WM_CREATE далеко не самое первое из обрабатываемых сообщений, хотя, предусмотрев обработку сообщений с помощью DefWindowProc при неопределенном указателе, можно осуществлять инициализацию и при обработке WM_CREATE.
Конечно, этот пример крайне упрощен. Вообще, даже в простейших случаях, надо проводить контроль корректности данных, убедиться, что окно еще не существует перед вызовом CreateWindow в методе create, проверить on_create_ptr перед использованием и многое другое. Данный пример специально лишен всего этого, что бы в максимально открытом виде продемонстрировать простейшую схему.
Основы работы с памятью
Дополнительно надо разобраться с несколькими терминами Windows API, которые постоянно применяются, но очень плохо описаны в документации. Речь идет о хендлах копии приложения (HINSTANCE), модуля (HMODULE) и задачи (HTASK). Все эти хендлы используются разными функциями, причем разница между ними никак не поясняется. Помимо этого в Win32 API появилась пара дополнительных хендлов — хендл процесса и хендл потока, а также идентификаторы процесса и потока. При этом осталась все прежние понятия, часто изменившие смысл, но в документации по–прежнему не описанные (или описанные плохо).
Хендл задачи (HTASK), хендлы и идентификаторы процесса и потока
В Windows 3.x под задачей подразумевается конкретный запущенный процесс, для которого определены командная строка, текущая выполняемая инструкция, указатель на стек, переменные окружения, PDB (эквивалент префикса задачи (PSP) в среде DOS) и пр. Хендл задачи можно получить с помощью функции
HTASK GetCurrentTask( void );
В Win32 хендл задачи не применяется, а вместо него надо пользоваться хендлами и/или идентификаторами процесса и потока. Их можно получить с помощью функций:
HANDLE GetCurrentProcess( void );
HANDLE OpenProcess( fdwAccess, fInherit, dwIDProccess );
DWORD GetCurrentProcessId( void );
HANDLE GetCurrentThread( void );
DWORD GetCurrentThreadId( void );
Функции GetCurrentProcess и GetCurrentThread возвращают так называемый псевдодескриптор19 процесса (потока). Псевдодескриптор — это некоторая величина, рассматриваемая в качестве дескриптора текущего процесса (потока). То есть эта величина, применяемая в контексте другого процесса (потока), будет описывать его, а не данный поток. Для получения “настоящего” хендла из псевдодескриптора надо воспользоваться функцией:
BOOL DuplicateHandle(
hSourceProcess, hSourceHandle, hTargetProcess, lphTargetHandle,
fdwAccess, fInherit, fdwOptions
);
Хендл копии приложения (HINSTANCE)
В Windows 3.x этот хендл указывает на сегмент данных приложения, который содержит стек и локальную кучу. Для каждого запущенного приложения создается свой собственный сегмент данных, что позволяет однозначно определить конкретную копию приложения по его сегменту данных или организовать обмен данными между двумя копиями одного приложения. Так функция GetInstanceData позволяет скопировать данные, принадлежащие сегменту данных другой копии, в то–же самое место текущей копии.
int GetInstanceData( hInstance, pByte, cbData );
В функцию WinMain передается как хендл текущей копии приложения, так и хендл предыдущей копии, что позволяет организовать взаимодействие двух копий между собой, предотвратить запуск других копий, если это может привести к ошибке, или для выполнения действий, необходимых только в первой копии приложения.
В Win32 для каждого запущенного приложения (т.е. процесса) выделяется виртуальное адресное пространство в 4Г в едином сегменте. Поэтому данный хендл соответствует не сегменту данных (который описывает весь 4Г сегмент), а адресу в виртуальном пространстве, с которого был загружен данный модуль. В адресном пространстве одного процесса никаких других приложений не существует, поэтому этот хендл не может применяться для обнаружения других копий приложения и тем более для обмена данными между разными копиями приложений. В приложениях Win32 hPrevInstance всегда равен NULL, а хендл текущей копии приложения в большинстве случаев совпадает. При необходимости обнаружения других копий приложения надо использовать какие–либо иные методы, например функцию:
HWND FindWindow( lpszClassName, lpszWindowTitle );
Хендл окна в Win32 является уникальным и может идентифицировать конкретное окно в любом приложении.
Для обмена данными между приложениями (процессами) приходится передавать данные из адресного пространства одного процесса в адресное пространство другого. Для выполнения этих операций предусмотрено сообщение WM_COPYDATA. Когда Вы посылаете это сообщение окну, созданному другим процессом, указанные Вами данные копируются в адресное пространство другого процесса и могут быть прочитаны оконной процедурой окна–получателя. Этот механизм может применяться и для обмена данными между 16ти и 32х битовыми приложениями, однако для этого необходимо определить номер сообщения WM_COPYDATA и специальную структуру COPYDATASTRUCT для 16ти битовой платформы — так как файл windows.h не содержит этих определений:















