Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Основы организации приложения в среде Windows

 

Итак, мы рассмотрим основы оранизации приложения в среде Windows и отметим несколько нюансов:

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

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

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

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

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

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

Несколько замечаний:

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

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

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

Эта особенность, вообще говоря совершенно неестественна. Дело в том, что функция WinMain() вызывается не непосредственно средой Windows, а промежуточным startup-кодом, являющимся частью run-time библиотеки (как и в DOS-приложениях). Этот код инициализирует стандартные переменные, кучу, стек, обнуляет неинициаизированные статические данные и вызывает конструкторы статических объектов до вызова функции WinMain().

Windows вызывает непосредственно этот startup-код, передавая ему нужные данные через регистры. То есть, в тот момент, когда вызываются конструкторы статических объектов, параметры функции WinMain() уже известны, и, более того, они даже сохранены в статических переменных. Однако по непонятным соображениям эти переменные не декларированы как публичные и являются локальными для startup-кода.

Замечание 4. Цикл обработки сообщений, в том виде, который рекомендован руководствами, не проверяет наличие окон у приложения. Для его завершения используется сообщение WM_QUIT, извлечение которого из очереди приводит к завершению цикла.

При этом требуется, что бы сообщение WM_QUIT посылалось с помощью функций PostMessage(), PostAppMessage() или PostQuitMessage() (только тогда оно попадает в очередь приложения). Обычно это сообщение посылается при уничтожении главного окна приложения (при обработке сообщения WM_DESTROY направленного этому окну). В более общем случае подразумевается последнее окно приложения.

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

while (IsWindow(hMainWnd)) {

if (!GetMessage(&msg, NULL, NULL, NULL)) break;

TranslateMessage(&msg);

DispatchMessage(&msg);

}

Если цикл обработки сообщений не будет прерван при уничтожении последнего окна приложения, то приложение останется активным, а у Вас уже не будет средств для его завершения, кроме выхода из Windows. При этом Ваше приложение исчезнет из списка приложений Task Manager (этот список, вообще говоря, содержит не задачи, а главные окна приложений).

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

Поясним это на примере создания окна. В случае “чистого” ООП для создания объекта он должен получить сообщение “create”, обработка которого приведет к его инициализации. В Windows сообщение WM_CREATE не выполняет никаких функций по созданию окна. Оно только информирует окно о том, что в это время окно создается средствами обычной функциональной библиотеки, например посредством функции CreateWindowEx(). Вы можете вообще игнорировать это сообщение, возвращать любой результат, вызывать или не вызывать функцию обработки по умолчанию - окно все равно будет создано.

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

Ситуация дополнительно усугубляется тем, что в документации как правило ничего не сообщается о том, какая обработка сообщения выполняется по умолчанию и, кроме того, по некоторым сообщениям приводятся некорректные (или неполные) сведения об их параметрах, выполняемым функциям, условиям возникновения и возвращаемом результате. Help, сопровождающий компиляторы Borland наиболее полный из всех (но не исчерпывающий).

Для окон, использующих в качестве процедуры обработки сообщений по умолчанию не DefWindowProc(), а иную функцию (например, DefMDIChildProc()), можно уточнить список сообщений обязательно подлежащих обработке по умолчанию. Однако это уточнение касается только тех сообщений, обработку которых для DefWindowProc() можно игнорировать, а для иных функций нельзя, список же того, что можно игнорировать для DefWindowProc(), а что нельзя остается неизвестным.

Замечание 6. В 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. Последний способ эффективен при минимизации главного окна приложения - так как минимизированное главное окно обычно обозначает передачу активности другому приложению.

Проверить состояние окна можно с помощью функции

BOOL IsIconic(hWnd);

При использовании системного меню превращению окна в иконку соответствует пункт ‘ Minimiz e’, порождающий системную команду SC_MINIMIZE (или синоним SC_ICON). (см. сообщение WM_SYSCOMMAND)

В этом случае используется сразу три разных термина для обозначения одного и того-же: minimize, close и iconic. При этом функция CloseWindow() является единственной, интерпретирующей термин close таким способом; в остальных случаях close означает действительно закрытие (иногда уничтожение) окна. Здесь же надо, чтто термин open, применяемый к минимизированному окну обозначает его максимизацию или восстановление нормальных размеров.

· нормальным, то есть мы видим (или можем увидеть) его рамку, мы можем перемещать окно по экрану. Когда окно находится в нормальном состоянии, то для него определены максимально и минимально допустимый размеры. Эти размеры нельзя путать с максимизированным и минимизированным состояниями. Максимальный размер нормального окна может даже превышать размер окна в максимизированном состоянии, минимальный размер это обычно такой размер, при котором окно еще может быть корректно представлено в виде окна.

Для перехода из минимизированого состояния к нормальному можно воспользоваться функцией OpenIcon(hWnd); или, как из минимизированого, так и из максимизированого состояния можно пользоваться функцией ShowWindow() с параметрами:

ShowWindow(hWnd, SHOW_OPENWINDOW);

ShowWindow(hWnd, SW_SHOWNORMAL);

ShowWindow(hWnd, SW_RESTORE);

ShowWindow(hWnd, SW_SHOWNOACTIVATE);

В документации (SDK Help) указано, что SW_RESTORE и SW_SHOWNORMAL эквивалентны, но это далеко не так - SW_RESTORE восстанавливает предыдущее состояние, а не нормальное. То есть, если Вы минимизировали окно из максимизированного, то SW_RESTORE вернет Вас к максимизированному окну, а SW_SHOWNORMAL - к нормальному. SW_SHOWNORMAL имеет синоним SHOW_OPENWINDOW.

Если окно восстанавливается или максимизируется из минимизированного состояния, то Ваше окно получит сообщение WM_QUERYOPEN - обрабатывая которое Вы можете разрешить или запретить дальнейшие действия. Если Вы возвращаете TRUE, то окно будет раскрыто, а если Вы вернете FALSE, то окно останется минимизированным.

Замечание 7. Дополнительно надо разобраться с несколькими терминами Windows, которые постоянно применяются, но никак в документации не описаны. Речь идет о хендлах копии приложения (HINSTANCE), модуля (HMODULE) и задачи (HTASK). Все эти хендлы используются разными функциями, причем разница между ними никак не поясняется. Поэтому нам надо рассмотреть эти хендлы более подробно:

· 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 возвращают так называемый псевдодескриптор процесса (потока). Псевдодескриптор – это некоторая величина, рассматриваемая в качестве дескритора текущего процесса (потока). То есть эта величина, применяемая в контексте другого процесса (потока), будет описывать его, а не данный поток. Для получения “настоящего” хендла надо воспользоваться функцией:

BOOL   DuplicateHandle(

hSourceProcess, hSourceHandle, hTargetProcess, lphTargetHandle,

fdwAccess, fInherit, fdwOptions

);

· HINSTANCE описывает копию приложения.

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

int         GetInstanceData(hInstance, pByte, cbData);

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

В Win32 для каждого запущенного приложения (т.е. процесса) выделяется виртуальное адресное пространство в 4G в едином сегменте. Поэтому данный хендл описывает не сегмент данных (который описывает весь 4G сегмент), а адрес в виртуальном пространстве, с которого был загружен данный модуль. В адресном пространстве одного процесса никаких других приложений не существует, поэтому этот хендл не может применяться для обнаружения других копий приложения и тем более для обмена данными между разными копиями приложений. В приложениях 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 API, работающие с хендлом модуля, корректно выполняются при передачи им хендла копии приложения, так что в документации возможен некоторый разнобой в используемых терминах.

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

 


Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...