74
Московский государственный университет им. М.В.Ломоносова Физический факультет ПРОГРАММИРОВАНИЕ В СРЕДЕ WINDOWS: WIN32 API К.Э. Плохотников Москва, 2006. — 1 —

Win API

Embed Size (px)

Citation preview

Page 1: Win API

Московский государственный университет им. М.В.ЛомоносоваФизический факультет

ПРОГРАММИРОВАНИЕ В СРЕДЕ WINDOWS:WIN32 API

К.Э. Плохотников

Москва, 2006.

— 1 —

Page 2: Win API

Семинар №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 Шилдт Г. Полный справочник по С. — М.: Издательский дом “Вильямс”, 2002. 704с.2 Мартынов Н.Н. Программирование для Windows на C/C++. Том 1. — М.: ООО “Бином-Пресс”, 2004.

528с.3 Саймон Р. Microsoft Windows API. Справочник системного программиста. — К.: ООО “ТИД “ДС”, 2004.

1216с.4 Верма Р.Д. Справочник по функциям Win32 API. —М.: Горячая линия-Телеком, 2005. 551с.5 http://msdn.microsoft.com/subscriptions/

— 2 —

Page 3: Win API

и визуализации движения цилиндра по наклонной плоскости. Данный проект будет представлен в деталях, включая физическую постановку задачи и все этапы программирования в среде 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 <windows.h>

HINSTANCE hin;//идентификатор приложения//оконная процедураLRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);//создание и регистрация нового оконного классаATOM winclass();HWND makeexamplewin();//новое окно//обработка очереди сообщений приложения

— 3 —

Page 4: Win API

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(){

//далее заполняем поля структуры WNDCLASSEXWNDCLASSEX 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 —

Page 5: Win API

//определяем курсор для окна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 —

Page 6: Win API

Адрес оконной процедуры передается окну при регистрации класса.

Рис.2. Итог работы программы примера №1

Большинство функций Win32 API объявлены в файле windows.h, который всегда будет присоединяться к нашим примерам.

При создании окна ему дается уникальный идентификатор — дескриптор окна (тип HWND). В примере №1 таким дескриптором является winhandler.

Система Windows API предоставляет множество стандартных классов окон, реализующих кнопки, поля редактирования, списки и другие элементы управления приложением.

В следующем примере №2 (проект Expl2) создадим три окна, третье из которых сделаем дочерним по отношению ко второму с помощью функции SetParent. Дочернее же окно должно находиться в пределах родительского. На листинге №2 приведен фрагмент кода, отличный от кода предыдущего примера. Полный текст кода приведен в файле Expl2.cpp, который находится в папке Примеры.

— 6 —

Page 7: Win API

Точка входа в Windows программу WINAPI WinMain

Создание и регистрация нового оконного класса winclass()

Создание окна makeexamplewin()

Отображение окна ShowWindow(…,SW_SHOW)

Обработка очереди сообщений messageprocess()

Рис.3. Блок-схема работы программы листинга №1

Листинг №2WINAPI 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;

}

На рис.4 приведен итог работы проекта Expl2. Согласно листингу №2, процедура makeexamplewin была применена трижды, что привело к порождению трех экземпляров окна одного и того же типа.

Приведем для иллюстрации несколько функций, непосредственно ассоциированных с окнами:

• CreateWindow — создает окно с заданными параметрами;• DefWindowProc — вызывает заданную по умолчанию процедуру окна;

— 7 —

Page 8: Win API

• DestroyWindow — уничтожает окно;• GetWindow — возвращает дескриптор окна;• GetWindowLong — возвращает значение указанного атрибута окна;• SetWindowLong — устанавливает значение указанного атрибута.

В общем случае область окна может быть разделена на две части: клиентская область — область ввода (вывода) и неклиентская область — область, где размещается заголовок окна, быть может, меню, а также рамка окна.

Рис.4. Создание трех окон, второе из которых дочернее третьему

Каждое приложение, разработанное на базе Win32 API, может поддерживать один или более процессов. Внутри отдельного процесса могут иметь место несколько потоков. Потоки позволяют осуществлять параллельное выполнение процессов.

Поток и поддерживающее его окно могут находиться в двух состояниях:

• приоритетное окно (foreground window) — окно, с которым в данный момент работает пользователь;

• заднее окно (background window) — неприоритетное окно.Система Windows поддерживает специальный оконный стек, который

определяет распределение окон по мнимой оси Z, направленной от экрана к пользователю. Пользователь может менять координату Z окон, а также делать так, чтобы одно из окон было всегда поверх других.

Возможны следующие типы окон:• перекрывающиеся окна (overlapped window) — как правило, это

главные окна приложения;• всплывающие окна (pop-up window) — окна временного

— 8 —

Page 9: Win API

пользования для отдельных сообщений, диалогов и пр.;• дочерние окна (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 —

Page 10: Win API

}return 0;

}

//создание и регистрация нового оконного классаATOM winclass(HINSTANCE k){

//далее заполняем поля структуры WNDCLASSEXWNDCLASSEX 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 —

Page 11: Win API

Рис.5. В примере №3 появляется родительское окно,а также дочернее и всплывающее окна

Дополнительный набор функций для работы с окнами:• ArrangeIconicWindows — упорядочивает свернутые окна;• BringWindowToTop — раскрывает перекрытое окно;• CascadeWindows — упорядочивает окна каскадом;• CloseWindow — сворачивает, но не уничтожает окно;• GetClientRect — возвращает размеры клиентской области окна;• GetWindowRect — возвращает экранные координаты окна;• GetWindowText — копирует заголовок окна в буфер;• IsWindowVisible — определяет состояние видимости окна;• MoveWindow — меняет позицию и размеры окна;• SetWindowText — устанавливает заголовок окна;• ShowWindow — изменяет состояние видимости окна;• TileWindow — упорядочивает окна черепицей.

Более подробно об аргументах данных функций можно ознакомиться либо по тем подсказкам, которые всплывают в среде Microsoft Visual C++6.0 после вставки открывающей скобки, либо в перечисленных выше источниках, либо в библиотеке MSDN.

— 11 —

Page 12: Win API
Page 13: Win API

Семинар №2. Контекст устройства. Вывод текста и линийКонтекст устройства представляет собой структуру объемом 64Кб, в

которой содержится все необходимое для вывода на устройство. Устройством может быть дисплей, окно, а также память, принтер или информационный контекст устройства.

Способами использования контекста устройства для окна являются следующие:

• выделяем окну собственный контекст устройства;• берем контекст устройства для оконного класса;• используем один из пяти общих контекстов устройства;• используем контекст устройства родительского окна.По умолчанию используется один из общих контекстов устройства.Для иллюстрации контекста устройства в примере №4 в окне

отображается текст, который виден до тех пор, пока не измениться размер окна. На листинге №4 приведен фрагмент кода соответствующей программы. Полный код представлен в файле Expl4.cpp.

Листинг №4WINAPI 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 —

Page 14: Win API

Рис.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 —

Page 15: Win API

//сообщаем системе о завершении программы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.

Листинг №6//оконная процедура для обработки сообщенийLRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)

— 3 —

Page 16: Win API

{//создание контекста устройства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 —

Page 17: Win API

Рис.3. Итог работы примера №6 — текст в клиентскойчасти окна выровненный по центру

Листинг №7//оконная процедура для обработки сообщений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;

— 5 —

Page 18: Win API

}

После формирования соответствующего проекта, компиляции и запуска на исполнение будет получено то, что представлено на рис.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 —

Page 19: Win API

представляет собой набор функций (всего более 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 —

Page 20: Win API
Page 21: Win API

Семинар №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);//сохраняем разрешение экрана по оси XOx=GetDeviceCaps(dc,HORZRES);//сохраняем разрешение экрана по оси YOy=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 —

Page 22: Win API

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 —

Page 23: Win API

