Почему Microsoft не может инкапсулировать библиотеки для C эффективно? К чему все эти непроизводительные затраты?
Сначала я был очарован архитектурой документ/вид. Я еще не знал то, что до этого был соответствующий "образец" (pattern). А через MFC было мое первое с ним столкновение. Поскольку мои программы стали беспорядочными циклическими зависимостями, я, реализуя парадигму документ-вид, понял, что MFC ужасна. По крайней мере, в их реализации этой идеи!! Когда-либо обратите внимание, что примеры программ очень малы в книгах. Большинство книг никогда не касается того, как реализовать взаимодействия больших наборов классов. Что было бы, если бы я имел большую программу с 1000 видами, и 1000 документами, и все они находились бы во взаимосвязи. Все передавли бы сообщения. Что, если бы это была многопотчная или распределенная программа…., Какой был бы беспорядок при использовании архитектуры документ/вид, реализованной с использованием MFC. Вот такие пироги!
«Hello Windows!» в классовой обертке
Перевод А. И. Легалова
Англоязычный оригинал находится на сервере компании Reliable Software
Прежде, чем приступить к программированию для Windows, необходимо понять, как выполняется представленная здесь самая простая программа. Обратите внимание: это — Win32-программа. Она будет выполняться под управлением Windows 95 и Windows NT (если кто-то хочет, чтобы Вы программировали для 16-разрядной платформы, то он должен платить Вам вдвое больше!). Windows API вызовы окрашены в синий цвет, а специфические для Windows типы данных окрашены зеленым. Я буду также ставить два двоеточия перед обращениями к функциям API. В C++ это означает вызов глобальной функции и позволяет, в ряде случаев, избавиться от неоднозначности.
Исходные тексты программы расположены на сервере Reliable software (чужого стараюсь не держать [А.Л.]). Не забудьте откомпилировать его как приложение Windows. Например, в Visual C++ выберите File.New.Projects.Win32 Application. Иначе вы получите ошибку: нерешенный внешний _main. (я обеспечил проектным файлом тех, кто используют MS VC++ 6.0)
Сначала Вы должны определить классы окон, которые будут отображаться вашим приложением. В данном случае мы отобразим только одно окно (WinClass), но тем не менее, мы должны дать Windows некоторую минимальную информацию относительно его класса. Наиболее важная часть WinClass — адрес процедуры обратного вызова, или оконной процедуры (WindowProcedure). Windows, в соответствии с внутренней организацией, вызывает нас — он посылает сообщения нашей программе, вызывая оконную процедуру.
Обратите внимание на объявление оконной процедуры. Windows будет вызывать ее, передавая дескриптор текущего окна и два элемента данных, связанных с сообщением (параметры сообщения): WPARAM и LPARAM.
В WinClass мы также должны определить дескриптор экземпляра программы HINSTANCE, курсор мыши (мы лишь загружаем стандартный курсор – стрелку), кисть, чтобы закрасить фон окна (мы выбрали заданный по умолчанию цвет окна) и имя нашего класса.
После заполнения всех полей структуры WNDCLASS мы регистрируем класс окна в Windows.
#include <windows.h>
LRESULT CALLBACK WindowProcedure(HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam);
class WinClass {
public:
WinClass (WNDPROC winProc, char const * className, HINSTANCE hInst);
void Register() {
::RegisterClass (&_class);
}
private:
WNDCLASS _class;
};
WinClass::WinClass(WNDPROC winProc, char const * className, HINSTANCE hInst) {
_class.style = 0;
_class.lpfnWndProc = winProc; // оконная процедура: обязательна
_class.cbClsExtra = 0;
_class.cbWndExtra = 0;
_class.hInstance = hInst; // владелец класса: обязательный
_class.hIcon = 0;
_class.hCursor = ::LoadCursor (0, idc_arrow); // optional
_class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // optional
_class.lpszMenuName = 0;
_class.lpszClassName = className; // обязательно
}
Как только Класс Окна зарегистрирован, мы можем продолжить создание окна. Для этого вызываем функцию API CreateWindow. У нее много параметров: имя только что зарегистрированного класса окна, заголовок, появляющийся в названии окна, стиль, положение на экране и размер, а также дескриптор экземпляра приложения. Остальные параметры в данный момент оставлены обнуленными.
Окно не будет появляться на экране до тех пор, пока Вы не скажете, чтобы Windows показал его.
class WinMaker {
public:
WinMaker(): _hwnd (0) {}
WinMaker (char const* caption, char const* className, HINSTANCE hInstance);
void Show (int cmdShow) {
::ShowWindow(_hwnd, cmdshow);
::UpdateWindow(_hwnd);
}
protected:
HWND _hwnd;
};
WinMaker::WinMaker(char const * caption, char const * className, HINSTANCE hInstance) {
_hwnd = :: CreateWindow(
className, // имя регистрируемого оконного класса
caption, // заголовок окна
WS_OVERLAPPEDWINDOW, // стиль окна
CW_USEDEFAULT, // позиция x
CW_USEDEFAULT, // позиция y
CW_USEDEFAULT, // ширина
CW_USEDEFAULT, // высота
0, // handle to parent window
0, // handle to menu
hInstance, // дескриптор экземпляра
0); // дата создания (window creation data)
}
Windows программа управляется событиями. Это означает, что Вам, как программисту, полагается находиться в обороне. Пользователь будет бомбардировать Windows различными внешними действиями, а Windows будет бомбардировать вашу программу сообщениями, соответствующими этим действиям. Все, что Вы должны делать — это отвечать на сообщения. Рисунок ниже схематично показывает как все это работает.
Каждое сообщение адресовано определенному окну. Когда Вы запрашиваете сообщение у Windows, система выяснит класс вашего окна, найдет связанную с ним оконную процедуру, и вызовет ее. Любое сообщение, посланное нашему окну может обрабатываться в нашей оконной процедуре. Нам остается только отреагировать на его. И что? Мы должны отвечать соответстветствующим образом на всевозможные сообщения Windows? Там их сотни! К счастью, нет! Мы должны перехватывать только те сообщения, в которых заинтересованы. Все остальные мы возвращаем обратно в Windows для обработки по умолчанию, используя DefWindowProc.
Windows получает различные события от клавиатуры, мыши, портов, и т.д. Каждое событие быстро преобразуется в сообщение. Windows посылает сообщения, соответствующим окнам. Например, все сообщения от клавиатуры идут к окну, которое в настоящее время имеет фокус ввода (активное окно). Сообщения мыши посылаются согласно позиции курсора мыши. Они обычно идут к окну, которое расположено непосредственно под курсором (если какая-нибудь программа не захватила мышь).