p align="left"> PostQuitMessage( 0 ); }LRESULT WINAPI _export WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch ( uMsg ) { HANDLE_MSG( hWnd, WM_CREATE, Cls_OnCreate ); HANDLE_MSG( hWnd, WM_PAINT, Cls_OnPaint ); HANDLE_MSG( hWnd, WM_DESTROY, Cls_OnDestroy ); default: break; } return DefWindowProc( hWnd, uMsg, wParam, lParam ); } static BOOL init_instance( HINSTANCE hInstance ) { WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = WinProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; 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; return RegisterClass( &wc ) == NULL ? FALSE : TRUE; } int PASCAL WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow ) { UNUSED_ARG( lpszCmdLine ); MSG msg; HWND hWnd; if ( !hPrevInst ) { if ( !init_instance( hInst ) ) return 1; } hWnd= CreateWindow( szWndClass, // class name "window header", // window name WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT,CW_USEDEFAULT, // window position CW_USEDEFAULT,CW_USEDEFAULT, // window size NULL, // parent window NULL, // menu hInst, // current instance NULL // user-defined parameters ); if ( !hWnd ) return 1; ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); while ( GetMessage( &msg, NULL, NULL, NULL ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } return msg.wParam; } Немного об объектахЗдесь мы рассмотрим некоторые основные особенности реализации объектно-ориентированного программирования в Windows. В последнее время получили огромное распространение библиотеки объектов для создания приложений в среде Windows. Особенно широко они стали распространяться с развитием систем визуального программирования. Наибольшее распространение получили библиотеки объектов компаний Borland -- Object Windows Library (OWL), поддерживается компиляторами Borland C++ (рассматривается версия v2.5, сопровождающая компилятор Borland C/C++ v4.5). Microsoft -- Microsoft Foundation Classes (MFC), поддерживается наибольшим количеством компиляторов, среди которых Microsoft Visual C++, Watcom C++, Symantec C++ и другие (рассматривается версия v4.0, сопровождающая Visual C/C++ v4.0). Такие библиотеки достаточно многофункциональны и громоздки, размер исполняемого файла, созданного с их помощью редко бывает меньше 300-400K. Конечно, при разработке больших систем, поддерживающих такие инструменты как OLE, DAO или WOSE, регистрирующих свои собственные типы файлов и т.д., использование этих библиотек может существенно сократить время, необходимое для разработки приложения. Эти библиотеки объектов, хотя и имеют огромное количество различий, неизбежно имеют и много общего, что определяется платформой, на которой они работают -- Windows. Для обеспечения эффективной работы приложений эти библиотеки вынуждены предусматривать простой механизм доступа посредством методов объектов к функциям API, что их неизбежно сближает между собой. Кроме того реализация и иерархия объектов в Windows неизбежно приводит к появлению чем-то сходной иерархии классов в библиотеках ООП. В этом разделе мы рассмотрим простейшее приложение в среде Windows, построенное средствами ООП, причем все классы будут оригинальными -- ни MFC, ни OWL не применяется. Это сделано для того, что бы “извлечь” на поверхность некоторые аспекты разработки классов для Windows-приложений. Здесь будут использоваться существенно упрощенные методы реализации объектов, по сравнению с “большими” библиотеками. Возможно, что в некоторых частных случаях использование такого подхода может оказаться и более продуктивным, чем применение MFC или OWL. Особенно, если ваше приложение похоже на простейшее “Hello, world!” (в этом случае, правда, еще удобнее может быть обойтись совсем без классов). Особенности ООП в WindowsНа самом деле Windows не является настоящей объектно-ориентированной средой. Хотя окно и может быть названо объектом ООП, но лишь с достаточной натяжкой. Самое существенное отличие окна в Windows от объекта ООП заключается в том, что сообщение, обрабатываемое оконной функцией, во многих случаях не выполняет действий, а является “информационным”, указывая на то, что над окном выполняется та или иная операция какой-либо внешней функцией.Поясним это на примере создания окна. В случае ООП для уничтожения объекта он должен получить сообщение “destroy”, обработка которого приведет к его уничтожению. В Windows сообщение WM_DESTROY не выполняет никаких функций по уничтожению окна. Оно только информирует окно о том, что в это время окно уничтожается средствами обычной функциональной библиотеки, например посредством функции DestroyWindow. Вы можете вообще игнорировать это сообщение, возвращать любое значение, вызывать или не вызывать функцию обработки по умолчанию -- окно все равно будет уничтожено.С точки зрения реализации объектов это приводит к тому, что большая часть методов представлена в двух вариантах -- один вызывает соответствующую функцию API, а другой вызывается при обработке соответствующего сообщения. Так в случае функции DestroyWindow и сообщения WM_DESTROY для класса, представляющего окно, будет существовать два метода: метод Destroy и метод OnDestroy (названия методов условны, в реальных библиотеках они могут отличаться). Метод Destroy будет соответствовать вызову функции DestroyWindow, а метод OnDestroy -- обработчику сообщения WM_DESTROY.На этом, к сожалению, сложности не кончаются. Допустим, что вы хотите сделать так, что бы окно не уничтожалось. На первый взгляд надо переопределить метод Destroy, что бы он не вызывал функцию DestroyWindow; при этом вызов метода Destroy не будет уничтожать окно. Однако все гораздо сложнее: окно по-прежнему может быть уничтожено прямым обращением к функции API -- DestroyWindow. Более того, стандартные обработчики сообщений (то есть принадлежащие Windows, а не библиотеке классов) так и делают. Так стандартная обработка сообщения WM_CLOSE приводит к вызову функции DestroyWindow (а не метода Destroy). То есть для решения подобной задачи надо переопределить все методы объекта и все обработчики сообщений, которые ссылаются на соответствующую функцию API. Задача фактически не решаемая -- особенно с учетом недостаточно подробной и точной документации.Все это приводит к тому, что для написания надежных приложений с использованием библиотек классов приходится очень хорошо представлять себе как работает Windows, каким функциям или сообщениям API соответствуют те или иные методы, а, кроме того, как это реализовано в конкретной библиотеке.Базовые классы приложенияКогда разрабатывается обычное приложение на C или C++ без использования классов, то надо разработать как функцию WinMain, определяющую работу приложения в целом, так и оконную процедуру, определяющую реакцию окна на внешние воздействия. При применении объектов эти функции возлагаются на методы классов. Естественно, что и в MFC, и в OWL существуют классы, предназначенные как для описания приложения в целом, так и для описания окон. Эти классы должны использоваться в качестве классов-предков, от которых порождаются классы, описывающие ваше приложение и его главное окно.Классы, описывающие приложение, в конечном итоге берут на себя работу, выполняемую функцией WinMain, -- они регистрируют специальный класс окон в Windows, организуют создание и отображение главного окна приложения и цикл обработки сообщений, организуют инициализацию и создание необходимых объектов при запуске приложения и их уничтожение при завершении. Причем, так как заранее не известно, как будут называться классы, разработанные пользователем, и сколько и каких окон надо будет создавать, то библиотеки предлагают только лишь базовые классы, на основе которых надо разработать свои собственные классы для описания приложения и окна.MFCВ библиотеке Microsoft Foundation Classes для описания приложения используются следующие классы:Рисунок 5. Классы MFC, описывающие окно и приложение. Данная версия MFC рассчитана на разработку многопотоковых приложений для Win32. Это наложило свой отпечаток на иерархию классов -- в качестве одного из базовых выступает класс CWinThread, описывающий отдельный поток. И только на его основе построен класс CWinApp, описывающий приложение (в Win32 существует понятие основного потока, который обслуживает функцию WinMain, именно он и будет представлен объектом класса CWinThread, на основе которого порождается объект класса CWinApp). OWLВ этой библиотеке иерархия классов несколько иная -- библиотека рассчитана на разработку 16ти разрядных приложений Windows 3.x, не поддерживающую потоков. Рисунок 6. Классы OWL, описывающие окно и приложение. Окна ООП и окна WindowsПри использовании библиотек классов надо как-то связывать экземпляры объектов, описывающих окна в приложении, с описанием того-же окна в Windows. Здесь надо учитывать, что, с одной стороны:существуют методы классов, соответствующие вызову функций API; существуют методы классов, соответствующие обработчикам сообщений; а, с другой стороны: существуют окна, созданные с помощью классов ООП; существуют окна, созданные другими приложениями, модулями а также стандартными средствами Windows, не имеющими отношения к применяемой библиотеке. Так, например, диалог “Открыть Файл” является стандартным диалогом Windows. Он создается и выполняется посредством вызова одной функции API -- FileOpen. Эта функция сама, независимо от приложения и его библиотеки классов, создает необходимые окна и работает с ними. Однако у программиста может возникнуть необходимость как-то взаимодействовать с этим диалогом в процессе его работы. Можно выделить четыре возможных ситуации, с которыми придется столкнуться во время работы приложения: должна быть вызвана функция API для окна, реализованного как объект класса; должна быть вызвана функция API для окна, не являющегося объектом класса; окно, реализованное как объект класса, получает сообщение -- то есть надо вызвать соответствующий метод-обработчик этого сообщения; окно, не являющееся объектом класса, получает сообщение. Случаи 1 и 2 решаются сравнительно просто -- среди членов-данных класса должен присутствовать член, задающий хендл окна в Windows. В таком случае вызов функций API, нуждающихся в хендле, происходи элементарно. Небольшой нюанс связан с окнами, не являющимися объектами класса. Например, диалоги, включая стандартные и их элементы управления -- кнопки, флажки и прочее, часто создаются как окна, принадлежащие Windows. То есть первоначально, в момент их создания, не существует объектов приложения, соответствующих этим окнам. Для этого в библиотеку вводятся средства создания объектов по хендлу. Эти средства могут несколько различаться в разных библиотеках. Например метод CWnd* CWnd::FromHandle( HWND ), существующий в MFC, создает специальный объект, описывающий окно, связывает его с указанным хендлом и возвращает указатель на него. Этот объект считается “временным” -- спустя некоторое время MFC сама его уничтожит. В OWL аналогичного эффекта можно добиться используя специальную форму конструктора объекта TWindow. Случай 3 существенно более сложный. Когда окно получает сообщение, Windows вызывает зарегистрированную оконную процедуру, причем для этой процедуры передается только хендл окна и параметры сообщения. Указатель на объект приложения, описывающего это окно, остается в момент получения сообщения неизвестным! Обычно в библиотеках классов используют следующий способ: при запуске приложения в Windows регистрируется оконная процедура, определяющая специальный класс окон, используемый всей иерархией классов данной библиотеки. Эта оконная процедура получает сообщение и выполняет две операции: находит связанное с данным хендлом окно -- для этого библиотеки классов поддерживают специальные таблицы соответствия хендлов окон описаниям этих окон в приложении распределяет пришедшее сообщение соответствующему (обычно виртуальному) методу-обработчику этого сообщения. Для задания методов-обработчиков конкретных сообщений вводятся специальные таблицы отклика или таблицы трансляции сообщений (response table, message map table). Когда вы разрабатываете новый класс окон, вы для него должны разработать такую таблицу, в которой должны быть указаны соответствия приходящих сообщений и вызываемых методов (конечно, если это не сделано в классе-предке). Случай 4 вообще самый сложный -- в нормальных условиях библиотеки его не обрабатывают, так как для получения сообщений окна, не являющегося объектом класса, необходимо подменить процедуру обработки сообщений. Это накладывает ограничения на применение методов-обработчиков сообщений -- для окон, не созданных как объекты класса, эти методы вызываться не будут. В случае MFC названия таких методов обычно начинаются на On..., например OnDestroy; а в случае OWL -- на Ev..., например EvDestroy. Часто можно так организовать приложение, что переопределять эти методы просто не потребуется, однако это не всегда удобно и возможно.
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
|