• CreateDIBitmap — создает DDB рисунок из DIB;• CreateDIBSection — создает рисунок DIB;• GetBitmapDimensionEx — возвращает размеры рисунка;• GetDIBits — копирует рисунок в буфер;• GetPixel — возвращает RGB-значения пикселя в заданных координатах;• SetBitmapDimensionEx — устанавливает размеры рисунка;• SetPixel — устанавливает цвет пикселя;• StretchBlt — копирует и масштабирует рисунок.

Графический инструмент — кисть используется для закрашивания различного рода областей или заднего фона.

Различают три типа кистей: сплошная, штриховая и шаблонная.В следующем примере №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;

— 3 —

Page 24: Win API

//все другие сообщений система обрабатывает по умолчанию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 —

Page 25: Win API

гарнитурой 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 —

Page 26: Win API

Рис.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 —

Page 27: Win API

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 —

Page 28: Win API

Рис.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 —

Page 29: Win API

Семинар №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 —

Page 30: Win API

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 —

Page 31: Win API

Листинг №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);//освобождаем память, занятую областью rDeleteObject(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 —

Page 32: Win API

Рис.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 —

Page 33: Win API

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 —

Page 34: Win API

• UnionRect — объединяет два прямоугольника.

Ввод информации от мыши к окну происходит с помощью сообщений. Эти сообщения информируют активное окно, захватившее мышь, о перемещении мыши и нажатии ее клавиш. В примере №15 выводится окно, в котором изображаются координаты курсора мыши относительно левого верхнего угла клиентской области окна. Фрагмент соответствующего кода приведен на листинге №15. Полностью код приведен в файле Expl15.cpp.

Листинг №15//оконная процедура для обработки сообщений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;

}

После создания проекта на базе файла Expl15.cpp, его компиляции и запуска на исполнение получится окно, подобное рис.4, в которое выводятся координаты мыши.

— 6 —

Page 35: Win API

Рис.4. Вывод координат мыши в примере №15

Ассоциированный список функций:• GetCapture — возвращает дескриптор окна, захватившего мышь;• GetDoubleClickTime — определяет время двойного щелчка;• GetMouseMovePointsEx — возвращает предыдущие координаты мыши или пера;• ReleaseCapture — освобождает мышь от захвата;• SetCapture — устанавливает захват мыши окном;• SetDoubleClickTime — устанавливает время двойного щелчка;• SwapMouseButton — меняет местами значение кнопок мыши;• TrackMouseEvent — посылает сообщение, когда мышь покидает окно.

Взаимодействие операционной системы Windows, окон и приложений осуществляется в основном с помощью сообщений.

Сообщения делят на две категории:• сообщения-запросы изменяют или получают свойства адресата;• сообщения-уведомления передают значения своих свойств.Сопутствующие функции:• DisspatchMessage — посылает сообщение окну адресату;• GetMessage — получает сообщение из очереди;• PostMessage — посылает сообщение в очередь;• PostQuitMessage — сообщает системе о завершении потока;• RegisterWindowMessage — регистрирует новое сообщение;• ReplyMessage — отвечает на сообщения;• SendMessage — передает сообщение оконной процедуре окна;• SendNitifyMessage — посылает сообщение окну.

— 7 —

Page 36: Win API
Page 37: Win API

Семинар №5. Стандартные оконные классы. ДиалогиЭлементы управления в Windows такие, как кнопки, поля

редактирования и пр. реализуются с помощью стандартных оконных классов. Окна этих классов необходимо создавать как дочерние. Имена этих классов следующие:

• ″BUTTON″ — класс кнопок;• ″EDIT″ — класс редактируемых полей ввода;• ″LISTBOX″ — класс списков;• ″COMBOBOX″ — класс выпадающих списков;• ″MDICLIENT″ — класс окон MDI (интерфейс нескольких

документов);• ″SCROLLBAR″ — реализует полосу прокрутки (слайдер);• ″STATIC″ — поддерживает статическое поле.В примере №16 программа создает пару кнопок в окне, нажатие

которых приводит к появлению текста. В листинге №16 приведен фрагмент кода программы. Полный текст кода содержится в файле Expl16.cpp.

Листинг №16//оконная процедура для обработки сообщений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;};

— 1 —

Page 38: Win API

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 —

Page 39: Win API

//создаем два окна редактирования текста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 —

Page 40: Win API

Рис.2. Два поля редактирования в окне примера №17

Листинг №18//Функция вставляющая имена оконного класса и//окна hwnd в список, определенный параметром lParamBOOL CALLBACK EnumW(HWND hwnd, LPARAM lParam){

int len=0; //количество записанных символов//выделяем память для строкиchar*str=new char [84];//записываем в str имя оконного класса окна hwndlen=GetClassName(hwnd,str,40);str[len++]=' ';str[len++]='-';str[len++]=' ';//дописываем в str имя окна hwndlen+=GetWindowText(hwnd,str+len,40);str[len]=0; //заканчиваем строку 0//вставляем строку str в список lParamSendMessage((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 —

Page 41: Win API

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 <commctrl.h>WINAPI WinMain //точка входа в Windows программу(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

— 5 —

Page 42: Win API

{//инициируем дополнительные оконные классы//с помощью следующей установки в проекте://Project -> Settings -> Link -> в поле//Object/library modules вставляем//библиотеку comctl32.libInitCommonControls();//сохраняем идентификатор приложения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 —

Page 43: Win API

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<3;i++)

ShowWindow(hwtab[i],SW_HIDE);//отобразим окно выбранной закладкиShowWindow(hwtab[stab],SW_SHOW);

}break;

case WM_DESTROY: //сообщение уничтожения окна//сообщаем системе о завершении программыPostQuitMessage(0);break;//все другие сообщений система обрабатывает по умолчанию

default:return DefWindowProc(hwnd,msg,wParam,lParam);

}return 0;

}

Рис.4. Закладки примера №19

На рис.4 приведено итоговое окно с закладками.Ассоциированный список функций:• CheckDlgButton — изменяет состояние кнопки-флажка;

— 7 —

Page 44: Win API

• CheckRadioButton — изменяет состояние радиокнопки в группе;• IsDlgButtonChecked — определяет отмечен ли элемент управления флажком или

переключателем;• DlgDirList — заполняет список именами файлов и подкаталогов;• DlgDirListComboBox — заполняет выпадающий список именами файлов и подкаталогов;• DlgDirSelectEx — определяет текущий выбор списка;• GetListBoxInfo — определяет информацию о списке;• EnableScrollBar — включает или отключает одну или обе стрелки слайдера;• ScrollWindowEx — прокручивает часть клиентской области окна.

В Windows предусмотрены специальные окна, именуемые диалоговыми окнами или просто диалогами. Основное их назначение — максимально упростить диалог между пользователем и приложением. В проекте моделирования движения цилиндра по наклонной плоскости будет использовано именно диалоговое окно для задания численных значений параметров модели и директив по запуску или выходу из программы.

Существует два типа диалоговых окон:• модальные, когда все остальные действия заблокированы до

завершения диалога;• немодальные, когда все остальные действия можно совершать без

закрытия диалога.Особенностью диалогов является то, что они не вызывают функцию

DefWindowProc. Наиболее широко используемый метод создания диалогов — создание файла ресурсов.

В примере №20 представлены два наиболее часто встречающихся диалога: диалоги выбора цвета и шрифта. Оба эти диалога являются модальными. В листинге №20 приведен соответствующий фрагмент кода. Полный текст кода содержится в файле Expl20.cpp.

Листинг №20WINAPI WinMain //точка входа в Windows программу(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

