Московский государственный университет им. М.В.Ломоносова Физический факультет ПРОГРАММИРОВАНИЕ В СРЕДЕ WINDOWS: WIN32 API К.Э. Плохотников Москва, 2006. —1— Семинар №1. Введение в среду программирования Win32 API Операционные системы семейства Windows являются доминирующими на компьютерном рынке информационных технологий. Знание основ разработки приложений на базе Windows является важным элементом современной культуры программирования. Среда операционной системы Windows предоставляет полный интерфейс для разработки различного рода приложений. Этот интерфейс кратко характеризуется, как Win32 API, где аббревиатура API означает Application Program Interface. Программный интерфейс Windows реализуется в виде библиотек динамической компоновки, размещенных в системном подкаталоге system32 операционной системы Windows. Существует две разновидности API: Win16 и Win32. Win32 поддерживает 32-разрядную прямую адресацию, тогда как Win16 — 16разрядную сегментарную модель памяти1. С точки зрения графического интерфейса GUI (Graphical User Interface) все Windows-приложения можно подразделить на три группы2: 1) консольные приложения: Win32 Consol Application; 2) полноценный графический интерфейс пользователя или так называемый Win32 GUI Applications; 3) диалоговые пользовательские окна на базе Win32 Application. Как наиболее простые, консольные приложения, рассматривались на первом курсе. Темой наших занятий является изучение полноценного графического оконного интерфейса (посредством клавиатуры, мыши и пр.) с Windows-приложением на базе использования различного рода кнопок, редактируемых полей, списков и пр. в среде Microsoft Visual C++6.0. На завершающей стадии курса будет рассмотрено программирование более простых — диалоговых окон. Программирование диалоговых окон — компромисс между простотой консольных приложений и сложностью разработки низкоуровенного оконного интерфейса. Литература по изучению среды программирования Win32 API довольно внушительна. Это, например, капитальный труд Ричарда Саймона3, содержащий компакт-диск с примерами программ. Либо небольшое, но хорошо написанное, справочное пособие Р.Д. Верма по функциям Win32 API4. Большинство учебных примеров заимствовано из справочника Р.Д. Верма. Некоторые из них пришлось модифицировать с тем, чтобы они полноценно работали в среде Microsoft Visual C++6.0. Наконец, наиболее полные сведения о среде Win32 API представлены в библиотеке MSDN (Microsoft Developer Network Library)5. В качестве завершения курса будет представлен проект моделирования 1 2 Шилдт Г. Полный справочник по С. — М.: Издательский дом “Вильямс”, 2002. 704с. Мартынов Н.Н. Программирование для Windows на C/C++. Том 1. — М.: ООО “Бином-Пресс”, 2004. 528с. 3 Саймон Р. Microsoft Windows API. Справочник системного программиста. — К.: ООО “ТИД “ДС”, 2004. 1216с. 4 Верма Р.Д. Справочник по функциям Win32 API. —М.: Горячая линия-Телеком, 2005. 551с. 5 http://msdn.microsoft.com/subscriptions/ —2— и визуализации движения цилиндра по наклонной плоскости. Данный проект будет представлен в деталях, включая физическую постановку задачи и все этапы программирования в среде Win32 API. На рис.1 приведено основное диалоговое окно прототипа курсовой работы “Движение цилиндра по наклонной плоскости”. Следует отметить, что наша конечная цель состоит в том, чтобы студенты самостоятельно научились делать курсовые работы, используя адекватные программные средства, включая Win32 API. Рис.1. Прототип курсовой работы “Движение цилиндра по наклонной плоскости” Входим в среду Microsoft Visual C++6.0. Далее нажимаем кнопки: File → New → Project: Win32 Application → Project name: Expl1 → в меню “Win32 Application — Step 1 of 1” выбираем An empty project → Finish → OK. В окне Workspace переходим на вкладку FileView и метим папку Source Files, нажимаем правую кнопку мыши и выбираем пункт контекстного меню Add Files to Folder… или добавить файл к проекту, где находим нужный файл Expl1.cpp в папке Примеры. Содержимое файла Expl1.cpp имеет следующий вид. Листинг №1 //Пример №1. Мое первое окно //////////////////////////////////////////////////////////////////////////////////////////////////////////// #include HINSTANCE hin;//идентификатор приложения //оконная процедура LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM); //создание и регистрация нового оконного класса ATOM winclass(); HWND makeexamplewin();//новое окно //обработка очереди сообщений приложения —3— void messageprocess(); WINAPI WinMain //точка входа в Windows программу (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //сохраняем идентификатор приложения hin=hInstance; //регистрируем новый оконный класс winclass(); //создаем окно HWND winhandler=makeexamplewin(); //отображаем окно ShowWindow(winhandler,SW_SHOW); //обрабатываем очередь сообщений messageprocess(); return 0; } //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе озавершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } //создание и регистрация нового оконного класса ATOM winclass() { //далее заполняем поля структуры WNDCLASSEX WNDCLASSEX winexam1; //размер структуры winexam1.cbSize=sizeof(WNDCLASSEX); //свойства окон winexam1.style=CS_OWNDC|CS_HREDRAW|CS_VREDRAW; //адрес оконной процедуры winexam1.lpfnWndProc=WinProc; //дополнительная память для оконного класса winexam1.cbClsExtra=0; //дополнительная память для каждого окна winexam1.cbWndExtra=0; //идентификатор процесса, создоющего окно winexam1.hInstance=hin; //определяем иконку окна winexam1.hIcon=LoadIcon(hin,IDI_APPLICATION); —4— //определяем курсор для окна winexam1.hCursor=LoadCursor(hin,IDC_APPSTARTING); //определяем кисть для окна winexam1.hbrBackground=HBRUSH(COLOR_WINDOW+1); //имя ресурса меню winexam1.lpszMenuName=NULL; //имя оконного класса winexam1.lpszClassName="examplewinclass"; //определяем маленькую иконку winexam1.hIconSm=LoadCursor(hin,IDC_APPSTARTING); //регистрация оконного класса return RegisterClassEx(&winexam1); } //создаем экземпляр окна нового оконного класса с //атрибутами по умолчанию HWND makeexamplewin() { return CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,"examplewinclass", "Мое первое окно",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL, hin,NULL); } void messageprocess() { MSG msg; //получаем в цикле следующее сообщение из очереди сообщений while(GetMessage(&msg,(HWND) NULL,0,0)) { //передаем сообщение окну-адресату DispatchMessage(&msg); } } После компиляции проекта Expl1 (Ctrl+F7) и его запуска (Ctrl+F5) на исполнение получится окно, представленное на рис.2. Из рис.2 следует, что проект Expl1 строит на мониторе компьютера окно со всеми типичными атрибутами. Листинг №1 является базовым для всех Windows приложений. В большинстве дальнейших примеров код листинга №1 берется за основу. На рис.3 приведена структурная схема работы программы. Основой интерфейса в среде Windows является окно. Взаимодействие между окнами, а также между пользователем и окнами осуществляется с помощью сообщений. Обработкой сообщений занимается специальная функция — оконная процедура. Для каждого окна создается своя оконная процедура. В примере №1 в качестве оконной процедуры выступает функция LRESULT CALLBACK WinProc. Анализ этой процедуры показывает, что она отдельно реагирует на сообщение об уничтожении окна, а все остальные сообщения обрабатываются по умолчанию с помощью процедуры DefWindowProc. —5— Адрес оконной процедуры передается окну при регистрации класса. Рис.2. Итог работы программы примера №1 Большинство функций Win32 API объявлены в файле windows.h, который всегда будет присоединяться к нашим примерам. При создании окна ему дается уникальный идентификатор — дескриптор окна (тип HWND). В примере №1 таким дескриптором является winhandler. Система Windows API предоставляет множество стандартных классов окон, реализующих кнопки, поля редактирования, списки и другие элементы управления приложением. В следующем примере №2 (проект Expl2) создадим три окна, третье из которых сделаем дочерним по отношению ко второму с помощью функции SetParent. Дочернее же окно должно находиться в пределах родительского. На листинге №2 приведен фрагмент кода, отличный от кода предыдущего примера. Полный текст кода приведен в файле Expl2.cpp, который находится в папке Примеры. —6— Точка входа в Windows программу Создание и регистрация нового оконного класса Создание окна WINAPI WinMain winclass() makeexamplewin() ShowWindow(…,SW_SHOW) messageprocess() Отображение окна Обработка очереди сообщений Рис.3. Блок-схема работы программы листинга №1 WINAPI WinMain //точка входа в Windows программу (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //сохраняем идентификатор приложения hin=hInstance; //регистрируем новый оконный класс, три окна //будут принадлежать одному классу winclass(); //создаем первое окно HWND winhandler1=makeexamplewin(); //создаем второе окно HWND winhandler2=makeexamplewin(); //создаем третье окно HWND winhandler3=makeexamplewin(); //отображаем первое окно ShowWindow(winhandler1,SW_SHOW); //отображаем второе окно ShowWindow(winhandler2,SW_SHOW); //отображаем третье окно ShowWindow(winhandler3,SW_SHOW); //делаем третье окно дочерним второму SetParent(winhandler3,winhandler2); //обрабатываем очередь сообщений messageprocess(); return 0; } Листинг №2 На рис.4 приведен итог работы проекта Expl2. Согласно листингу №2, процедура makeexamplewin была применена трижды, что привело к порождению трех экземпляров окна одного и того же типа. Приведем для иллюстрации несколько функций, непосредственно ассоциированных с окнами: • • CreateWindow — создает окно с заданными параметрами; DefWindowProc — вызывает заданную по умолчанию процедуру окна; —7— В общем случае область окна может быть разделена на две части: клиентская область — область ввода (вывода) и неклиентская область — область, где размещается заголовок окна, быть может, меню, а также рамка окна. • • • • DestroyWindow — уничтожает окно; GetWindow — возвращает дескриптор окна; GetWindowLong — возвращает значение указанного атрибута окна; SetWindowLong — устанавливает значение указанного атрибута. Рис.4. Создание трех окон, второе из которых дочернее третьему Каждое приложение, разработанное на базе Win32 API, может поддерживать один или более процессов. Внутри отдельного процесса могут иметь место несколько потоков. Потоки позволяют осуществлять параллельное выполнение процессов. Поток и поддерживающее его окно могут находиться в двух состояниях: • приоритетное окно (foreground window) — окно, с которым в данный момент работает пользователь; • заднее окно (background window) — неприоритетное окно. Система Windows поддерживает специальный оконный стек, который определяет распределение окон по мнимой оси Z, направленной от экрана к пользователю. Пользователь может менять координату Z окон, а также делать так, чтобы одно из окон было всегда поверх других. Возможны следующие типы окон: • перекрывающиеся окна (overlapped window) — как правило, это главные окна приложения; • всплывающие окна (pop-up window) — окна временного —8— пользования для отдельных сообщений, диалогов и пр.; • дочерние окна (child window) — окна находящиеся в клиентской области родительского окна; • слоистые окна (layered window) — специальный тип окон с улучшенным внешним видом; • окна только для сообщений (message-only window) — они всегда невидимы, не имеют своего порядка и могут либо получать, либо посылать сообщения. Окна могут находиться в следующих состояниях отображения: • скрытое окно невидимо на экране; • свернутое окно помещается на полосу задач (taskbar), если это окно верхнего уровня; • развернутое окно занимает весь экран или родительское окно; • активное окно, в котором в данный момент работает пользователь; активным может быть только одно окно в данный момент времени; • заблокированное окно — окно, в котором не действует мышь и клавиатура. В примере №3, фрагмент кода которого представлен на листинге №3, строится три окна: родительское, дочернее и всплывающее, причем оконные процедуры у родительского и у дочернего и всплывающего окон разные. Листинг №3 //оконная процедура для дочернего и всплывающего окон LRESULT CALLBACK CWinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) {return DefWindowProc(hwnd,msg,wParam,lParam);} LRESULT CALLBACK WinProc //оконная процедупа (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //msg - идентификатор обрабатываемого сообщения switch(msg) { //обрабатываем сообщение создания окна case WM_CREATE: //создаем всплывающее окно CreateWindow("child","popup window", WS_POPUP|WS_CAPTION|WS_VISIBLE,0,0,200,200, hwnd,NULL,hin,NULL); //создаем дочернее окно CreateWindow("child","child window", WS_CHILD|WS_CAPTION|WS_VISIBLE,0,0,200,200, hwnd,NULL,hin,NULL); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); —9— } return 0; } //создание и регистрация нового оконного класса ATOM winclass(HINSTANCE k) { //далее заполняем поля структуры WNDCLASSEX WNDCLASSEX winexam1; //размер структуры winexam1.cbSize=sizeof(WNDCLASSEX); //свойства окон winexam1.style=CS_OWNDC; //адрес оконной процедуры winexam1.lpfnWndProc=WinProc; //дополнительная память для оконного класса winexam1.cbClsExtra=0; //дополнительная память для каждого окна winexam1.cbWndExtra=0; //идентификатор процесса, создающего окно winexam1.hInstance=k; //определяем иконку окна winexam1.hIcon=LoadIcon(k,IDI_APPLICATION); //определяем курсор для окна winexam1.hCursor=LoadCursor(k,IDC_APPSTARTING); //определяем кисть для окна winexam1.hbrBackground=HBRUSH(COLOR_WINDOW+1); //имя ресурса меню winexam1.lpszMenuName=NULL; //имя оконного класса winexam1.lpszClassName="examplewinclass"; //определяем маленькую иконку winexam1.hIconSm=LoadCursor(k,IDC_APPSTARTING); //регистрация оконного класса главного окна RegisterClassEx(&winexam1); //создание класса для дочернего и всплывающего окон winexam1.lpszClassName="child"; winexam1.lpfnWndProc=CWinProc; //регистрация оконного класса return RegisterClassEx(&winexam1); } Полный код примера №3 находится в файле Expl3.cpp. Создайте проект Win32 API с этим файлом в среде Microsoft Visual C++6.0 и запустите его на исполнение. В результате должно получиться картина, представленная на рис.5. — 10 — Рис.5. В примере №3 появляется родительское окно, а также дочернее и всплывающее окна Дополнительный набор функций для работы с окнами: • • • • • • • • • • • • ArrangeIconicWindows — упорядочивает свернутые окна; BringWindowToTop — раскрывает перекрытое окно; CascadeWindows — упорядочивает окна каскадом; CloseWindow — сворачивает, но не уничтожает окно; GetClientRect — возвращает размеры клиентской области окна; GetWindowRect — возвращает экранные координаты окна; GetWindowText — копирует заголовок окна в буфер; IsWindowVisible — определяет состояние видимости окна; MoveWindow — меняет позицию и размеры окна; SetWindowText — устанавливает заголовок окна; ShowWindow — изменяет состояние видимости окна; TileWindow — упорядочивает окна черепицей. Более подробно об аргументах данных функций можно ознакомиться либо по тем подсказкам, которые всплывают в среде Microsoft Visual C++6.0 после вставки открывающей скобки, либо в перечисленных выше источниках, либо в библиотеке MSDN. — 11 — Семинар №2. Контекст устройства. Вывод текста и линий Контекст устройства представляет собой структуру объемом 64Кб, в которой содержится все необходимое для вывода на устройство. Устройством может быть дисплей, окно, а также память, принтер или информационный контекст устройства. Способами использования контекста устройства для окна являются следующие: • выделяем окну собственный контекст устройства; • берем контекст устройства для оконного класса; • используем один из пяти общих контекстов устройства; • используем контекст устройства родительского окна. По умолчанию используется один из общих контекстов устройства. Для иллюстрации контекста устройства в примере №4 в окне отображается текст, который виден до тех пор, пока не измениться размер окна. На листинге №4 приведен фрагмент кода соответствующей программы. Полный код представлен в файле Expl4.cpp. Листинг №4 WINAPI WinMain //точка входа в Windows программу (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //сохраняем идентификатор приложения hin=hInstance; //регистрируем новый оконный класс winclass(); //создаем окно HWND winhandler=makeexamplewin(); //отображаем окно ShowWindow(winhandler,SW_SHOW); //вывод текста после возврата функцией GetDC() //контекста устройства. Надпись не обновляема TextOut(GetDC(winhandler),120,120, "Смотри! Ты видишь текст!",24); //обрабатываем очередь сообщений messageprocess(); return 0; } Итог работы соответствующего проекта представлен на рис.1. Дополнительный список функции таков: • • • • • • • • CreateCompatibleDC — создает контекст устройства, совместимый с указанным устройством; CreateDC — создает контекст устройства; DeleteDC — удаляет контекст устройства; GetDC — возвращает дескриптор контекста устройства окна; GetWindowDC — возвращает дескриптор контекста устройства окна; ResetDC — обновляет контекст устройства; ReleaseDC — освобождает контекст устройства; SaveDC — сохраняет текущее состояние контекста устройства. —1— Рис.1. Использование контекста устройства в примере №4 для вывода текста в окно В предыдущем примере текст в окне исчезал при попытке изменения размеров окна. Чтобы это не происходило, оконная процедура должна обрабатывать сообщение WM_PAINT. Начинать процедуру рекомендуется с вызова функции BeginPaint, запрещающей посылку сообщения WM_PAINT до вызова функции EndPaint. В следующем примере №5 выводится текст и линия во время обновления окна, при этом они не исчезают при изменении размера окна. Фрагмент соответствующего кода приведен на листинге №5. Полный код находится в файле Expl5.cpp. Листинг №5 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //создание контекста устройства HDC dc; PAINTSTRUCT p; //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение обновления окна case WM_PAINT: dc=BeginPaint(hwnd,&p); //выводим текст TextOut(dc,120,120,"Обновляемый текст",17); //рисуем линию от текущей позиции пера LineTo(dc,440,440); EndPaint(hwnd,&p); break; case WM_DESTROY: //сообщение уничтожения окна —2— //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } На рис.2 приведен итог работы соответствующего проекта Visual C++ с файлом Expl5.cpp. Рис.2. Итог работы примера №5 — обновляемое окно с текстом и линией Список характерных функций: • • • • • • • BeginPaint — функция начала обновления окна; EndPaint — функция конца обновления окна; GetUpdateRect — возвращает прямоугольник для обновления; GetUpdateRgn — возвращает область обновления; LockWindowUpdate — запрещает перерисовку окна; RedrawWindow — перерисовывает указанную область окна; UpdateWindow — обновляет клиентскую область, посылая сообщение WM_PAINT. В следующем примере №6 приведен еще один способ вывода текста в клиентское окно. Текст будет выровнен по центру. Соответствующий фрагмент кода приведен на листинге №6. Полный текст кода приведен в файле Expl6.cpp. //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) Листинг №6 —3— { //создание контекста устройства HDC dc; PAINTSTRUCT p; //для хранения текущих размеров клиентской области RECT rect; //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&p); //сохраняем текущий размер клиентской области GetClientRect(hwnd,&rect); //рисуем текст DrawText(dc,"Текст, который всегда сверху в центре", 37,&rect,DT_CENTER); EndPaint(hwnd,&p); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Создаем соответствующий проект Expl6 на базе файла Expl6.cpp. Итогом работы данного проекта должно быть окно, вид которого приведен на рис.3. Список сопутствующих функций: • • • • • • DrawText — выводит текст; GetTextAlign — определяет выравнивание текста; GetTextCharacterExtra — определяет межсимвольный интервал; GetTextFace — определяет имя текущего шрифта; SetTextAlign — устанавливает флажки выравнивания текста; TextOut — выводит символьную строку. Для графического вывода в среде Win32 API существует множество функций. Следующий пример №7 иллюстрирует некоторые из этих функций. Соответствующий листинг №7 с фрагментом кода представлен ниже. Полный код программы представлен в файле Expl7.cpp. —4— Рис.3. Итог работы примера №6 — текст в клиентской части окна выровненный по центру //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //создание контекста устройства HDC dc; PAINTSTRUCT p; //для хранения текущих размеров клиентской области RECT rect; //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&p); //выводим прямоугольник с закругленными углами RoundRect(dc,200,200,60,60,105,105); //выводим эллипс Ellipse(dc,70,0,130,60); //меняем цвет фона текста SetBkColor(dc,0x34ffdd); TextOut(dc,0,70,"TEXT",4); EndPaint(hwnd,&p); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; Листинг №7 —5— } После формирования соответствующего проекта, компиляции и запуска на исполнение будет получено то, что представлено на рис.4. Рис.4. Итог работы примера №7 — образцы графического вывода в среде Win32 API Список функций вывода эллипсов и многоугольников: • • • • • • • • • • • • • • • • • • • • • Chord — рисует хорду; DrawFocusRect — рисует прямоугольник; Ellips — рисует эллипс; Pie — рисует сектор фигуры, похожей на кусок пирога; Polygon — рисует многоугольник; PolyPolygon — рисует ряд многоугольников; Rectangle — рисует прямоугольник; RoundRect — рисует прямоугольник со скругленными углами. AngleArc — рисует линейный сегмент и дугу; Arc, ArcTo — рисуют дугу; GetArcDirection — возвращает текущее направление дуги; LineTo — рисует линию от текущей позиции; MoveToEx — изменяет текущую позицию; PolyBezier, PolyBezierTo — рисуют одну и более кривых Безье; Polyline, PolylineTo — рисуют несколько линий; SetArcDirection — устанавливает текущее направление дуги. GetBkColor — возвращает текущий цвет фона; GetStretchBitMode — определяет режим масштабирования; GetTextColor — определяет текущий цвет текста; SetBkColor — устанавливает цвет фона; SetTextColor — устанавливает цвет текста. Список функций вывода линий и кривых: Список функций изменения атрибутов вывода: GDI (Graphical User Interface) — интерфейс графических устройств —6— представляет собой набор функций (всего более 120, некоторые из которых приведены выше) и объектов обеспечивающих взаимодействие пользователя с приложением. Аналогично окнам, управление графическими объектами осуществляется с помощью дескрипторов. Отметим наличие такого же способа управления графическими объектами в Matlab. Для использования объекта в устройстве его нужно поместить в контекст устройства. Цвет объекта задается 32-битовым числом (тип COLORREF). Например, 0x00 bb gg rr, где b — синяя, g — зеленая, r — красная компоненты цвета. Вместо такого представления можно использовать более традиционный способ записи цвета с помощью макроса RGB(R,G,B), а для получения отдельных компонент цвета — макросы GetRValue, GetGValue, GetBValue. Список функций: • • • • • GetObject — возвращает информацию об объекте; GetObjectType — определяет по дескриптору тип объекта GDI; GetStockObject — получает дескриптор предопределенного объекта GDI; SelectObject — помещает объект в контекст устройства; DeleteObject — уничтожает объект. —7— Семинар №3. Растровый рисунок, кисть, шрифт, перо, иконка, курсор В среде Windows изображение может храниться двумя способами: • устройствозависимым (DDB — Device Dependent Bitmap), когда для отображения объекта используется информация об устройстве, в котором объект изображается; • устройствонезависимым (DIB — Device Independent Bitmap), когда изображение всю необходимую информацию о себе хранит в себе. Обычно такие независимые графические объекты представляют собой отдельные файлы *.bmp. В примере №8 оконная процедура сохраняет текущее содержимое экрана в клиентской области окна. Соответствующий фрагмент кода приведен на листинге №8, а полный текст кода содержится в файле Expl8.cpp. Листинг №8 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //создание контекста устройства HDC dc; PAINTSTRUCT ps; //для хранения текущих размеров клиентской области RECT r; static HBITMAP bmdesktop; static HDC dcdesktop; static int Ox, Oy; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: //получаем контекст устройства экрана dc=GetDC(NULL); //создаем копию контекста устройства dcdesktop=CreateCompatibleDC(dc); //сохраняем разрешение экрана по оси X Ox=GetDeviceCaps(dc,HORZRES); //сохраняем разрешение экрана по оси Y Oy=GetDeviceCaps(dc,VERTRES); //создаем рисунок, совместимый (по размерам) с экраном bmdesktop=CreateCompatibleBitmap(dc,Ox,Oy); //помещаем рисунок в созданный контекст устройства SelectObject(dcdesktop,bmdesktop); //копируем содержимое экрана в рисунок BitBlt(dcdesktop,0,0,Ox,Oy,dc,0,0,SRCCOPY); //освобождаем контекст устройства нашего экрана ReleaseDC(NULL,dc); //получаем контекст устройства нашего окна dc=GetDC(hwnd); //устанавливаем режим масштабирования контекста //устройства нашего окна SetStretchBltMode(dc,HALFTONE); —1— break; //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&ps); //получаем текущий размер клиентской области GetClientRect(hwnd,&r); //копируем рисунок в окно, масштабируя его под //размеры окна StretchBlt(dc,0,0,r.right,r.bottom, dcdesktop,0,0,Ox,Oy,SRCCOPY); EndPaint(hwnd,&ps); break; case WM_DESTROY: //сообщение уничтожения окна //удаляем созданный контекст устройства DeleteDC(dcdesktop); //удаляем созданный рисунок DeleteObject(bmdesktop); //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Рис.1. Загрузка текущего содержимого экрана в клиентскую область окна После создания проекта Microsoft Visual C++ с файлом Expl8.cpp, его компиляции и запуска, получится окно с изображением в нем текущего содержимого экрана. На рис.1 приведено изображение такого окна. Список некоторых функций: • • CreateBitmap — создает рисунок; CreateCompatibleBitmap — создает рисунок совместимый с указанным устройством; —2— Графический инструмент — кисть используется для закрашивания различного рода областей или заднего фона. Различают три типа кистей: сплошная, штриховая и шаблонная. В следующем примере №9 показана работа первых двух типов кистей. Соответствующий фрагмент кода приведен на листинге №9. Полный текст кода программы находится в файле Expl9.cpp. Листинг №9 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //создание контекста устройства HDC dc; PAINTSTRUCT ps; //кисть синего цвета static HBRUSH br1=CreateSolidBrush(0xff0000); //кисть зеленого цвета static HBRUSH br2=CreateHatchBrush(HS_DIAGCROSS,0x00ff00); //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&ps); //выбираем первую кисть SelectObject(dc,br1); //закрашиваем прямоугольник первой кистью PatBlt(dc,0,0,150,150,PATCOPY); //выбираем вторую кисть SelectObject(dc,br2); //закрашиваем прямоугольник второй кистью PatBlt(dc,151,0,150,150,PATCOPY); EndPaint(hwnd,&ps); break; case WM_DESTROY: //сообщение уничтожения окна //получаем контекст устройства окна dc=GetDC(hwnd); //выбираем стандартную кисть SelectObject(dc,GetStockObject(WHITE_BRUSH)); //уничтожаем первую кисть DeleteObject(br1); //уничтожаем вторую кисть DeleteObject(br2); //сообщаем системе о завершении программы PostQuitMessage(0); break; • • • • • • • • CreateDIBitmap — создает DDB рисунок из DIB; CreateDIBSection — создает рисунок DIB; GetBitmapDimensionEx — возвращает размеры рисунка; GetDIBits — копирует рисунок в буфер; GetPixel — возвращает RGB-значения пикселя в заданных координатах; SetBitmapDimensionEx — устанавливает размеры рисунка; SetPixel — устанавливает цвет пикселя; StretchBlt — копирует и масштабирует рисунок. —3— //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Итог работы проекта с файлом Expl9.cpp представлен на рис.2. Рис.2. Пример №9 работы сплошной и штриховой кистей Соответствующий список функций: • • • • • • • • • CreateBrushIndirect — создать кисть с указанным стилем, цветом и шаблоном; CreateHatchBrush — создать штриховую кисть; CreatePatternBrush — создать кисть с растровым шаблоном; CreateSolidBrush — создать сплошную кисть; GetBrushOrgEx — возвращает координаты кисти контекста устройства; GetDCBrushColor — возвращает цвет кисти контекста устройства; PatBit — закрашивает прямоугольник; SetBrushOrgEx — изменяет координаты кисти контекста устройства; SetDCBrushColor — изменяет цвет кисти контекста устройства. Шрифт — это набор символов, выполненных в едином стиле. Такого рода стили иногда называют гарнитурами, например, гарнитура Times, Arial и пр. С позиций Windows шрифт это либо набор точек (растровый тип изображений), либо набор графических команд вывода (шрифты True Type, Open Type, PostScript). Второй тип шрифтом выглядит одинаково на любых устройствах вывода. Помимо шрифтовой гарнитуры символы могут быть специально начертаны: жирным, курсивом или быть подчеркнутыми. Существует и ряд других начертаний, например, все прописные, все строчные, контур и пр. Более подробно об этом можно узнать на примере такого популярного текстового редактора, как Word. В примере №10 в главном окне отображается текст, набранный —4— гарнитурой Arial, созданной с помощью функции CreateFont. Соответствующий фрагмент кода приведен на листинге №10. Полный текст кода содержится в файле Expl10.cpp. Листинг №10 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { static HFONT f; static HFONT oldf; //создание контекста устройства HDC dc; PAINTSTRUCT ps; //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение создания окна case WM_CREATE: //создаем шрифт f=CreateFont(25,0,0,0,700,1,0,0,ANSI_CHARSET, OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,34,"Arial"); //получаем контекст устройства окна dc=GetDC(hwnd); //выбираем в контекст выбранный шрифт oldf=(HFONT)SelectObject(dc,f); break; //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&ps); //выводим текст текущим текстом TextOut(dc,20,20,"CURSIV ARIAL TEXT",17); EndPaint(hwnd,&ps); break; case WM_DESTROY: //сообщение уничтожения окна //получаем контекст устройства окна dc=GetDC(hwnd); //помещаем старый шрифт, чтобы удалить созданный SelectObject(dc,oldf); DeleteObject(f);//уничтожаем созданный шрифт //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Создадим проект на базе файла Expl10.cpp. После компиляции проекта и запуска его на исполнение получим окно, внешний вид которого приведен на рис.3. —5— Рис.3. Пример №10 — работа со шрифтом Список функций: • • • • • • AddFontResource — добавляет ресурс шрифта; CreateFont — создает шрифт; CreateScalableFontResource — создает файл ресурса с информацией о шрифте; GetFontData — определяет метрическую информацию шрифта; GetGlyphOutline — определяет параметры символа; RemoveFontResource — удаляет добавленный ресурс шрифта. Инструмент — перо используется для вывода прямых и кривых линий. Перья делятся на два типа: • косметическое перо используется для вывода линий с фиксированной шириной; • геометрическое перо используется для начертания линий с уникальным стилем окончания и соединения. Список функций: • • • • CreatePen — создает перо; CreatPenIndirect — создает перо из структуры LOGPEN; GetDCPenColor — определяет цвет пера в контексте устройства; SetDCPenColor — изменяет цвет пера в контексте устройства. Иконки это растровые изображения, используемые для представления файлов, папок и т.д. В примере №11 в окне изображаются стандартные иконки. Соответствующий фрагмент кода приведен на листинге №11. Полный текст кода приведен в файле Expl11.cpp. Листинг №11 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { int ix; HICON ic; //создание контекста устройства —6— HDC dc; PAINTSTRUCT ps; //msg - идентификатор обрабатываемого сообщения switch(msg) { //сообщение перерисовки окна case WM_PAINT: dc=BeginPaint(hwnd,&ps); ix=0; //получаем иконку ic=LoadIcon(NULL,IDI_APPLICATION); DrawIcon(dc,ix,0,ic);//рисуем иконку ic=LoadIcon(NULL,IDI_ASTERISK); DrawIcon(dc,ix+=32,0,ic); ic=LoadIcon(NULL,IDI_EXCLAMATION); DrawIcon(dc,ix+=32,0,ic); ic=LoadIcon(NULL,IDI_HAND); DrawIcon(dc,ix+=32,0,ic); ic=LoadIcon(NULL,IDI_QUESTION); DrawIcon(dc,ix+=32,0,ic); ic=LoadIcon(NULL,IDI_WINLOGO); DrawIcon(dc,ix+=32,0,ic); EndPaint(hwnd,&ps); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } После отработки стандартным образом кода программы в файле Expl11.cpp, получим такое окно с иконками, как на рис.4. —7— Рис.4. Изображение иконок, пиктограмм и значков в примере №11 Список некоторых функций: • • • • • • • CopyIcon — копируем иконку; CreateIcon — создаем иконку с заданными параметрами; CreateIconFromResource — создаем иконку или курсор из ресурса; DestroyIcon — уничтожаем иконку; DrawIconEx — рисуем иконку или курсор; GetIconInfo — возвращает информацию об иконке или курсоре; LoadIcon — загружает иконку из исполняемого файла. Курсором называется маленькая картинка, чье местоположение на экране управляется мышью, пером или трекболом. В изображении курсора есть особый пиксель, называемый горячей точкой (hot spot). Он и определяет точное местоположение курсора. Изображения курсоров могут храниться в файлах с расширением *.cur и *.ani, или содержаться как ресурс в исполняемых файлах. Список некоторых функций: • • • • • • • • • • • • ClipCursor — ограничивает движение курсора в прямоугольной области; CreateCursor — создает курсор с указанными параметрами; DestroyCursor — уничтожает курсор; GetClipCursor — возвращает экранные координаты прямоугольной области, ограничивающей движение курсора; GetCursor — возвращает дескриптор текущего курсора; GetCursorInfo — возвращает информацию о курсоре; GetCursorPos — возвращает позицию курсора; LoadCursor — загружает ресурс курсора из исполняемого модуля; LoadCursorFromFile — загружает курсор из указанного файла: SetCursor — устанавливает форму курсора; SetCursorPos — перемещает курсор в указанную позицию; ShowCursor — изменяет изображение курсора. —8— Семинар №4. Область, область отсечения, путь (контур), прямоугольник, движение мыши. Сообщения Областью может быть прямоугольник, многоугольник, эллипс или комбинация этих фигур в любом количестве. В примере №12 строится оконная процедура для обработки окна с двумя “дырами” прямоугольной и эллиптической формы. Фрагмент кода, описывающий соответствующую оконную процедуру, приведен на листинге №12. Полный текст кода содержится в файле Expl12.cpp. Листинг №12 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HRGN rg,rg1; RECT r; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: //вычисляем размер области окна GetWindowRect(hwnd,&r); r.right=r.right-r.left; r.left=0; r.bottom=r.bottom-r.top; r.top=0; //из нее создаем область rg=CreateRectRgnIndirect(&r); //создаем эллиптическую область rg1=(HRGN)CreateEllipticRgn(40,40,300,150); //комбинируем области CombineRgn(rg,rg,rg1,RGN_XOR); //удаляем ненужную область DeleteObject(rg1); //создаем прямоугольную область rg1=(HRGN)CreateRectRgn(200,200,600,400); //комбинируем области CombineRgn(rg,rg,rg1,RGN_XOR); //удаляем ненужную область DeleteObject(rg1); //получившаяся область становится окном SetWindowRgn(hwnd,rg,true); break; case WM_DESTROY: //сообщение уничтожения окна //получаем область окна GetWindowRgn(hwnd,rg); //удаляем эту область из памяти DeleteObject(rg); //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } —1— return 0; } После создания соответствующего Win32 API проекта с файлом Expl12.cpp, получим окно, внешний вид которого представлен на рис.1. Рис.1. Необычное окно примера №12 с двумя “дырами” прямоугольной и эллиптической формы Список некоторых функций: • • • • • • • • • • • • • • • • • CombineRgn — создает область, объединяя две другие области; CreateEllipticRgn — создает эллиптическую область; CreatePolygonGrn — создает многоугольную область; CreatePolyPolygonRgn — создает область из многоугольников; CreateRectRgn — создает прямоугольную область; CreateRoundRectRgn — создает прямоугольную область со скругленными углами; EqualRgn — сравнивает две области на эквивалентность; FillRgn — закрашивает область указанной кистью; FrameRgn — обводит указанную область; GetRegionData — возвращает информацию об области; GetRgnBox — возвращает ограничительный прямоугольник области; InvertRgn — инвертирует цвета в области; OffsetRgn — перемещает область; PaintRgn — закрашивает область текущей кистью; PtlnRegion — определяет, входит ли указанная точка в область; RectlnRegion — определяет, входит ли часть прямоугольника в область; SetRectRgn — изменяет область в прямоугольную. Областью отсечения называется такая область, вне которой вывод запрещен. Область отсечения возможна только в контексте некоторого устройства. В примере №13 части эллипса и прямоугольника с закругленными углами отсекается прямоугольной областью отсечения. На листинге №13 приведен фрагмент кода соответствующей программы. Полностью код программы представлен в файле Expl13.cpp. —2— Листинг №13 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HRGN r; HDC dc; PAINTSTRUCT p; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_PAINT: dc=BeginPaint(hwnd,&p); //создаем прямоугольную область r=CreateRectRgn(0,0,260,260); //копию области делаем как область отсечения SelectClipRgn(dc,r); //выводим прямоугольник с закругленными углами RoundRect(dc,150,0,280,150,15,15); //выводим эллипс Ellipse(dc,0,0,150,450); //освобождаем память, занятую областью r DeleteObject(r); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } После создания проекта Visual C++ на основе файла Expl13.cpp, его компиляции и запуска на исполнение, получим результат, представленный на рис.2. Некоторый список функций: • • • • • • • • • • • ExcludeClipRect — создает новую область отсечения, исключая указанный прямоугольник из текущей; ExtSelectClipRgn — объединяет указанную область с текущей областью отсечения; GetClipBox — определяет прямоугольник области отсечения; GetClipRgn — возвращает дескриптор области отсечения; GetRandomRgn — копирует текущую область отсечения в указанную область; IntersectClipRect — создает новую область отсечения, являющуюся пересечением текущей и указанного прямоугольника; OffsetClipRgn — передвигает область отсечения; PtVisible — определяет, находится ли указанная точка в области отсечения; RectVisible — определяет, пересекаются ли указанный прямоугольник с областью отсечения; SelectClipPath — выбирает текущий путь как область отсечения; SelectClipRgn — выбирает указанную область как область отсечения; —3— Рис.2. Область отсечения в виде прямоугольника в примере №13 Объект под названием путь или контур собирается из отрезков, кривых и простейших фигур, входящих в арсенал GDI среды Win32 API. В примере №14 строится простейший контур. Соответствующий фрагмент кода приведен в листинге №14. Полностью код содержится в файле Expl14.cpp. Листинг №14 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HDC dc; PAINTSTRUCT ps; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_PAINT: dc=BeginPaint(hwnd,&ps); BeginPath(dc); //открываем путь //перемещаем текущую точку вывода MoveToEx(dc,10,10,NULL); LineTo(dc,220,30); //рисуем линию LineTo(dc,160,190); //рисуем линию LineTo(dc,115,150); //рисуем линию CloseFigure(dc);//закрываем фигуру EndPath(dc);//рисуем линию, закрываем путь StrokePath(dc); //обводим путь текущим пером //заканчиваем обновление окна EndPaint(hwnd,&ps); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию —4— default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } На рис.3 показано окно с контуром, программа рисования которого содержится в файле Expl14.cpp. Рис.3. Пример построения простейшего контура в примере №14 Соответствующий список функций: • • • • • • • • BeginPath — открывает путь; CloseFigure — закрывает открытую фигуру в пути; EndPath — закрывает путь и помещает его в указанный контекст устройства; FillPath — закрывает все открытые фигуры и закрашивает текущей кистью; FlattenPath — преобразует любые кривые в путь; GetPath — определяет конечные и контрольные точки кривых в пути; PathToRegion — преобразует путь в область; StrokePath — обводит путь текущим пером. Прямоугольники в среде Win32 API описываются с помощью типа RECT. Ранее в примерах эти объекты уже встречались. Дополнительный набор функций, связанный с прямоугольниками следующий: • • • • • • • • • • CopyRect — копирует координаты одного прямоугольника в другой; EqualRect — определяет эквивалентность двух прямоугольников; InflateRect — изменяет размеры прямоугольника; IntersectRect — вычисляет пересечение двух прямоугольников; IsRectEmpty — определяет, пустой ли прямоугольник; OffsetRect — перемещает прямоугольник; PtlnRect — определяет нахождение точки внутри прямоугольника; SetRect — устанавливает параметры прямоугольника; SetRectEmpty — создает пустой прямоугольник; SubtractRect — вычисляет разницу двух прямоугольников; —5— • UnionRect — объединяет два прямоугольника. Ввод информации от мыши к окну происходит с помощью сообщений. Эти сообщения информируют активное окно, захватившее мышь, о перемещении мыши и нажатии ее клавиш. В примере №15 выводится окно, в котором изображаются координаты курсора мыши относительно левого верхнего угла клиентской области окна. Фрагмент соответствующего кода приведен на листинге №15. Полностью код приведен в файле Expl15.cpp. //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { POINT ptCursor; HDC hDC; char szMsg[128]; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_MOUSEMOVE: hDC=GetDC(hwnd); //стираем предыдущий текст путем его //прорисовки белым цветом RGB(255,255,255) SetTextColor(hDC, RGB(255,255,255)); TextOut(hDC,0,0,szMsg,lstrlen(szMsg)); //определяем координаты курсора GetCursorPos(&ptCursor); //определяем координаты курсора относительно //левого верхнего угла клиентской области окна ScreenToClient(hwnd,&ptCursor); wsprintf(szMsg,"Movement of the cursor of mouse X=%d, Y=%d", ptCursor.x,ptCursor.y); //записываем новый текст красным текстом RGB(255,0,0) SetTextColor(hDC, RGB(255,0,0)); TextOut(hDC,0,0,szMsg,lstrlen(szMsg)); ReleaseDC(hwnd,hDC); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Листинг №15 После создания проекта на базе файла Expl15.cpp, его компиляции и запуска на исполнение получится окно, подобное рис.4, в которое выводятся координаты мыши. —6— Рис.4. Вывод координат мыши в примере №15 Ассоциированный список функций: • • • • • • • • GetCapture — возвращает дескриптор окна, захватившего мышь; GetDoubleClickTime — определяет время двойного щелчка; GetMouseMovePointsEx — возвращает предыдущие координаты мыши или пера; ReleaseCapture — освобождает мышь от захвата; SetCapture — устанавливает захват мыши окном; SetDoubleClickTime — устанавливает время двойного щелчка; SwapMouseButton — меняет местами значение кнопок мыши; TrackMouseEvent — посылает сообщение, когда мышь покидает окно. Взаимодействие операционной системы Windows, окон и приложений осуществляется в основном с помощью сообщений. Сообщения делят на две категории: • сообщения-запросы изменяют или получают свойства адресата; • сообщения-уведомления передают значения своих свойств. Сопутствующие функции: • • • • • • • • DisspatchMessage — посылает сообщение окну адресату; GetMessage — получает сообщение из очереди; PostMessage — посылает сообщение в очередь; PostQuitMessage — сообщает системе о завершении потока; RegisterWindowMessage — регистрирует новое сообщение; ReplyMessage — отвечает на сообщения; SendMessage — передает сообщение оконной процедуре окна; SendNitifyMessage — посылает сообщение окну. —7— Семинар №5. Стандартные оконные классы. Диалоги Элементы управления в Windows такие, как кнопки, поля редактирования и пр. реализуются с помощью стандартных оконных классов. Окна этих классов необходимо создавать как дочерние. Имена этих классов следующие: • ″BUTTON″ — класс кнопок; • ″EDIT″ — класс редактируемых полей ввода; • ″LISTBOX″ — класс списков; • ″COMBOBOX″ — класс выпадающих списков; • ″MDICLIENT″ — класс окон MDI (интерфейс нескольких документов); • ″SCROLLBAR″ — реализует полосу прокрутки (слайдер); • ″STATIC″ — поддерживает статическое поле. В примере №16 программа создает пару кнопок в окне, нажатие которых приводит к появлению текста. В листинге №16 приведен фрагмент кода программы. Полный текст кода содержится в файле Expl16.cpp. //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HDC dc; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: //создаем две кнопки команды CreateWindow("BUTTON","Первая кнопка", WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE, 0,0,160,20,hwnd,0,hin,NULL); CreateWindow("BUTTON","Вторая кнопка", WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE, 0,40,160,20,hwnd,(HMENU)1,hin,NULL); break; //обработка сообщений - уведомлений case WM_COMMAND: //получаем дескриптор контекста устройства dc=GetDC(hwnd); switch(LOWORD(wParam)) { case 0://событие от первой кнопки if (HIWORD(wParam)==BN_CLICKED) TextOut(dc,170,20,"Нажата первая кнопка",20); break; case 1://событие от второй кнопки if (HIWORD(wParam)==BN_CLICKED) TextOut(dc,170,20,"Нажата вторая кнопка",20); break; }; Листинг №16 —1— break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } Кнопки различаются по идентификатору, заданному в функции CreateWindow в аргументе HMENU. Оконная процедура получает эти идентификаторы как младшее слово wParam сообщения WM_COMMAND. На рис.1 показано итоговое окно с двумя кнопками и соответствующим текстом. Рис.1. Две кнопки и текст в окне примера №16 В следующем примере №17 в итоговом окне отображаются два поля редактирования. Фрагмент кода приведен на листинге №17. Полный текст кода содержится в файле Expl17.cpp. Листинг №17 //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: —2— //создаем два окна редактирования текста CreateWindow("edit","Окно редактирования 1", WS_CHILD|WS_VISIBLE|ES_MULTILINE|WS_BORDER| WS_VSCROLL|WS_HSCROLL,50,50,160,80, hwnd,0,hin,NULL); CreateWindow("edit","Окно редактирования 2", WS_CHILD|WS_VISIBLE|ES_MULTILINE|WS_BORDER| WS_VSCROLL|WS_HSCROLL,220,50,160,80, hwnd,0,hin,NULL); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } void messageprocess() { MSG msg; //получаем в цикле следующее сообщение из очереди сообщений while(GetMessage(&msg,(HWND) NULL,0,0)) { //переводим сообщения виртуальных клавиш TranslateMessage(&msg); //передаем сообщение окну-адресату DispatchMessage(&msg); } } Отметим, что без функции TranslateMessage в окнах редакторов текст изменить невозможно, поскольку они работают с символьными сообщениями клавиатуры. На рис.2 приведено соответствующее окно с двумя редактируемыми списками. В следующем примере №18 займемся списком. В качестве списка будут выступать имена окон и их оконных классов, разделенных тире. Для получения информации о всех окнах верхнего уровня необходимо использовать функцию EnumWindows, которой передается адрес функции EnumW. Соответствующий фрагмент кода приведен на листинге №18. Полный текст кода содержится в файле Expl18.cpp. —3— Рис.2. Два поля редактирования в окне примера №17 Листинг №18 //Функция вставляющая имена оконного класса и //окна hwnd в список, определенный параметром lParam BOOL CALLBACK EnumW(HWND hwnd, LPARAM lParam) { int len=0; //количество записанных символов //выделяем память для строки char*str=new char [84]; //записываем в str имя оконного класса окна hwnd len=GetClassName(hwnd,str,40); str[len++]=' '; str[len++]='-'; str[len++]=' '; //дописываем в str имя окна hwnd len+=GetWindowText(hwnd,str+len,40); str[len]=0; //заканчиваем строку 0 //вставляем строку str в список lParam SendMessage((HWND)lParam,LB_ADDSTRING,0,(long)str); delete [] str; //освобождаем память //возвращаем 1 для продолжения перечисления окон return 1; } //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { HWND hw; //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: //создаем список с сортировкой hw=CreateWindow("LISTBOX","", WS_CHILD|WS_VISIBLE|LBS_STANDARD| —4— LBS_DISABLENOSCROLL,0,0,480,200, hwnd,0,hin,NULL); //обрабатываем все окна верхнего уровня EnumWindows((WNDENUMPROC)EnumW,(long)hw); break; case WM_DESTROY: //сообщение уничтожения окна //сообщаем системе о завершении программы PostQuitMessage(0); break; //все другие сообщений система обрабатывает по умолчанию default: return DefWindowProc(hwnd,msg,wParam,lParam); } return 0; } На рис.3 приведен итог работы программы с кодом из файла Expl18.cpp. Рис.3. Список имен окон и оконных классов примера №18 В примере №19 рассмотрим такой элемент управления, как закладка. Закладка относится к дополнительным оконным классам, доступ к ним осуществляется с помощью декларирования файла commctrl.h. Разместим на первой и второй закладках редактируемый текст, а на третьей закладке кнопку. Соответствующий фрагмент кода приведен на листинге №19. Полный текст кода находится в файле Expl19.cpp. Листинг №19 #include WINAPI WinMain //точка входа в Windows программу (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) —5— { //инициируем дополнительные оконные классы //с помощью следующей установки в проекте: //Project -> Settings -> Link -> в поле //Object/library modules вставляем //библиотеку comctl32.lib InitCommonControls(); //сохраняем идентификатор приложения hin=hInstance; //регистрируем новый оконный класс winclass(); //создаем окно HWND winhandler=makeexamplewin(); //отображаем окно ShowWindow(winhandler,SW_SHOW); //обрабатываем очередь сообщений messageprocess(); return 0; } //оконная процедура для обработки сообщений LRESULT CALLBACK WinProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { //переменная, содержащая атрибуты одной закладки TCITEMA tabitem; //дескриптор окна элемента управления static HWND hw; //массив дескрипторов окон, накладываемых на закладки static HWND hwtab[3]; int stab; //индекс выбранной закладки //msg - идентификатор обрабатываемого сообщения switch(msg) { case WM_CREATE: //создаем элемент управления закладками hw=CreateWindow(WC_TABCONTROL,"", WS_CHILD|WS_VISIBLE,0,0,150,150, hwnd,(HMENU)10,hin,NULL); //используем надписи на закладках tabitem.mask=TCIF_TEXT; //задаем имя первой закладки tabitem.pszText="tab1"; //вставляем первую закладку SendMessage(hw,TCM_INSERTITEM,1,LPARAM(&tabitem)); //задаем имя второй закладки tabitem.pszText="tab2"; //вставляем вторую закладку SendMessage(hw,TCM_INSERTITEM,2,LPARAM(&tabitem)); //задаем имя третьей закладки tabitem.pszText="tab3"; //вставляем третью закладку SendMessage(hw,TCM_INSERTITEM,3,LPARAM(&tabitem)); //создаем окна редакторов и кнопку —6— hwtab[0]=CreateWindow("edit","Текст на первой закладке", WS_CHILD|ES_MULTILINE,0,30,149,119,hw,0,hin,NULL); hwtab[1]=CreateWindow("edit","Текст на второй закладке", WS_CHILD|ES_MULTILINE,0,30,149,119,hw,0,hin,NULL); hwtab[2]=CreateWindow("button","Третья закладка", WS_CHILD|BS_PUSHBUTTON,5,60,120,30,hw,0,hin,NULL); break; //обрабатываем сообщения от закладок case WM_NOTIFY: //если произошла смена закладок if (((LPNMHDR)lParam)->code==TCN_SELCHANGE ) { stab=SendMessage(hw,TCM_GETCURSEL,0,0); //скроем остальные окна for (int i=0;i