Смекни!
smekni.com

Ознакомление с приложениями Windows (стр. 16 из 18)

В примерах, сопровождающих компиляторы инициализация объекта и указателя на объект в структуре описания окна выполняется при обработке 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 не содержит этих определений:

#define WM_COPYDATA 0x004A

typedef struct tagCOPYDATASTRUCT {
DWORD dwData;
DWORD cbData;
LPVOID lpData;
} COPYDATASTRUCT, FAR* PCOPYDATASTRUCT;

Хендл модуля (HMODULE) 

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

HMODULE GetModuleHandle( lpszFileName );
int GetModuleFileName( hInstance, lpsBuffer, cbMaxSize );

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

Подробнее о приложении (2)

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

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

Обычно, хотя это и не обязательно, функция WinMain реализует следующую схему:

· выполняются требуемые инициализационные действия

· создается главное окно приложения, для чего часто регистрируется новый класс окон (оконная функция);

· организуется цикл обработки сообщений приложения. Обычно цикл завершается при закрытии главного окна приложения;

· после завершения цикла обработки сообщений выполняется освобождение занятых ресурсов, после чего функция WinMain заканчивается.

Замечание 1. Если приложение содержит непродолжительные (порядка 1 сек.) операции, не требующие взаимодействия с пользователем (например, только файл–ориентированный ввод–вывод или настройка другого приложения), то эти действия могут быть выполнены непосредственно функцией WinMain без создания окон и без организации цикла обработки сообщений.

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

Замечание 3. В момент вызова функции WinMain ей, через аргументы, передается несколько параметров, например хендл копии приложения hInstance. До вызова WinMain приложение “не знает” этих данных. Поэтому могут возникать сложности с использованием статических конструкторов объектно–ориентированных языков (C++).

!!!!!!!!Фокус ввода!!!!!!

В Windows существует определенная путаница терминов. Попробуем разобраться с некоторыми из них. Как известно, окно может находиться в нескольких состояниях:

Максимизированном, то есть быть “распахнутым” на весь экран — при этом внутренняя область окна занимает весь экран, кроме небольших полос сверху — где размещается заголовок и меню, снизу — горизонтальная полоса прокрутки и справа — вертикальная полоса прокрутки; рамка окна находится за пределами экрана, мы ее не видим, перемещение окна невозможно.

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

ShowWindow( hWnd, SHOW_FULLSCREEN );
ShowWindow( hWnd, SW_SHOWMAXIMIZED );
ShowWindow( hWnd, SW_MAXIMIZE );

максимизированное окно всегда активно и имеет фокус ввода. Когда какое–либо окно максимизируется, все остальные верхние окна получают сообщение WM_SIZE, информирующее о том, что они “закрыты” сверху максимизированным окном.

Мы можем узнать, является ли наше окно максимизированным с помощью функции

BOOL IsZoomed( hWnd );

При использовании системного меню операции максимизации окна соответствует пункт Maximize, выбор которого порождает системную команду SC_MAXIMIZE (или синоним SC_ZOOM). (см. сообщение WM_SYSCOMMAND)

Здесь вместо термина maximize может использоваться zoom.

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

ShowWindow( hWnd, SHOW_ICONWINDOW );
ShowWindow( hWnd, SW_SHOWMINIMIZED );
ShowWindow( hWnd, SW_SHOWMINNOACTIVE );
ShowWindow( hWnd, SW_MINIMIZE );
CloseWindow( hWnd );

Разные способы, использующие ShowWindow, отличаются только правилами активации окна. SW_SHOWMINIMIZED и SHOW_ICONWINDOW отображает окно в виде пиктограммы, делая его активным; SW_SHOWMINNOACTIVE не изменяет текущего активного окна; SW_MINIMIZE (как и функция CloseWindow) делает активным следующее окно в списке Windows. Последний способ эффективен при минимизации главного окна приложения — так как минимизированное главное окно обычно обозначает передачу активности другому приложению.