//сохраняем идентификатор приложенияhin=hInstance;//регистрируем новый оконный классwinclass(); //создаем окноHWND winhandler=makeexamplewin();//отображаем окноShowWindow(winhandler,SW_SHOW);//отобразим стандартное окно диалога выбора цветаCOLORREF dop[16]; //палитра дополнительных цветов//переменная для инициализации диалогаCHOOSECOLOR ch;//размер структурыch.lStructSize=sizeof(CHOOSECOLOR);//указываем окно владельца диалогаch.hwndOwner=winhandler;ch.Flags=CC_ANYCOLOR|CF_SCREENFONTS; //опции//указываем палитру для дополнительных цветов,

— 8 —

Page 45: Win API

//для простоты не будем ее заполнятьch.lpCustColors=dop;ChooseColor(&ch); //создаем диалог выбора цвета//переменная для инициализации диалогаCHOOSEFONT cf;//переменная для сохранения выбранного фонтаLOGFONT f;//размер структурыcf.lStructSize=sizeof(CHOOSEFONT);//указываем окно владельца диалогаcf.hwndOwner=winhandler;//опцииcf.Flags=CF_APPLY|CF_SCREENFONTS|CF_EFFECTS;//указываем, куда сохранить выбранный шрифтcf.lpLogFont=&f;ChooseFont(&cf); //создаем диалог выбора шрифта//обрабатываем очередь сообщенийmessageprocess();return 0;

}

На рис.5 приведен итог работы соответствующего проекта по реализации примера №20.

Рис.5. Диалог выбора цвета в окне примера №20

Ассоциированный список функций:• CreateDialog, CreateDialogIndirect, CreateDialogIndirectParam, CreateDialogParam — создают

окна диалогов;• DefDlgProc — функция обработки сообщений по умолчанию;• DialogBox — создает окно модального диалога;• EndDialog — уничтожает окно модального диалога;• MessageBox — выводит окно сообщения.

— 9 —

Page 46: Win API
Page 47: Win API

Семинар №6. Таймер, курсор, менюДля задач моделирования динамики важно иметь счетчик

равноотстоящих моментов времени. Для целей моделирования временной динамики можно использовать встроенную для каждого окна возможность, называемую таймер. Каждому окну можно назначить таймер. После назначения таймера через указанный промежуток времени система будет посылать сообщение окну.

В примере №21 создается таймер для окна. В окне через каждые две секунды будет отображаться прямоугольник в новой позиции и с новой шириной. В программе уничтожение таймера произойдет после 15-го перемещения. Соответствующий фрагмент кода приведен на листинге №21. Полный текст программы содержится в файле Expl21.cpp.

Листинг №21//функция обработки таймераvoid timerproc(HWND hwnd,UINT msg,

UINT_PTR idevent,DWORD dwtime);

WINAPI WinMain //точка входа в Windows программу(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

//сохраняем идентификатор приложенияhin=hInstance;//регистрируем новый оконный классwinclass(); //создаем окноHWND winhandler=makeexamplewin();//отображаем окноShowWindow(winhandler,SW_SHOW);

//устанавливаем таймер созданному окнуSetTimer(winhandler,1,2000,(TIMERPROC)timerproc);//обрабатываем очередь сообщенийmessageprocess();return 0;

}//функция обработки таймераvoid timerproc(HWND hwnd,UINT msg,

UINT_PTR idevent,DWORD dwtime){

static int i=0;//координаты прямоугольникаstatic RECT rect={0,20,15,150};if (i<15 && i>0){

//стираем старый прямоугольникDrawFocusRect(GetDC(hwnd),&rect);rect.left+=5; //изменяем позицию прямоугольникаrect.right+=15; //изменяем ширину прямоугольника//выводим прямоугольник в новой позицииDrawFocusRect(GetDC(hwnd),&rect);i++;

— 1 —

Page 48: Win API

}else{

if (i==0){

//выводим прямоугольник в первый разDrawFocusRect(GetDC(hwnd),&rect);i++;

}if (i==15)

KillTimer(hwnd,idevent); //уничтожаем таймер}

}

На рис.1 приведен внешний вид окна с прямоугольником, который в течение 30 секунд подрастал до тех размеров, которые представлены на рисунке.

Рис.1. Иллюстрация работы таймера в примере №21

Список сопутствующих функций:• GetSystemTime — возвращает текущую системную дату и время;• GetTickCount — возвращает число миллисекунд после запуска Windows;• KillTimer — уничтожает указанный таймер;• QueryPerformanceCounter — возвращает текущее значение счетчика;• QueryPerformanceFrequency — определяет частоту счетчика;• SetSystemTime — устанавливает системное время и дату;• SetTimer — создает либо видоизменяет системный таймер;• TimeProc — обработчик сообщения WM_TIMER.

Курсор является мигающей линией, прямоугольником или произвольным изображением. Обычное назначение курсора указывать на место вставки текста или изображения. Система Windows обеспечивает один курсор на очередь сообщений, при этом курсор отображается лишь активным

— 2 —

Page 49: Win API

окном. В примере №22 выводится курсор прямоугольной формы, черного цвета с явно утрированными размерами. Соответствующий фрагмент кода приведен на листинге №22. Полный текст кода приведен в файле Expl22.cpp.

Листинг №22//оконная процедура для обработки сообщенийLRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){

//msg - идентификатор обрабатываемого сообщенияswitch(msg){

case WM_CREATE://создаем форму курсораCreateCaret(hwnd,NULL,50,25);ShowCaret(hwnd); //отображаем курсор в окнеSetCaretPos(10,10); //изменяем позицию курсораbreak;

case WM_DESTROY: //сообщение уничтожения окна//сообщаем системе о завершении программыPostQuitMessage(0);break;//все другие сообщений система обрабатывает по умолчанию

default:return DefWindowProc(hwnd,msg,wParam,lParam);

}return 0;

}

Создадим проект Visual C++ на базе файла Expl22.cpp, откомпилируем его и пошлем на исполнение. В итоге должно появиться окно с мигающим прямоугольником черного цвета. Этот прямоугольник выступает в качестве курсора. На рис.2 приведен внешний вид окна, которое должно появиться в итоге работы проекта.

Список ассоциированных функций:• CreateCaret — создает новую форму курсора;• DestroyCaret — уничтожает текущую форму курсора;• GetCaretBlinkTime — возвращает время в миллисекундах периода мерцания курсора;• GetCaretPos — определяет текущую позицию курсора;• HideCaret — скрывает курсор экрана;• SetCaretBlinkTime — устанавливает параметры мерцания курсора;• SetCaretPos — устанавливает позицию курсора;• ShowCaret — отображает курсор на экране.

— 3 —

Page 50: Win API

Рис.2. Пример №22: курсор в окне

Помимо кнопок, списков и прочих элементов управления, еще одним способом управления является меню. Знание функций меню позволяет создавать динамически изменяемые меню, оптимально настраиваемые для решения задач пользователя. Обработка выбора пунктов меню происходит через сообщение WM_COMMAND. В примере №23 приводится процедура создания меню окна. Соответствующий фрагмент кода приведен на листинге №23. Полный текст кода дан в файле Expl23.cpp.

Листинг №23HWND makeexamplewin(){

MENUITEMINFO item; //структура для элемента меню//размер структуры менюitem.cbSize=sizeof(MENUITEMINFO);//тип элемента меню (строка в нашем случае)item.fType=MFT_STRING;//устанавливаем член dwTypeData структуры//макрос MIIM_STRING можно найти в файле//winuser.h он равен 0x00000040item.fMask=0x00000040;//создаем пустое менюHMENU menu=CreateMenu();//вставляем элемент меню "OPTIONS"item.dwTypeData="OPTIONS";InsertMenuItem(menu,1,0,&item);//вставляем элемент меню "HELP"item.dwTypeData="HELP";InsertMenuItem(menu,2,0,&item);//создаем подменюHMENU submenu=CreatePopupMenu();//вставляем два элемента в подменюitem.dwTypeData="OPEN";

— 4 —

Page 51: Win API

InsertMenuItem(submenu,11,0,&item);//после \t можно указать используемую//комбинацию клавиш для элемента менюitem.dwTypeData="SAVE\tCtrl+S";InsertMenuItem(submenu,12,0,&item);//указываем, что вставляем подменюitem.fMask=MIIM_SUBMENU|0x00000040;//указываем дескриптор подменюitem.hSubMenu=submenu;//указываем имя подменю//(& - подчеркивает следующий символ)item.dwTypeData="&FILE";//вставляем подменю по позицииInsertMenuItem(menu,1,1,&item);HWND hw=CreateWindowEx

(WS_EX_OVERLAPPEDWINDOW,"examplewinclass","Окно с меню",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hin,NULL);

//назначаем созданное меню окнуSetMenu(hw,menu);//возвращаем дескриптор окнаreturn hw;

}

