В–третьих, иногда нам надо получить данные от предыдущей копии приложения (например, если наши приложения организуют обмен данными между собой). С помощью hPrevInstance мы можем сделать это (только в Windows API, в Win32 API это не получится)[8].
В–четвертых, 32х битовые приложения Win32 API всегда предполагают, что запущена только одна копия приложения (так как в виртуальном адресном пространстве приложения кроме нее других приложений, в том числе копий, не находится). При этом hInstance указывает начальный адрес загрузки модуля (и для большинства приложений он совпадает), а hPrevInstance всегда равен NULL.
LPSTR lpszCmdLine — как и обычная C–программа, приложение Windows может получать командную строку. Параметр lpszCmdLine является указателем на эту строку.
int nCmdShow — этот параметр указывает, в каком виде должно быть изображено окно приложения. Для описания значений этой переменной существует целый набор #define’ов, начинающихся с префикса SW_. Например, значение nCmdShow равное SW_SHOWMINNOACTIVE указывает на то, что окно должно быть отображено в минимизированном состоянии, а значение SW_SHOWNORMAL указывает на необходимость отображения окна в нормальном состоянии. Пользователь может указать, в каком виде показывать главное окно приложения, настраивая характеристики ярлыка (shortcut).
После аргументов функции WinMain мы начнем рассматривать непосредственно тело этой процедуры. В самом начале мы должны зарегистрировать класс нашего окна, убедившись в том, что это первая копия приложения. Для этого мы должны заполнить структуру WNDCLASS и передать ее функции RegisterClass:
WNDCLASS WC;
if ( !hPrevInstance ) {
WC.style= NULL;
WC.lpfnWndProc= WinProc;
WC.cbClsExtra= NULL;
WC.cbWndExtra= NULL;
WC.hInstance= hInstance;
WC.hIcon= LoadIcon( NULL, IDI_APPLICATION );
WC.hCursor= LoadCursor( NULL, IDC_ARROW );
WC.hbrBackground= GetStockObject( WHITE_BRUSH );
WC.lpszMenuName= NULL;
WC.lpszClassName= "Hello application";
if ( !RegisterClass( &WC ) ) return NULL;
}
Эта операция обычно выполняется в отдельной процедуре (init_instance в 1a.cpp) так как структура WNDCLASS используется однократно, только для вызова функции RegisterClass, после чего ее можно не держать в стеке.
Структура WNDCLASS содержит следующие поля:
Поле style содержит комбинацию CS_xxx констант, задающих некоторые характеристики класса. Часто этот параметр равен NULL. Для более подробного знакомства с этим полем рекомендуется посмотреть описание функции RegisterClass и возможные стили класса.
Например, стиль CS_HREDRAW говорит о том, что окно должно перерисовываться целиком при изменении его горизонтального размера, CS_VREDRAW — при изменении вертикального размера, CS_DBLCLK — окно будет реагировать на двойные нажатия на клавишу мыши. Особенно надо отметить стиль CS_GLOBALCLASS. Обычно зарегистрированный класс используется только лишь данным приложением (и всеми его копиями), но недоступен для других приложений. Стиль CS_GLOBALCLASS разрешает использование класса другими приложениями.
Если Вы захотите объединить несколько стилей класса, используйте оператор ПОБИТОВОЕ ИЛИ, например: CS_HREDRAW|CS_VREDRAW|CS_DBLCLK.
lpfnWndProc является указателем на оконную функцию. Вы не можете указать его 0, эта функция должна быть написана Вами.
cbClsExtra, cbWndExtra. При регистрации класса окна Windows создает специальный блок, содержащий информацию о классе; В некоторых случаях бывает удобно включить в эту структуру свои данные (их смогут использовать все окна, принадлежащие этому классу — даже окна разных приложений).
Для этого Windows может зарезервировать специальное добавочное пространство в этом блоке, размер этого пространства указывается параметром cbClsExtra. Это пространство может быть использовано вами по своему усмотрению, поэтому вы должны задать его размер. Если Вы не собираетесь использовать это пространство, укажите его размер 0(NULL).
Позже, при создании окна, Windows создаст другой блок, описывающий окно, и в нем выделит дополнительное пространство размером cbWndExtra байт. Оно также предназначено для использования Вами. Если оно Вам не требуется, укажите размер 0 (NULL). В таких полях удобно хранить данные, уникальные для каждого окна — скажем, хендл редактируемого файла, если это окно редактора.
Рисунок 2. Структуры данных, используемые Windows для описания окон.
Вы можете использовать при желании двенадцать функций для чтения/записи данных, находящихся в структурах описания окна и класса:
UINTGetWindowWord( hWnd, nIndex );
UINT SetWindowWord( hWnd, nIndex, wNewValue );
LONG GetWindowLong( hWnd, nIndex );
LONG SetWindowLong( hWnd, nIndex, dwNewValue );
int GetWindowText( hWnd, lpWinName, nMaxCount);
int GetWindowTextLength( hWnd );
void SetWindowText( hWnd, lpszWinName );
“Текст окна” (...WindowText...) является обычно заголовком окна, а для окон специального вида (например, кнопки) — текстом, написанном в этом окне.
UINT GetClassWord( hWnd, nIndex );
UINT SetClassWord( hWnd, nIndex, wNewValue );
LONG GetClassLong( hWnd, nIndex );
LONG SetClasslong( hWnd, nIndex, dwNewValue );
int GetClassName( hWnd, lpClassName, nMaxCount );
Поле hInstance, конечно, содержит хендл копии приложения, регистрирующего данный класс окон. Он будет использоваться Windows для связи оконной функции с копией приложения, содержащего оконную функцию.
Следующие три поля (hIcon, hCursor, hbrBackground) пояснить будет посложнее — для этого надо будет разобраться с GDI (hbrBackground) и ресурсами (hIcon, hCursor), что будет сделано в соответствующих разделах. Поэтому пока что мы сделаем несколько замечаний и рассмотрим простейший случай.
hIcon — это хендл пиктограммы (иконки), которая будет выводиться вместо окна в минимизированном состоянии. Функция LoadIcon находит соответствующую пиктограмму и возвращает ее хендл. В нашем примере LoadIcon использует стандартную пиктограмму (первый параметр равен NULL), которая используется по умолчанию для представления приложения (второй параметр равен IDI_APPLICATION).
hCursor является, аналогично hIcon, хендлом курсора. Это курсор мыши, который будет отображаться при перемещении мыши через окно. Требуемый курсор загружается функцией LoadCursor, применение которой похоже на функцию LoadIcon. Для стандартных курсоров существуют следующие имена: IDC_ARROW, IDC_CROSS, IDC_IBEAM, IDC_ICON, IDC_SIZE, IDC_SIZENESW, IDC_SIZES, IDC_SIZENWSE, IDC_SIZEWE, IDC_UPARROW и IDC_WAIT. Наиболее часто употребляемый курсор IDC_ARROW.
Третий хендл — хендл кисти — hbrBackground. Применение кисти существенно отличается от пиктограммы и курсора, поэтому она задается иным способом. Функция GetStockObject возвращает хендл заранее созданного Windows стандартного объекта. Мы используем GetStockObject для получения хендла “белой кисти” WHITE_BRUSH. Помимо хендлов кистей эта функция может возвращать хендлы других объектов. В примере 1a.cpp функция GetStockObject не применялась — вместо хендла кисти разрешено указывать специальную константу, обозначающую системный цвет (в примере — COLOR_WINDOW). Для того, что бы система могла отличить хендл от цвета, требуется, что бы к нему была прибавлена 1 (0 означает неверный хендл, являясь в то же время корректным номером цвета).
Следует отметить, что кисть WHITE_BRUSH вовсе не обязательно имеет белый цвет. Просто эта кисть обычно используется для закраски фона окна, часто это действительно белый цвет, но он может быть изменен при настройке цветов Windows.
Указатель на строку lpszMenuName. Если Ваше окно имеет связанное с ним меню, то Вы можете указать имя этого меню для его автоматического использования. Как это делается мы рассмотрим несколько позже. В данном примере предполагается, что меню нет — lpszMenuName равен NULL.
Последний параметр lpszClassName является именем регистрируемого класса окна. Рекомендуется включать в это имя название задачи, так как это уменьшает вероятность совпадения имени данного класса с уже зарегистрированным классом другого приложения.
ВНИМАНИЕ! Если Вы применяете русские символы в именах меню, класса и др., то Вам надо быть очень осторожными — стандартные шрифты нелокализованных для России версий Windows 3.x не содержат русских символов, и, помимо этого, компиляторы могут не воспринимать русский текст, особенно некоторые его буквы (как, например, букву “я”, код которой равен 255). Дополнительно могут встретиться осложнения из–за того, что среда программирования в Windows может использовать собственные нерусифицированные шрифты, или из–за того, что DOS–программы используют иную кодировку русских символов, чем Windows–приложения (если Вы используете какие–либо DOS–средства для разработки программ, либо Ваше приложение будет использовать DOS–программы).
Если функция RegisterClass вернула не 0, то класс успешно зарегистрирован. Этот зарегистрированный класс будет существовать до тех пор, пока приложение активно, или пока Вы не вызовете функцию
UnregisterClass( lpszClassName, hInstance )
для уничтожения данного класса. Если вы собираетесь вызвать функцию UnregisterClass, то надо убедиться что нет ни одного окна, принадлежащего данному классу. Эта функция обычно применяется для глобальных классов (со стилем CS_GLOBALCLASS).
После регистрации класса мы можем создавать окна. Делается это с помощью процедуры CreateWindow. Эта функция создает окно и возвращает его хендл. Если создание окна почему–либо невозможно, то возвращается значение NULL. Рассмотрим параметры этой функции:
HWND CreateWindow(
lpszClassName, lpszWindowName, dwStyle,
nX, nY, nWidth, nHeight,
hWndParent, hMenu, hInstance, lpParam
);
lpszClassName задает имя класса окна. По этому имени Windows находит описание класса окон и, соответственно, оконную функцию, которая будет вызываться для обработки сообщений, посылаемых окну. Такой механизм связывания окна с функцией (через имя класса) позволяет определять оконную функцию в одном приложении, а использовать в другом.
lpszWindowName задает заголовок окна. Этот заголовок размещается в верхней части окна. В некоторых случаях этот текст используется иначе — например, если окно — "кнопка", то это текст на кнопке, или это может быть текст под пиктограммой, если окно минимизировано.
Параметр dwStyle содержит битовые флаги, задающие некоторые характеристики окна, как–то тип рамки, наличие заголовка, кнопок системного меню, минимизации, максимизации и т.д. Признаки окна называются стилями и описаны в windows.h с префиксом WS_. Вы должны объединять отдельные стили с помощью операции ПОБИТОВОЕ ИЛИ: WS_CAPTION|WS_SYSMENU.