Окна приложений в среде Windows
31 Основы организации приложения в среде 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 ); При использовании системного меню превращению окна в иконку соответствует пункт `Minimize', порождающий системную команду 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
Страницы: 1, 2, 3
|