Рис.3. Создание меню в примере №23

Создавая соответствующий проект Visual C++, получаем на выходе окно, подобное тому, которое приведено на рис.3.

Профильный список функций:• CreateMenu — создает меню;• CreatePopupMenu — создает подменю или раскрывающееся меню;• DeleteMenu — удаляет элемент из указанного меню;• DestroyMenu — уничтожает меню;

— 5 —

Page 52: Win API

• DrawMenuBar — перерисовывает строку меню указанного окна;• EnableMenuItem — изменяет состояние меню;• EndMenu — завершает активное меню вызывающего потока;• GetMenu — возвращает дескриптор меню указанного окна;• GetMenuBarInfo — находит информацию о полосе меню;• GetMenuItemCount — определяет число элементов в меню;• GetMenuItemID — определяет идентификатор пункта меню;• GetMenuItemInfo — возвращает информацию о пункте меню;• GetMenuItemRect — возвращает ограничительный прямоугольник указанного пункта меню;• HiliteMenuItem — изменяет подсветку элемента меню;• InsertMenuItem — вставляет новый пункт меню в указанную позицию в меню;• IsMenu — определяет, является ли дескриптор дескриптором меню;• RemoveMenu — удаляет пункт меню;• SetMenu — назначает новое меню указанному окну.

Обычно название отдельных клавиш меню сопровождается набором оперативных клавиш, акселераторов для быстрого доступа к этим элементам меню. Соответствующий список функций таков:

• CreateAcceleratorTable — создает таблицу оперативных клавиш;• DestroyAcceleratorTable — уничтожает таблицу оперативных клавиш;• LoadAccelerators — загружает таблицу оперативных клавиш;• TranslateAccelerator — переводит сообщения нажатия клавиш в WM_COMMAND или

WM_SYSCOMMAND, если текущая комбинация клавиш находится в указанной таблице.

— 6 —

Page 53: Win API

Семинар №7. Консоль, функции директорий, файлов идисков. Файл ресурсов

Система Windows позволяет создавать специальный тип окон — текстовые окна. Их еще называют консольными окнами или просто консолями. Можно создавать полностью консольные приложения, подобные, например, файловому менеджеру Far. В некотором смысле консольные окна наследники DOS. В примере №24 показано как создавать консольное окно. На листинге №24 приведен соответствующий фрагмент кода. Полный текст кода содержится в файле Expl24.cpp.

Листинг №24/*Для ожидания нажатия клавиши определенавспомогательная функция getkey. Функцияждет нажатия клавиши и сохраняет ее коды:scan - скан-код,ascii - символьный код,vkey - код виртуальной клавиши.*/

unsigned long getkey(char* scan, char* ascii, char* vkey){

unsigned long s;INPUT_RECORD key;//получаем дескриптор буфера ввода консолиHANDLE hinput=GetStdHandle(STD_INPUT_HANDLE);while (1) //выполняем цикл до нажатия клавиши{

if (ReadConsoleInput(hinput,&key,1,&s) &&(key.Event.KeyEvent.bKeyDown))if (key.EventType==KEY_EVENT)

break;//если указатель не пустой, сохраняем кодif (scan)

*scan=(char)key.Event.KeyEvent.wVirtualScanCode;if (ascii)

*ascii=(char)key.Event.KeyEvent.uChar.AsciiChar;if (vkey)

*vkey=(char)key.Event.KeyEvent.wVirtualKeyCode;}

//возвращаем состояние таких клавиш, как Altreturn key.Event.KeyEvent.dwControlKeyState;

}

WINAPI WinMain //точка входа в Windows программу(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

//сохраняем идентификатор приложенияhin=hInstance;unsigned long wi;COORD coord;coord.X=0; coord.Y=0;

— 1 —

Page 54: Win API

//регистрируем новый оконный классwinclass(); //создаем окноHWND winhandler=makeexamplewin();//отображаем окноShowWindow(winhandler,SW_SHOW);AllocConsole(); //создаем окно консоли//изменяем заголовок консольного окнаSetConsoleTitle("My first console window");//получаем дескриптор экранного буфера консолиHANDLE hout=GetStdHandle(STD_OUTPUT_HANDLE);//заполняем фон текста серым цветомFillConsoleOutputAttribute(hout,BACKGROUND_BLUE|

BACKGROUND_GREEN|BACKGROUND_RED,80*25,coord,&wi);//выводим приветствиеWriteConsole(hout,"Hello world\n",12,&wi,0);//устанавливаем атрибуты выводимого текстаSetConsoleTextAttribute(hout,FOREGROUND_RED|

BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED);

WriteConsole(hout,"Press any key\n",14,&wi,0);getkey(NULL,NULL,NULL); //ждем нажатия клавиши//освобождаем память, выделенную под консольFreeConsole();//обрабатываем очередь сообщенийmessageprocess();return 0;

}

Запуская проект с файлом Expl24.cpp, получим подобно рис.1 пару окон: обычное окно и консольное.

Рис.1. Генерация консоли в примере №24

Ассоциированные функции:• AllocConsole — выделяет консоль;

— 2 —

Page 55: Win API

• CreateConsoleScreenBuffer — создает экранный буфер;• FlushConsoleInputBuffer — сбрасывает буфер ввода консоли;• FreeConsole — удаляет консоль из памяти;• GetConsoleCursorInfo — возвращает информацию о курсоре консоли;• GetConsoleScreenBufferInfo — возвращает информацию об экранном буфере;• GetConsoleTitle — возвращает заголовок консольного окна;• ReadConsole — читает символьные данные из буфера ввода консоли и удаляет их из буфера;• ScrollConsoleScreenBuffer — прокручивает часть экранного буфера консоли;• SetConsoleActiveScreenBuffer — устанавливает активный экранный буфер консоли;• SetConsoleCursorPosition — устанавливает позицию курсора;• SetConsoleTextAttribute — устанавливает атрибуты выводимого на консоль текста;• SetConsoleTitle — изменяет заголовок окна консоли;• SetConsoleWindowInfo — изменяет информацию об окне консоли;• WriteConsole — записывает строку в экранный буфер консоли с текущего положения курсора.

Система Windows предоставляет стандартный набор средств для управления директориями, файлами и дисками. В примере №25 программа опознает наличие дисков на компьютере, тип файловой системы, объем занятого и свободного места на диске, а также максимальную длину имени файла. Код приведен на листинге №25 и в файле Expl25.cpp.

Листинг №25#include <windows.h>#include <iostream>#include <string>#include <vector>using namespace std;

//массив имен установленных дисковых устройствvector <string> disks;void get_disks(); //заполнение массива disks//получение информации о выбранном устройствеvoid get_disks_info();

//структура информации о дискеstruct DISK_info{

string name; //имя диска, например "c:\\"string type; //тип устройстваstring type_fs; //тип файловой системыunsigned long fnlen; //максимальная длина файлаunsigned __int64 totalb; //размер диска в байтахunsigned __int64 freeb; //размер свободного места//заполнение структуры по имени устройстваvoid init(string);void out(); //вывод информации

};

void DISK_info::init(string _name){

char tmp[100];name=_name;//определяем тип устройстваswitch (GetDriveType(name.begin())){

— 3 —

Page 56: Win API

case DRIVE_UNKNOWN:type="type cannot be determined"; break;

case DRIVE_REMOVABLE: type="removable"; break;case DRIVE_FIXED: type="fixed"; break;case DRIVE_REMOTE: type="remote drive"; break;case DRIVE_CDROM: type="CD-ROM"; break;case DRIVE_RAMDISK: type="RAM disk"; break;default: type="error";

}//запрещаем отображение сообщения//для вставки в устройство дискаSetErrorMode(SEM_FAILCRITICALERRORS);//определяем тип файловой системы и//максимальную длину файлов в символахif (GetVolumeInformation(name.begin(),0,0,0,

&fnlen,0,tmp,100))type_fs=tmp;

else{

type_fs="disk not inserted";fnlen=0;

}//определяем размер дискаGetDiskFreeSpaceEx(name.begin(),0,

(PULARGE_INTEGER) &totalb,(PULARGE_INTEGER) &freeb);}

void DISK_info::out(){

//определим переменные типа long//для представления объема памяти в Mblong tot,fre;cout << "\nDisk name: " << name << "\n";cout << "Drive type: " << type << "\n";cout << "Current file system: " << type_fs << "\n";cout << "Maximum length of file name: " << fnlen << "\n";if (type_fs != "disk not inserted"){tot=totalb/1024; tot=tot/1024;cout << "Total size of disk: " << tot << " Mb\n";fre=freeb/1024; fre=fre/1024;cout << "Free size of disk: " << fre << " Mb\n";}else{

cout << "Total size of disk: " << 0 << " Mb\n";cout << "Free size of disk: " << 0 << " Mb\n";

}}

int main(int argc, char* argv[]){

get_disks();get_disks_info();return 0;

}

— 4 —

Page 57: Win API

//заполнение массива disksvoid get_disks(){

//маска установленных устройствunsigned long d=GetLogicalDrives();unsigned long mask=1;char dname='A';string tmp;

if (d==0){

cout << "some error occur";return;

}//заполняем disks именами устройствfor (int i=0; i<='Z'; i++){

if (d&mask){

tmp="";tmp+=dname;tmp=tmp+":\\";disks.push_back(tmp);

}mask<<=1;dname++;

}}

//получение информации о выбранном устройствеvoid get_disks_info(){

DISK_info disk;int n; //номер выбранного устройстваfor (;;){

cout << "\nYou have " << disks.size() <<" logical disks\n";

cout << "Select disk for view more information: ";cin >> n; //выбор устройстваif (n<disks.size()){

//получить информацию о дискеdisk.init(disks[n]);//вывод информацииdisk.out();

}else break;

}}

На рис.2 приведен итог работы программы листинга №25, где, в частности, приводится базовая информация о диске C:\ текущего компьютера.

— 5 —

Page 58: Win API

Рис.2. Информация о диске C:\ полученная в примере №25

Список профильных функций:• CopyFileEx — копирует файл;• CreateDirectoryEx — создает директорию;• CreateFile — создает файл;• DeleteFile — удаляет файл;• FindClose — закрывает поиск файлов;• FindFirstFile — начинает поиск файлов;• FindNextFile — продолжает поиск файлов;• GetBinaryType — определяет тип файлов;• GetCurrentDirectory — определяет текущую директорию;• GetDiskFreeSpaceEx — определяет свободное место на диске;• GetFileAttributes — возвращает атрибуты файла;• GetFileSizeEx — возвращает размер файла;• GetFileType — определяет тип файла;• MoveFileEx — перемещает файл;• ReadFile — читает данные из файла;• RemoveDirectory — удаляет директорию;• ReplaceFile — заменяет файл;• SetEndOfFile — устанавливает конец файла;• SetFileAttributes — устанавливает атрибуты файла;• SetFilePointerEx — перемещает указатель файла;• WriteFile — читает данные из файла.

Файл описания ресурса *.rc определяет такие компоненты, как меню, диалоговые окна, пиктограммы, растровые изображения, элементы управления и другие средства. Файл ресурса позволяет отказаться от использования отдельных файлов, прикрепленных к приложению. Файл описания ресурса транслируется транслятором ресурса RC.EXE, который вырабатывает файл RES. После создания исполняемого файла проекта *.exe файл RES присоединяется к концу исполняемой программы. Каждый ресурс идентифицируется либо именем, либо числом от 1 до 65535 (число нужно пропустить через макрос MAKEINTRESOURSE, который преобразует число в тип ресурса).

В примере №26 меню в программе создается с помощью файла

— 6 —

Page 59: Win API

ресурса. На листинге №26 приводится фрагмент кода соответствующей программы. Полный текст программы находится в файле Expl26\Expl26.cpp.

Листинг №26//создание и регистрация нового оконного классаATOM winclass(){

//далее заполняем поля структуры WNDCLASSEXWNDCLASSEX 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);//определяем курсор для окнаwinexam1.hCursor=LoadCursor(hin,IDC_APPSTARTING);//определяем кисть для окнаwinexam1.hbrBackground=HBRUSH(COLOR_WINDOW+1);//имя ресурса менюwinexam1.lpszMenuName="sample";//имя оконного класса winexam1.lpszClassName="examplewinclass";//определяем маленькую иконкуwinexam1.hIconSm=LoadCursor(hin,IDC_APPSTARTING);//регистрация оконного классаreturn RegisterClassEx(&winexam1);

}

Меню программы создается в отдельном файле ресурса Expl26\sample.rc, который присоединяется обычным образом к соответствующему проекту Visual C++ в папку ресурсов. На листинге №27 приведено содержание файла ресурсов.

Листинг №27//Содержимое файла ресурса sample.rcsample MENU{

POPUP "&FILE"{

MENUITEM "SAVE" 120MENUITEM "LOAD" 121MENUITEM "SAVE AS" 122MENUITEM "EXIT" 123

}

— 7 —

Page 60: Win API

POPUP "S&etup"{

MENUITEM "&autosave", 124MENUITEM "&make backup", 125, CHECKEDMENUITEM "", 126

POPUP "&Display option"{

MENUITEM "color", 127MENUITEM "resolution", 128

}}

}

Рис.3. Создание меню с помощью файла ресурсов в примере №26

На рис.3 приведен итог работы исполняемого файла, полученного в проекте с файлом Expl26.cpp.

В следующем примере №27 создается меню из одного элемента, при выборе которого появляется модельный диалог. На листинге №28 приводится фрагмент соответствующего кода. Полный текст кода содержится в файле Expl27\Expl27.cpp.

Листинг №28//оконная процедура диалогаINT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){

switch (msg){

//обрабатываем нажатие кнопкиcase WM_COMMAND:

//уничтожаем диалогreturn EndDialog(hwnd,1);

//остальные сообщения обрабатывает система

— 8 —

Page 61: Win API

default: return 0;}

}

//оконная процедура для обработки сообщенийLRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){

//msg - идентификатор обрабатываемого сообщенияswitch(msg){

case WM_COMMAND://если выбор элемента меню "about"if (LOWORD(wParam)==10)

//то создаем модальный диалогDialogBox(hin,"about_dlg",hwnd, (DLGPROC) DlgProc);

break;case WM_DESTROY: //сообщение уничтожения окна

//сообщаем системе о завершении программыPostQuitMessage(0);break;//все другие сообщений система обрабатывает по умолчанию

default:return DefWindowProc(hwnd,msg,wParam,lParam);

}return 0;

}

//создание и регистрация нового оконного классаATOM winclass(){

//далее заполняем поля структуры WNDCLASSEXWNDCLASSEX 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);//определяем курсор для окнаwinexam1.hCursor=LoadCursor(hin,IDC_APPSTARTING);//определяем кисть для окнаwinexam1.hbrBackground=HBRUSH(COLOR_WINDOW+1);//имя ресурса менюwinexam1.lpszMenuName="sample";//имя оконного класса winexam1.lpszClassName="examplewinclass";//определяем маленькую иконку

— 9 —

Page 62: Win API

winexam1.hIconSm=LoadCursor(hin,IDC_APPSTARTING);//регистрация оконного классаreturn RegisterClassEx(&winexam1);

}

Содержимое файла ресурсов для примера №27 представлено на листинге №29. Сам файл sample.rc находится в папке Expl27.

Листинг №29sample MENU{

MENUITEM "ABOUT", 10}

about_dlg DIALOGEX 10, 10, 150, 100

CAPTION "about"{

LTEXT "Это модальный диалог, сообщающий о программе",2, 0, 10, 150, 40

CTEXT "Нажмите кнопку OK для закрытия диалога",2, 0, 40, 150, 12

PUSHBUTTON "&OK", 3, 60, 80, 30, 12}

После создания исполняемого файла в данном проекте с учетом основного файла Expl27\Expl27.cpp и файла ресурсов Expl27\sample.rc, и после нажатия пункта меню ABOUT, получится картина представленная на рис.4.

Рис.4. Меню с одним пунктом, которое вызываетдиалог в примере №27

— 10 —

Page 63: Win API

Семинар №8. Движение цилиндра по наклонной плоскостиНа примере разработки прообраза курсовой работы “Движение

цилиндра по наклонной области” данный семинар завершает введение в среду Win32 API.

Для моделирования движения цилиндра по наклонной плоскости необходимо обратиться к любому учебнику по теоретической механике, в котором дается определение для коэффициентов трения скольжения и качения. Так, в учебнике6 приводится следующий способ решения задачи движения цилиндра по наклонной плоскости.

На рис.1 приводится схема действия сил и моментов на движущийся цилиндр. Силы: P — вес, N — реакция опоры, T — трение скольжения. Момент MT — момент силы трения качения. Угол α — угол наклонной плоскости (0 ≤ α ≤ π / 2).

y

x

T N

P

MT O

α

Рис.1. Схема сил и моментов, действующих на движущийсяпо наклонной плоскости цилиндр

Запишем условия равновесия цилиндра:

=−=−=+−

.0sin

,0cos

,0sin

ααα

rPM

PN

PT

T

(1)

Кроме того, должны выполняться следующие неравенства:T ≤ f N, MT ≤ δ N, (2)

где f — коэффициент трения скольжения, δ — коэффициент трения качения.Объединяя (1), (2), находим: tg α ≤ f, tg α ≤ δ / r. Введем два

критических значения для угла наклонной плоскости α f = arctg f и 6 Бутенин Н.В., Лунц Я.Л., Меркин Д.Р. Курс теоретической механики. — СПб.: “Лань”, 2002.

— 1 —

Page 64: Win API

αδ = arctg(δ / r), которые характеризуют потерю устойчивости цилиндра по отношению к силе трения скольжения и моменту силы трения качения. Возвращаясь к неравенствам (2), приходим к следующей классификации типов движения:

1) цилиндр покоится: α ≤ min(α f , αδ );2) цилиндр скользит: α f ≤ α ≤ αδ ;3) цилиндр катится: αδ ≤ α ≤α f ;4) цилиндр и скользит, и катится: α ≥ max(α f , αδ ).Пусть в начальный момент времени цилиндр выставляется на вершине

наклонной плоскости в начало координат. Будем отслеживать движение центра масс цилиндра (переменная x = x( t ), где t — время) и угол поворота цилиндра (переменная ϕ = ϕ ( t )). Соответствующие уравнения движения можно переписать в следующем виде:

≥−=

≥−

=

. ,)cos(

)sin(

; ,)cos(

sin( )

δδ

δ ααα

ααϕ

ααα

αα

rPJ

Pxm ff

f

(3)

Первое уравнение в (3) описывает отдельно скольжение цилиндра, которое имеет место при α ≥ αf. Второе уравнение в (3) описывает качение цилиндра, которое имеет место при α ≥ αδ. В (3) m, r и J — масса, радиус и момент инерции цилиндра. Будем считать, что цилиндр состоит из

однородного материала, тогда 241 mrJ = . Решая уравнения (3), находим

≥−=

≥−

=

, ,2)cos(

)sin(4)(

; ,2)cos(

)sin()(

2

2

δδ

δ ααα

ααϕ

ααα

αα

gt

rt

gttx f

f

f

(4)

где g — ускорение свободного падения.Перейдем к построению схемы изображения движения цилиндра по

наклонной плоскости. На рис.2 представлена система координат в клиентской области окна, наклонная плоскость и цилиндр в начальный момент времени. Длина наклонной плоскости обозначена буквой L.

Итак, в качестве входных параметров проекта выступают;1) угол наклонной плоскости, α;2) коэффициент трения скольжения, f;3) коэффициент трения качения, δ;4) радиус цилиндра, r;

— 2 —

Page 65: Win API

5) длина наклонной плоскости, L.Проведем перенормировку радиуса цилиндра и длины наклонной

плоскости к размерам окна, в которых будет изображено движение цилиндра. Согласно рис.2, общие габариты цилиндра и наклонной плоскости по осям x и y следующие: r (1−sinα) + L cosα, r (1+cosα) + L sinα. В качестве масштаба s выбираем из этих габаритов максимальное значение, т.е.

s = max{ r (1−sinα) + L cosα, r (1+cosα) + L sinα}.Перенормировку осуществляем по схемеr → (r / s)min{Lx,Ly}, L → (L / s)min{Lx,Ly}

где Lx, Ly габариты в пикселях окна изображения по осям x и y соответственно.

α

x

y

A

B

C

D

L Lsinα

Lcosα

dx

dy

r(1− sinα)

r(1+cosα)

M

Рис2. Схема изображения движения цилиндра понаклонной плоскости

Точки A, B, C, D, M имеют следующие координаты:A = (dx + r (1−sinα),dy + r (1 + cosα) + L sinα),B = (dx + r (1−sinα),dy + r (1 + cosα)),C = (dx + r,dy + r),D = (dx + r (1−sinα) + L cosα,dy + r (1 + cosα) + L sinα),M = (dx + r + 0.5 r sinα,dy + r + 0.5 r cosα),

где M — маркер, изображающий вращение цилиндра.Теперь все готово для разработки программы по изображению

скатывания цилиндра по наклонной плоскости. В листинге №30 приведен полный код данного проекта. Этот же код содержится в файле InclPlane.cpp, который лежит в папке Expl29.

— 3 —

Page 66: Win API

Листинг №30//Пример №28. Скатывание цилиндра по ///////////////////////////////////////// наклонной плоскости//////////////////#include <windows.h>//Присоединяется заголовочный файл, который//находится в папке InclPlane#include "resource.h"#include <stdlib.h>//Заголовочный файл математических функций#include <math.h>//ШаблонBOOL CALLBACK DlgProc(HWND,UINT,WPARAM,LPARAM);

//точка входа в Windows программуWINAPI WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{int ret=DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),

NULL,(DLGPROC)DlgProc);return ret;

}

//Глобальные переменные//Длина буфера#define BufSize 32char Buf[BufSize];//Дескрипторы полей редактированияHWND hEdit1,hEdit2,hEdit3,hEdit4,hEdit5;//Дескриптор поля режимов движенияHWND hStatic;//Дескрипторы кнопокHWND hButParam,hButStart,hButClose;//Переменная угла наклонной плоскости, αdouble angle;//Коэффициент трения скольжения, fdouble fric;//Коэффициент трения качения, δdouble fric_s;//Радиус цилиндра, rdouble radius;//Длина наклонной плоскости, Ldouble length;//Масштабный коэффициент, sdouble scale;//Координаты центра цилиндра и маркераdouble xCyl,yCyl,xMark,yMark;//Угол поворота цилиндра, ϕdouble fi;

//Оконная процедура для обработки сообщенийBOOL CALLBACK DlgProc(HWND hwnd,UINT msg,

WPARAM wParam,LPARAM lParam){

//msg - идентификатор обрабатываемого сообщения

— 4 —

Page 67: Win API

switch(msg){

//Инициация диалогового окнаcase WM_INITDIALOG:

{//Определяем дескрипторы окон редактирования://hEdit1 - угол наклонной плоскости, в градусах;hEdit1=GetDlgItem(hwnd,IDC_EDIT1);//hEdit2 - коэффициент трения скольжения, число;hEdit2=GetDlgItem(hwnd,IDC_EDIT2);//hEdit3 - коэффициент трения качения, в сантиметрах;hEdit3=GetDlgItem(hwnd,IDC_EDIT3);//hEdit4 - радиус цилиндра, в сантиметрах.hEdit4=GetDlgItem(hwnd,IDC_EDIT4);//hEdit5 - длина наклонной плоскости, в сантиметрахhEdit5=GetDlgItem(hwnd,IDC_EDIT5);//Дескриптор поля режимов движенияhStatic=GetDlgItem(hwnd,IDC_STATIC6);//Загрузка численных значений по умолчаниюSetWindowText(hEdit1,"30");SetWindowText(hEdit2,"0.1");SetWindowText(hEdit3,"0.01");SetWindowText(hEdit4,"2.5");SetWindowText(hEdit5,"20");//Определяем дескрипторы кнопочных оконhButParam=GetDlgItem(hwnd,IDC_Param);hButStart=GetDlgItem(hwnd,IDC_St);hButClose=GetDlgItem(hwnd,IDC_Close);//Отключаем клавишиEnableWindow(hButStart,FALSE);return FALSE;

}//Обработка сообщений от кнопок и//полей ввода численных значенийcase WM_COMMAND:

{switch (LOWORD(wParam)){

case IDC_EDIT1:GetWindowText(hEdit1,Buf,BufSize);//Функция atof() преобразует строку в//значение типа doubleangle=atof(Buf);

return TRUE;case IDC_EDIT2:

GetWindowText(hEdit2,Buf,BufSize);fric=atof(Buf);

return TRUE;case IDC_EDIT3:

GetWindowText(hEdit3,Buf,BufSize);fric_s=atof(Buf);

return TRUE;case IDC_EDIT4:

GetWindowText(hEdit4,Buf,BufSize);radius=atof(Buf);

return TRUE;

— 5 —

Page 68: Win API

case IDC_EDIT5:GetWindowText(hEdit5,Buf,BufSize);length=atof(Buf);

return TRUE;//Определение значений параметров модели и//проверка вхождения выбранных значений в//допустимые диапазоныcase IDC_Param://Подтверждаем выбор параметров моделиGetWindowText(hEdit1,Buf,BufSize);angle=atof(Buf);GetWindowText(hEdit2,Buf,BufSize);fric=atof(Buf);GetWindowText(hEdit3,Buf,BufSize);fric_s=atof(Buf);GetWindowText(hEdit4,Buf,BufSize);radius=atof(Buf);GetWindowText(hEdit5,Buf,BufSize);length=atof(Buf);if ((angle<0) || (angle>90)){

MessageBox(hwnd,"Угол накл. плоскости\nвне диапазона [0,90]!","",MB_OK);

return TRUE;}if (fric<0){MessageBox(hwnd,

"Коэффициент трения скольжения\nположительная величина!","",MB_OK);return TRUE;

}if (fric_s<0){MessageBox(hwnd,

"Коэффициент трения качения\nположительная величина!","",MB_OK);return TRUE;

}if (radius<0){MessageBox(hwnd,

"Радиус цилиндра\nположительная величина!","",MB_OK);return TRUE;

}//Длина наклонной плоскости//превышает длину окружности цилиндраif (radius>(length/(2*3.14159))){

MessageBox(hwnd,"Радиус цилиндра слишком велик!","",MB_OK);return TRUE;

}//Длина наклонной плоскости меньше//10 длин окружности цилиндраif (radius<(length/(20*3.14159))){

MessageBox(hwnd, "Радиус цилиндра слишком мал!","",MB_OK);return TRUE;

— 6 —

Page 69: Win API

}if (length<0){MessageBox(hwnd,

"Длина наклонной плоскости\nположительная величина!","",MB_OK);return TRUE;

}//Запрещаем ввод в полях редактирования

EnableWindow(hEdit1,FALSE);EnableWindow(hEdit2,FALSE);EnableWindow(hEdit3,FALSE);EnableWindow(hEdit4,FALSE);EnableWindow(hEdit5,FALSE);

//Блокируем кнопку Параметры и активируем кнопку StartEnableWindow(hButParam,FALSE);EnableWindow(hButStart,TRUE);return TRUE;

//Моделируем движение цилиндраcase IDC_St:

//Определяем габариты окна//рисования цилиндра и плоскостиint dx,dy,Lx,Ly;dx=20; dy=40; Lx=300, Ly=230;HDC hDC;HRGN Cyl,Marker;LOGBRUSH lb;HBRUSH hBrCyl,hBrMark,hBrWh;hDC=GetDC(hwnd);

//Рисуем рамку для наклонной плоскости и цилиндраRectangle(hDC,dx,dy,dx+Lx,dy+Ly);

//Масштабируем радиус и длину (перенормировка)angle=3.14159*(angle/180);scale=(((radius*(1-sin(angle))+length*cos(angle))>

(radius*(1+cos(angle))+length*sin(angle)))?(radius*(1-sin(angle))+length*cos(angle)):(radius*(1+cos(angle))+length*sin(angle)))/((Lx<Ly)?Lx:Ly);

radius=radius/scale;length=length/scale;//Рисуем наклонную плоскость//Устанавливаем позицию рисования в точку AMoveToEx(hDC,(int)(dx+radius*(1-sin(angle))),(int)(dy+radius*(1+cos(angle))+length*sin(angle)),NULL);//Соединяем точку A и BLineTo(hDC,(int)(dx+radius*(1-sin(angle))),

(int)(dy+radius*(1+cos(angle))));//Соединяем точку B и D

LineTo(hDC,(int)(dx+radius*(1-sin(angle))+length*cos(angle)),(int)(dy+radius*(1+cos(angle))+length*sin(angle)));

//Соединяем точку D и ALineTo(hDC,(int)(dx+radius*(1-sin(angle))),

(int)(dy+radius*(1+cos(angle))+length*sin(angle)));//Выбираем кисть белого цвета

lb.lbStyle = BS_SOLID;lb.lbColor = RGB(255,255,255);hBrWh = CreateBrushIndirect( &lb );

— 7 —

Page 70: Win API

//Рисуем цилиндр в виде кругаxCyl=dx+radius; yCyl=dy+radius;Cyl=CreateEllipticRgn((int)(xCyl-radius),(int)(yCyl-radius),

(int)(xCyl+radius),(int)(yCyl+radius));//Выбираем цвет цилиндра - красныйlb.lbColor = RGB(255,0,0);hBrCyl = CreateBrushIndirect( &lb );FillRgn(hDC,Cyl,hBrCyl);//Рисуем маркер M, изображающий вращение цилиндраMarker=CreateEllipticRgn(

(int)(dx+radius+0.5*radius*sin(angle)-4),(int)(dy+radius-0.5*radius*cos(angle)-4),(int)(dx+radius+0.5*radius*sin(angle)+4),(int)(dy+radius-0.5*radius*cos(angle)+4));

//Выбираем цвет маркера - черныйlb.lbColor = RGB(0,0,0);hBrMark = CreateBrushIndirect( &lb );FillRgn(hDC,Marker,hBrMark);

///////////////Первый тип движения - покой///////////////Условия того, что цилиндр покоится://angle<min(arctg(fric),arctg(fric_s/radius))if (angle<=((atan(fric)<atan(fric_s/radius))?atan(fric):

atan(fric_s/radius))){

SetWindowText(hStatic,"Цилиндр покоится");}

//////////Второй тип движения - скольжение///////////////Условия того, что цилиндр скользит://atan(fric)<angle<arctg(fric_s/radius)if ((atan(fric)<=atan(fric_s/radius))&&

(angle>atan(fric))&&(angle<=atan(fric_s/radius))){

SetWindowText(hStatic,"Цилиндр скользит");for (int i=1;i<=150;i++){//Организация паузы длиной 150 миллисекунд//для анимации динамики движения цилиндраint t=GetTickCount();while ((GetTickCount()-t)<150){};xCyl=xCyl+0.01*sin(angle-atan(fric))*cos(angle)*i*i;yCyl=yCyl+0.01*sin(angle-atan(fric))*sin(angle)*i*i;if(((xCyl+radius)>(dx+Lx))||

((yCyl+radius)>(dy+radius*(1+cos(angle))+length*sin(angle)))) break;

FillRgn(hDC,Cyl,hBrWh);DeleteObject(Cyl);

Cyl=CreateEllipticRgn((int)(xCyl-radius),(int)(yCyl-radius),(int)(xCyl+radius),(int)(yCyl+radius));

FillRgn(hDC,Cyl,hBrCyl);Marker=CreateEllipticRgn( (int)(xCyl+0.5*radius*sin(angle)-4),

(int)(yCyl-0.5*radius*cos(angle)-4),(int)(xCyl+0.5*radius*sin(angle)+4),(int)(yCyl-0.5*radius*cos(angle)+4));FillRgn(hDC,Marker,hBrMark);}

}

— 8 —

Page 71: Win API

/////////////Третий тип движения - качение////////////////////Условие того, что цилиндр катится без скольжения:

//atan(fric_s/radius)<angle<atan(fric)if ((atan(fric_s/radius)<atan(fric))&&(angle>atan(fric_s/radius))&&(angle<atan(fric))){SetWindowText(hStatic,"Цилиндр катится");for (int i=1;i<150;i++){//Организация паузы длиной 150 миллисекунд//для анимации динамики движения цилиндраint t=GetTickCount();while ((GetTickCount()-t)<150){};fi=0.01*sin(angle-atan(fric_s/radius))*i*i;xCyl=xCyl+2*3.14159*fi*cos(angle);yCyl=yCyl+2*3.14159*fi*sin(angle);if(((xCyl+radius)>(dx+Lx))||

((yCyl+radius)>(dy+radius*(1+cos(angle))+length*sin(angle)))) break;

FillRgn(hDC,Cyl,hBrWh);DeleteObject(Cyl);

Cyl=CreateEllipticRgn((int)(xCyl-radius),(int)(yCyl-radius), (int)(xCyl+radius),(int)(yCyl+radius));

FillRgn(hDC,Cyl,hBrCyl);Marker=CreateEllipticRgn(

(int)(xCyl+0.5*radius*sin(angle+fi)-4),(int)(yCyl-0.5*radius*cos(angle+fi)-4),(int)(xCyl+0.5*radius*sin(angle+fi)+4),(int)(yCyl-0.5*radius*cos(angle+fi)+4));FillRgn(hDC,Marker,hBrMark);

}}

//////////Четвертый тип движения – скольжение+качение////////////Условие того, что цилиндр катится и скользит://angle>max(atan(fric),atan(fric_s))if (angle>=((atan(fric)>atan(fric_s/radius))?

atan(fric):atan(fric_s/radius))){

SetWindowText(hStatic,"Цилиндр скользит и катится");for (int i=1;i<150;i++){//Организация паузы длиной 150 миллисекунд//для анимации динамики движения цилиндра

int t=GetTickCount();while ((GetTickCount()-t)<150){};

fi=0.01*(4/radius)*(sin(angle-atan(fric_s/radius))/cos(atan(fric_s/radius)))*i*i;

xCyl=xCyl+0.01*(sin(angle-atan(fric))/cos(atan(fric)))*cos(angle)*i*i;

yCyl=yCyl+0.01*(sin(angle-atan(fric))/cos(atan(fric)))*sin(angle)*i*i;

if(((xCyl+radius)>(dx+Lx))||((yCyl+radius)>(dy+radius*(1+cos(angle))+

length*sin(angle)))) break;FillRgn(hDC,Cyl,hBrWh);DeleteObject(Cyl);

— 9 —

Page 72: Win API

Cyl=CreateEllipticRgn((int)(xCyl-radius),(int)(yCyl-radius),(int)(xCyl+radius),(int)(yCyl+radius));

FillRgn(hDC,Cyl,hBrCyl);Marker=CreateEllipticRgn(

(int)(xCyl+0.5*radius*sin(angle+fi)-4),(int)(yCyl-0.5*radius*cos(angle+fi)-4),(int)(xCyl+0.5*radius*sin(angle+fi)+4),(int)(yCyl-0.5*radius*cos(angle+fi)+4));FillRgn(hDC,Marker,hBrMark);}

}/////////////////////////////////////////////////////////////DeleteObject(Cyl);DeleteObject(Marker);DeleteObject(hBrCyl);DeleteObject(hBrMark);DeleteObject(hBrWh);ReleaseDC(hwnd,hDC);//Активируем поля редактирования для//выбора значений параметров моделиEnableWindow(hEdit1,TRUE);EnableWindow(hEdit2,TRUE);EnableWindow(hEdit3,TRUE);EnableWindow(hEdit4,TRUE);EnableWindow(hEdit5,TRUE);//Блокируем кнопку Параметры и активируем кнопку StartEnableWindow(hButParam,TRUE);EnableWindow(hButStart,FALSE);return TRUE;}

}case IDC_Close:

{EndDialog(hwnd,0);return TRUE;

}//Закрытие диалогового окнаcase WM_CLOSE:

{EndDialog(hwnd,0);return TRUE;

}}return FALSE;

}

Следующий, завершающий этап построения курсовой работы состоит в создании пустого проекта InclPlane в среде Microsoft Visual C++ и присоединения к нему файлов InclPlane.cpp, resource.h и Script.rc. Файл resource.h является заголовочным и загружается в папку Header Files проекта, файл Script.rc является ресурсным и загружается в папку Resource Files проекта. После компиляции проекта и запуска его на исполнение, появится модальное диалоговое окно, представленное на рис.3.

— 10 —

Page 73: Win API

Рис.3. Исходный вид диалогового окно моделирования движенияцилиндра по наклонной плоскости

На начальном этапе доступны пять полей редактирования и две кнопки. В полях редактирования выбираются численные значения пяти параметров модели: угол наклонной плоскости, α; коэффициент трения скольжения, f; коэффициент трения качения, δ; радиус цилиндра, r; длина наклонной плоскости, L. По умолчанию выбираются те значения параметров проекта, которые представлены на рис.3.

Рис.4. Цилиндр в процессе скатывания с наклонной плоскости

Угол наклонной плоскости измеряется в градусах, коэффициент трения качения, радиус цилиндра и длина наклонной плоскости — в сантиметрах. После выбора значений параметров, нажимаем кнопку Параметры и далее кнопку Start, цилиндр начинает катиться согласно рис.4. По завершении скатывания кнопка Start деактивируется, а кнопка Параметры становится вновь доступной и цикл экспериментов можно повторить вновь.

Наконец, на рис.5 приведены в совокупности все четыре режима

— 11 —

Page 74: Win API

движения цилиндра.

а) б)

в) г)Рис.5. Режимы движения цилиндра: а) цилиндр покоится; б) цилиндр скользит;

в) цилиндр катится; г) цилиндр скользит и катится

— 12 —