Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
//Глобальные переменные
int timeout=60;//60 замеров по 2с, всего 2 мин
int data//Переменная для получения измерительных данных
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT){
SetTimer(hwnd,1,2000,NULL)//Таймер №1, период 2с
}
void OnTimer (HWND hwnd, UINT){
... //Формирование очередного данного в переменной data
InvalidateRect(hwnd, NULL, TRUE);//Инициирование WM_PAINT
timeout--;//Отсчитываем число замеров
if(!timeout) //Если timeout = 0
KillTimer(hwnd,1)//уничтожим таймер №1
}
void OnPaint(HWND hwnd){
... //Вывод в главное окно данного data
}
Мультимедийные таймеры
Основным недостатком обычных таймеров Windows является невысокая предельная частота (18,2 Гц) и, как следствие этого, низкое временное разрешение (55 мс). Если воспользоваться таким таймером для плавного перемещения изображения какого-либо объекта по экрану, то при разрешении экрана 800´600 пикселов объект затратит на прохождение всего экрана по горизонтали более 40 с, т. е. движение его будет чрезвычайно медленным. Для получения более высоких скоростей перемещения и для отсчета интервалов времени с более высокой точностью можно использовать мультимедийные таймеры, предельное разрешение которых составляет 1 мс, что соответствует частоте 1 кГц.
Для работы с мультимедийными таймерами предусмотрена небольшая группа специальных функций, имена которых начинаются со слова time (начинающегося со строчной буквы, что нетипично для имен функций Windows).
Рассмотрим несколько вариантов установки и использования мультимедийного таймера.
Измерение интервалов времени
В процессе оптимизации программ может возникнуть необходимость измерить время выполнения того или иного фрагмента программы. Для этого можно использовать мультимедийную функцию timeGetTime(), которая возвращает время в миллисекундах, истекшее от последней загрузки операционной системы:
DWORD t1,t2,t3;//Переменные для записи времени
t1=timeGetTime();
...//Контролируемый фрагмент программы
t2=timeGetTime();
t3=t2-t1;//Время выполнения контролируемого фрагмента в мс
Организация периодического процесса
Установка и использование мультимедийного таймера требует выполнения целого ряда действий. Прежде всего с помощью функции timeBeginPeriod() задается требуемое временное разрешение устанавливаемого таймера в миллисекундах. Хотя минимальное значение параметра этой функции составляет 1 мс, однако следует иметь в виду, что установленный таймер активно использует ресурсы операционной системы и при малом значении времени разрешения или при установке нескольких таймеров системе может не хватить ресурсов, что приведет к ее аварийной остановке.
Следующим этапом является установка временного события, которая выполняется с помощью функции timeSetEvent(). В качестве параметров этой функции указывается, в частности, временной интервал срабатывания таймера, а также адрес той прикладной функции обратного вызова, которая будет активизироваться при каждом его срабатывании.
Уничтожение мультимедийного таймера требует вызова двух функций: timeEndPeriod(), отменяющей установленное ранее разрешение таймера, и timeKillEvent(), которая прекращает действие всех системных процессов, связанных с работой мультимедийного таймера.
Фрагмент программы, в которой устанавливается мультимедийный таймер, может выглядеть таким образом:
timeBeginPeriod(1);//Установим максимальное разрешение
MMRESULT mmr=timeSetEvent(5,1,TimeProc,0,TIME_PERIODIC);
...//Продолжение программы
В качестве параметров функции timeSetEvent() указывается период его срабатывания (5 мс в примере), значение установленного ранее разрешения (1 мс), имя прикладной функции обработки прерываний от таймера (например, TimeProc), произвольное данное пользователя, которое будет передано в эту функцию (у нас 0), а также символическая константа, задающая режим работы таймера. Функция установки таймера возвращает (в переменную типа MMRESULT) его номер, назначаемый системой и используемый затем нами при уничтожении данного таймера.
Прикладная функция TimeProc(), вызываемая в данном примере каждые 5 мс, предназначена для выполнения требуемых периодических действий. Сложность, однако, заключается в том, что в этой функции запрещен вызов каких-либо функций Windows, кроме мультимедийных, а также функции PostQuitMessage(). В результате типичный текст функции TimeProc() выглядит следующим образом:
void CALLBACK TimeProc(UINT, UINT, DWORD, DWORD, DWORD){
PostMessage(hwnd, WM_USER,(WPARAM)parm1,(LPARAM)parm2);
}
Вызов функции PostMessage() приводит к посылке в окно hwnd нашего приложения сообщения пользователя с кодом WM_USER, в состав которого входят два произвольных параметра parm1 и parm2. Для Windows код WM_USER (он равен 0x400), при использовании его в рамках окон прикладных классов, ничего не значит, так как стандартные сообщения Windows имеют коды от 0 до WM_USER–1, однако мы можем обрабатывать сообщение WM_USER в нашей оконной функции наравне с остальными (системными) сообщениями:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
... //Обработка других сообщений Windows
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
case WM_USER:
OnUser(wParam, lParam);//Вызов прикладной функции
//обработки сообщений от мультимедийного таймера
}
}
На выполнение прикладной функции OnUser(), которой через ее аргументы wParam и lParam передаются (при необходимости) наши параметры parm1 и parm2, уже не накладывается никаких ограничений; в ней можно выполнять любые действия и, в частности, вызывать любые функции Windows. Следует только иметь в виду, что функция OnUser() не вызывается непосредственно прерыванием от таймера; сообщение WM_USER поступает в очередь сообщений приложения, а функция OnUser() будет вызвана, лишь когда дойдет очередь до обработки этого сообщения. Описанный здесь механизм иногда называют отсроченной или отложенной обработкой.
После того, как необходимость в периодических действиях отпала, мультимедийный таймер необходимо уничтожить:
timeEndPeriod(1);//Указывается заданное ранее разрешение
timeKillEvent(mmr);//Указывается номер данного таймера
Задание однократного интервала времени
При необходимости отработки однократного интервала времени необходимо выполнить все описанные выше действия, только в качестве режима установки временного события указывается константа TIME_ONESHOT:
timeBeginPeriod(1);//Установим максимальное разрешение
mmr=timeSetEvent(1000,1,TimeProc,0,TIME_ONESHOT);
...//Продолжение программы
void CALLBACK TimeProc(UINT, UINT, DWORD, DWORD, DWORD){
PostMessage(hwnd, WM_USER,(WPARAM)parm1,(LPARAM)parm2);
time EndPeriod(1);//Отмена установленного ранее разрешения
..timeKillEvent(mmr);//В предположении, что mmr – глобальная
} // переменная
В данном варианте сообщение WM_USER посылается лишь один раз по истечении точно 1с (с погрешностью в 1 мс). В прикладной функции TimeProc обработки этого сообщения, помимо активизации содержательных действий (посредством посылки сообщения WM_USER), необходимо отменить установленное ранее временное разрешение, как это и показано в приведенных выше строках.
4. Дочерние окна
Создание и использование дочерних окон
Любое реальное приложение Windows содержит большое количество окон. Хотя все графические элементы, составляющие экранный кадр приложения (текстовые строки, геометрические фигуры, растровые изображения) можно вывести непосредственно в главное окно, такой способ формирования экранного кадра обычно оказывается неудобным, главным образом из-за невозможности раздельного взаимодействия (с помощью мыши или клавиатуры) с этими элементами. Организация же системы вложенных, или порожденных, окон позволяет для каждого такого окна или группы окон иметь, например, свою форму курсора или свой цвет фона; главное же достоинство порожденных окон заключается в возможности придания каждому окну индивидуальной оконной функции. Это позволяет отдельно и по-разному обрабатывать для каждого окна поступающие в него сообщения Windows (или, наоборот, посылать в окна программно сформированные сообщения).
Так, на рис. 4.1 приведен для примера возможный вид главного окна программы, управляющей некоторой измерительной установкой. В окне имеется поле для ввода экспозиции, поясняющая надпись “Введите Экспозицию:”, кнопка пуска, а также большое дочернее окно для динамического вывода значения времени, оставшегося до конца сеанса измерения.

Рис. 4.1. Дочернее окно в главном окне приложения
Реально почти все изображения, которые мы видим на экране, – разнообразные кнопки, полосы прокрутки, ползунки, поля для ввода текста, изображения различных приборов и индикаторов – представляют собой окна с теми или иными характеристиками. Обычно эти окна образуют иерархическую систему. Например, любой диалог прежде всего представляет собой окно; однако в нем обычно имеются еще и внутренние окна – кнопки, списки, полосы прокрутки и т. д.
Модальные диалоги, широко используемые в приложениях Windows в качестве средства управления работой программы, относятся к числу всплывающих окон, что определяется указанием при описании их стиля константы WS_POPUP. Модальные диалоговые окна могут быть только всплывающими. Вообще же вложенные окна, в частности немодальные диалоги, могут быть как всплывающими, так и дочерними; в последнем случае в описании их стиля присутствует константа WS_CHILD. Всплывающие и дочерние окна характеризуются следующими различиями:
· если в главном окне приложения одновременно образованы и всплывающие, и дочерние окна, то всплывающие окна будут изображаться поверх дочерних, “всплывать” над ними, что и определило их название;
· дочерние окна могут перемещаться только в пределах родительского окна, в то время как положение всплывающих окон не ограничено какими-либо границами; всплывающее окно можно вытащить за пределы главного окна приложения и поместить в любом месте экрана (для чего необходимо, чтобы у всплывающего окна, кроме рамки, была еще и полоса заголовка);
· координаты дочерних окон задаются относительно границ рабочей области родительского окна; координаты всплывающих окон задаются относительно границ экрана;
· всплывающее окно может содержать собственное меню; в дочерних окнах меню не бывает.
Указанные различия делают всплывающие окна более удобными для организации активного диалога с пользователем, в процессе которого он работает с органами управления окном, вводит входные данные в соответствующие поля, выбирает требуемые режимы и т. д. Дочерние окна часто используются как информационные, а также для придания отдельным областям главного окна специфических свойств. Выделив, например, в тексте справки некоторые ключевые слова в отдельные окна, можно с классом этих окон связать курсор характерной формы (обычную стрелку заменить на изображение ладони), задать в контексте устройства другой цвет символов и вдобавок определить функцию обработки сообщений WM_LBUTTONDOWN, чтобы при щелчке мышью по выделенному слову вызывался поясняющий это слово текст.
Диалоговые окна относятся к окнам предопределенных классов, описанных в Windows. Это ограничивает их возможности, но упрощает взаимодействие с элементами управления, входящими в состав диалоговых окон, так как в Windows для этого предусмотрены стандартные средства.
В тех случаях, когда вложенное окно должно обеспечивать более широкие возможности, целесообразно создать в программе не диалоговое, а обычное порожденное окно и наделить его требуемыми характеристиками. При этом, если форма диалоговых окон описывается в файле ресурсов, а создание осуществляется функциями DialogBox() или CreateDialog(), то порожденные (как дочерние, так и всплывающие) окна описываются непосредственно в программе, а создаются универсальными функциями CreateWindow() или CreateWindowEx(). Для определенности мы далее будем говорить о дочерних окнах, хотя фактически весь этот материал в равной степени относится и к окнам всплывающим.
Процедура создания и обслуживания дочернего, как и любого другого, окна состоит из трех этапов:
· регистрации класса дочернего окна, в процессе которой за окном закрепляется его оконная функция;
· создания окна функцией Windows CreateWindow() с указанием стиля окна и его местоположения;
· обработки сообщений, поступающих в дочернее окно.
Классы всех дочерних окон удобно регистрировать в функции WinMain() вслед за регистрацией класса главного окна; создаются же дочерние окна обычно в функции OnCreate(), вызываемой Windows в процессе создания главного окна.
Сообщения, поступающие в главное окно, обрабатываются, как известно, оконной функцией главного окна; для обработки сообщений, поступающих в дочерние окна, должна быть предусмотрена отдельная оконная функция. Если при этом дочерних окон несколько, и все они обладают существенно разными характеристиками, для них предусматривают отдельные классы и, соответственно, отдельные оконные функции. Если же дочерние окна более однородны, все они могут принадлежать одному классу, и тогда оконная функция для всех дочерних окон будет одна, а различать отдельные окна придется по их дескрипторам.
На рис. 4.2 приведен вид главного окна приложения, назначение которого – вывод на экран информации из базы данных пациента.

Рис. 4.2. Главное окно приложения с несколькими дочерними окнами
В главном окне образовано одно большое дочернее окно (с рамкой и полосой заголовка) для вывода данных в графической форме и пять “управляющих” окон меньшего размера с названиями отображаемых данных. Управляющие окна не имеют ни заголовка, ни рамки, и поэтому никак не выделяются на общем фоне главного окна. Их размеры соответствуют размерам выводимых в них строк текста. Все они принадлежат одному классу, для которого задана специфическая форма курсора – изображение кисти руки. В единую оконную функцию этих окон включена обработка сообщения WM_LBUTTONDOWN. При щелчке мышью по той или иной строке с названием данного в большое дочернее окно выводится соответствующая информация в виде графика.
Таким образом, в данном приложении зарегистрированы классы трех окон – главного окна, большого окна для вывода графика и пяти управляющих окон с названиями данных. Соответственно, в приложении имеются и три оконных функции. Легко сообразить, что оконная функция главного окна почти пуста – в ней может обрабатываться одно единственное сообщение WM_DESTROY о завершении приложения. Оконная функция окна графика содержит обработку сообщений WM_PAINT, а окна с названиями данных, как уже отмечалось, должны обрабатывать сообщения WM_LBUTTONDOWN.
Приведем теперь в качестве образца слегка сокращенный текст простой программы, в которой, кроме главного окна, предусмотрено еще одно дочернее окно для вывода графика (рис. 4.3).

Рис. 4.3. Главное окно приложения с одним дочерним окном
#include <windows. h>
#include <windowsx. h>
/*Глобальные переменные*/
HINSTANCE hI;
char szClassName[]="MainWindow";
char szDataClassName[]="DataWindow";
char szTitle[]="Дочернее окно в главном";
/*Главная функция приложения*/
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int){
hI=hInst;
MSG msg;
WNDCLASS wc;
ZeroMemory(&wc, sizeof(wc));
/*Регистрируем класс главного окна*/
wc. lpszClassName=szClassName;
wc. hInstance=hInst;
wc. lpfnWndProc=WndProc;
wc. hCursor=LoadCursor(NULL, IDC_ARROW);
wc. hIcon=LoadIcon(NULL, IDI_APPLICATION);
wc. hbrBackground=GetStockBrush(WHITE_BRUSH);
RegisterClass(&wc);
/*Регистрируем класс дочернего окна*/
wc. lpszClassName=szDataClassName;//Другой класс
wc. lpfnWndProc=DataWndProc;//Другая оконная функция
wc. hbrBackground=GetStockBrush(LTGRAY_BRUSH);//Другой цвет
RegisterClass(&wc);//Регистрируем
/*Создаем главное окно обычным образом*/
HWND hwnd = CreateWindow(szClassName, szTitle,
WS_OVERLAPPEDWINDOW,10,10,228,280,HWND_DESKTOP,
NULL, hInst, NULL);
ShowWindow(hwnd, SW_SHOWNORMAL);//Делаем главное окно видимым
while(GetMessage(&msg, NULL,0,0))//Цикл обработки сообщений
DispatchMessage(&msg);
return 0;
}
/*Оконная процедура главного окна*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
}
/*Функция обработки сообщения WM_CREATE главного окна*/
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT){
CreateWindow(szDataClassName, NULL,//Создадим дочернее окно
WS_CHILD | WS_DLGFRAME | //Окно дочернее, с толстой рамкой,
WS_VISIBLE,//видимое (не нужна функция ShowWindow())
10,10,200,200,//Положение дочернего окна в главном
hwnd, NULL, hI, NULL);
return TRUE;
}
/*Функция обработки сообщения WM_PAINT главного окна*/
void OnPaint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
/*Выведем поясняющий текст*/
char str[]="График Декартова листа";
TextOut(hdc,20,220,str, strlen(str));
EndPaint(hwnd,&ps);
}
/*Оконная процедура дочернего окна */
LRESULT CALLBACK DataWndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam){
switch(msg){
HANDLE_MSG(hwnd, WM_PAINT, DataOnPaint);
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
}
/*Функция обработки сообщения WM_PAINT дочернего окна*/
void DataOnPaint(HWND hwnd){
PAINTSTRUCT ps;
float x, y,t;
int a=-5;
HDC hdc=BeginPaint(hwnd,&ps);
/*Построим график функции*/
for(t=-50;t<50;t+=0.01){
x=a*t/(1+t*t*t)*30;
y=a*t*t/(1+t*t*t)*30;
Rectangle(hdc, x+100,y+100,x+102,y+102);
}
EndPaint(hwnd,&ps);
}
Как видно из текста программы, оконная функция дочернего окна и объявляется, и выглядит так же, как и для главного окна. В ней удобно использовать макрос HANDLE_MSG; единственным обрабатываемым здесь сообщением является WM_PAINT (поскольку дочернее окно мы отдельно закрывать не намерены, в оконной функции отсутствует обработка сообщения WM_DESTROY). Разумеется, при необходимости можно было обрабатывать и другие сообщения для этого окна.
Главное окно мы обычно сначала создаем функцией CreateWindow(), а затем делаем видимым вызовом функции ShowWindow(). Дочернее окно можно сразу вывести на экран, указав в его стиле, среди прочих, константу WS_VISIBLE. В этом случае отпадает необходимость в вызове для него функции ShowWindow() (впрочем, таким же приемом можно было воспользоваться и при создании главного окна). Константа WS_DLGFRAME делает дочернее окно выпуклым, что характерно для диалоговых окон. При использовании, например, константы WS_BORDER мы получили бы менее красивое “плоское” дочернее окно в тонкой черной рамке.
Окна предопределенных классов в главном окне
До сих пор мы сталкивались с такими формами приложений Windows:
· приложение с главным окном;
· приложение без главного окна на основе модального диалога;
· приложение с главным окном и вложенным диалоговым окном (модальным или немодальным);
· приложение с главным окном и вложенными дочерними окнами.
В действительности в рамках главного окна приложения можно иметь не только вложенные дочерние или всплывающие окна, созданные в программе, но и любые окна предопределенных в Windows классов – кнопки, списки, статические элементы с текстом и др. Таким образом, рассмотренные нами ранее элементы управления необязательно должны быть элементами диалога; с таким же успехом их можно использовать в составе главного окна приложения или любых вложенных окон. На рис. 4.1 были показаны два окна такого рода – окно для ввода текста, в данном случае числа, задающего время экспозиции, и обычная кнопка “Пуск”.
Для создания окон предопределенных классов так же, как и любых других, используется функция CreateWindow(). Так, окна, показанные на рис. 4.1, созданы следующим образом:
HWND hwndEdit=CreateWindow("EDIT",NULL,
WS_CHILD|WS_VISIBLE|WS_BORDER|ES_NUMBER,
162,10,45,20, hwnd, NULL, hInst, NULL);
HWND hwndBtn= CreateWindow("BUTTON","Пуск",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
162,40,45,35, (HMENU)ID_GO, NULL, hInst, NULL);
В качестве класса окна указывается один из предопределенных классов (“EDIT”, “BUTTON”, “LISTBOX” и т. д.). В составную константу, определяющую стиль окна, могут входить как константы общего назначения (WS_CHILD, WS_VISIBLE, WS_BORDER), так и характерные только для окон данного класса. Так, константа ES_NUMBER разрешает ввод в окно редактирования только десятичных цифр, а константа BS_PUSHBUTTON определяет внешний вид и функциональное назначение кнопки (“нажимаемая” кнопка).
Обычно элементам управления (кнопкам, спискам и проч.) присваивают идентификаторы – произвольные символические константы. Идентификаторы удобны тем, что при поступлении в окно приложения сообщения WM_COMMAND, возникшего в результате воздействия пользователя на тот или иной элемент управления, среди параметров этого сообщения передается и идентификатор активизированного элемента. Выполняя с помощью конструкции switch-case анализ этого параметра, программа определяет, на какой конкретно элемент управления воздействовал пользователь. Если, например, в диалоговом окне имеются две кнопки с идентификаторами BTN_1 и BTN_2, то функция OnCommand() будет выглядеть так:
void OnCommand(HWND hwnd, int id, HWND, UINT){
switch(id){
case BTN_1:
...//Действия при нажатии на кнопку с идентификатором BTN_1
break;
case BTN_2:
...//Действия при нажатии на кнопку с идентификатором BTN_2
break;
...//И т. д.
Если элементы управления входят в состав диалогового окна, то идентификаторы назначаются им в предложениях файла ресурсов:
CONTROL "Пуск", BTN_1, "BUTTON", BS_PUSHBUTTON, 27,32,43,12
Если, однако, элемент управления создается непосредственно в программе, как дочернее окно предопределенного класса, идентификатор должен быть назначен в процессе его создания. В этом случае идентификатор указывается в качестве восьмого по счету параметра функции CreateWindow() (см. приведенное выше предложение создания кнопки, которой назначается идентификатор ID_GO). Однако согласно прототипу функции CreateWindow() восьмой параметр должен иметь типа HMENU. Поэтому при подстановке в качестве этого параметра символического обозначения идентификатора его следует явным образом преобразовать в тип HMENU, что и сделано в приведенном выше фрагменте.
5. Вывод растровых изображений
Важнейшим изобразительным средством графической операционной системы являются цветные изображения разнообразных объектов. Рисунки используются для повышения наглядности курсоров, кнопок, пиктограмм, заставок и других элементов интерфейса; служат иллюстративным материалом для интерактивных учебников и справочников; входят органической частью в состав приложений, предназначенных для управления производственными процессами или измерительными установками.
Поскольку светящаяся поверхность видеомонитора представляет собой набор точек (точечный растр), любое изображение в конечном счете является точечным. Поэтому приобретают особую важность принципы создания, хранения и вывода на экран точечных, или растровых, изображений. В простейшем случае, для монохромной видеосистемы, каждая точка экрана описывается одним битом посылаемых на экран данных: если бит установлен, точка светится, если сброшен – погашена. Для современных цветных видеосистем это уже не так, каждая точка описывается обычно тремя байтами, характеризующими ее цвет, однако старое название растровых изображений – битовые матрицы или, иногда, битовые карты (от англ. bitmap) осталось в обиходе.
Процедура вывода растрового изображения
Имеется несколько возможностей хранения исходных растровых изображений:
· в файлах с расширением. BMP, создаваемых обычно с помощью того или иного графического редактора, например пакета CorelDraw от Corel Corporation, встроенной в Windows программы Paint или одного из универсальных редакторов ресурсов, входящих в пакеты сред программирования Borland C++, Visual C++ и др.; файл можно также получить путем сканирования фотографии с переводом результата в формат. BMP;
· в файле ресурсов в виде таблиц чисел, которые можно получить с помощью того же редактора ресурсов;
· непосредственно в полях данных приложения в виде массивов целых чисел.
Обычно используется первый вариант. Если исходное изображение хранится в файле. BMP, то загрузить его в память можно двумя способами. Первый требует использования файла ресурсов, в котором должно быть предложение, описывающее файл с изображением:
pict BITMAP "apple. bmp"
На имя этого ресурса (pict в данном примере) программа будет ссылаться при загрузке изображения в память. Как известно, при компиляции проекта, в который входит файл ресурсов, фактически выполняются две операции компиляции: исходного текста программы с образованием объектного файла с расширением. OBJ, и файла ресурсов с образованием второго промежуточного файла с расширением. RES. Этот файл содержит изображение практически в том же виде, что и исходный файл. BMP. Далее файлы. OBJ и. RES обрабатываются компоновщиком, который объединяет файлы. OBJ и. RES и создает загрузочный файл. EXE, готовый к выполнению. Ресурсы (не только изображения, но и описание диалогов, меню, текстовых строк и проч.) сохраняются в файле. EXE в специальном формате, понятном редактору ресурсов. Это дает возможность редактировать ресурсы в файле. EXE, не имея исходного текста программы.
Для того чтобы изображение можно было вывести на экран, оно должно находиться не в файле, а в памяти. Загрузка изображения-ресурса из файла .EXE в память осуществляется в программе вызовом функции LoadImage(). После этого программа может приступить к процедуре вывода изображения в окно. Отметим, что в этом случае для выполнения программы файл. BMP уже не нужен; изображение хранится в загрузочном файле, который в силу этого может иметь значительный размер.
Другой способ загрузки изображения в память не требует наличия файла ресурсов; загрузка изображения осуществляется той же функцией LoadImage()непосредственно из файла. BMP. Однако в этом случае приложение может выполняться, только если ему доступны все необходимые файлы с рисунками, поскольку в загрузочном файле рисунков нет. Легко сообразить, что именно такая методика используется в программах, позволяющих просматривать рисунки, хранящиеся на дисках. Оба описанных способа схематически изображены на рис. 5.1.

Рис. 5.1. Загрузка растрового изображения в память:
а – с помощью файла ресурсов, б –непосредственно из файла. BMP
Область памяти, куда загружается изображение, должна иметь особую организацию, совместимую с представлением на экране окна приложения. Такая память называется совместимой (compatible), и функция LoadImage(), загружая изображение из файла, одновременно создает область совместимой памяти соответствующего изображению размера. После этого нам остается только скопировать изображение из совместимой памяти в окно приложения, для чего предусмотрена функция BitBlt() (от Bit block transfer, блочная передача битов). Эта функция отличается высокой эффективностью, обеспечивая быстрый вывод на экран даже больших изображений. Как правило, копирование изображения из совместимой памяти в окно осуществляется в ответ на сообщение WM_PAINT, поскольку только в этом случае обеспечивается правильное восстановление содержимого окна после его затирания.
Как известно, при выводе в окно приложения любых графических образов (строк текста, геометрических фигур, отдельных точек) необходимо использовать контекст устройства (фактически – контекст окна). В контексте хранятся дескрипторы всех используемых графических инструментов (перьев, кистей и шрифтов). Для работы с совместимой памятью для нее также необходимо создать контекст устройства, который в этом случае называется совместимым контекстом. В совместимый контекст должен быть загружен дескриптор совместимой памяти, т. е. фактически дескриптор выводимого в окно растрового изображения.
Таким образом, для вывода на экран (точнее, в окно приложения) растрового изображения в памяти должны иметься три объекта:
· совместимая память, в которой находится изображение;
· обычный контекст устройства, посредством которого мы обращаемся к окну и выводим в него те или иные графические элементы;
· совместимый контекст, связанный с совместимой памятью, посредством которого мы обращаемся к этой памяти и забираем из нее находящееся в ней изображение.
Все эти объекты схематически изображены на рис. 5.2 вместе с функциями Windows, служащими для создания этих объектов или работы с ними.
Как уже отмечалось, для загрузки изображения в память предусмотрена функция LoadImage(), которая используется в двух вариантах. Для загрузки изображения из выполнимого файла. EXE (т. е. посредством файла ресурсов) вызов функции выглядит следующим образом:
HBITMAP hBitmap = (HBITMAP)LoagImage
(hInstance, "pict", IMAGE_BITMAP, 0,0,0);
Здесь hInstance – дескриптор экземпляра приложения, передаваемый в программы через первый параметр функции WinMain(), а pict – имя нашего ресурса в файле ресурсов. Последний параметр функции предназначен для указания флагов загрузки, которые в данном случае не нужны, а два параметра перед ним позволяют задать размеры создаваемой совместимой памяти. При нулевых значениях этих двух параметров выделяется столько памяти, сколько требуется для хранения изображения.

Рис. 5.2. Процедура вывода растрового изображения
Функция LoadImage() позволяет загружать не только растровые изображения, но также курсоры и пиктограммы. Поскольку дескрипторы этих графических объектов принадлежат к разным типам данных (HBITMAP, HCURSOR и HICON), возвращаемое функцией значение следует преобразовать в тип загружаемого объекта. В нашем случае это тип HBITMAP.
Во втором варианте, когда файл ресурсов отсутствует, и изображение загружается непосредственно из файла. BMP, функция LoadImage() используется в следующем формате:
HBITMAP hBitmap = (HBITMAP)LoagImage
(NULL,"apple. bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
Здесь вместо дескриптора экземпляра приложения указывается NULL, а вместо имени ресурса – имя самого файла с изображением. Кроме того, необходимо указать флаг LR_LOADFROMFILE, определяющий загрузку из файла. Как и ранее, возвращаемое значение следует явно преобразовать в тип HBITMAP.
Для работы с совместимой памятью необходимо иметь совместимый контекст устройства. Он создается с помощью функции CreateCompatibleDC():
HDC hdcMem = CreateCompatibleDC(hdc):
Обычно совместимый контекст создается в функции OnCreate(); необходимый для этого контекст окна hdc в этом случае может быть получен с помощью функции GetDC().
Совместимый контекст схож с обычным контекстом окна в том отношении, что в нем хранится та же информация о текущих графических инструментах (дескрипторы пера, кисти и шрифта, цвет шрифта и т. д.). Однако кроме этого в совместимом контексте имеется дескриптор растра. При создании совместимого контекста в него по умолчанию помещается дескриптор изображения, представляющего собой один единственный черный пиксел. Выбрав в совместимый контекст дескриптор нашего растра (совместимой памяти), мы получим возможность оперировать с этой памятью, в частности, копировать ее в окно приложения.
Выбор дескриптора совместимой памяти в совместимый контекст осуществляется вызовом макроса SelectBitmap() (или обобщенной функции SelectObject()):
SelectBitmap(hdcMem, hBitmap);
Собственно вывод изображения в окно приложения осуществляется, как уже отмечалось, функцией BiBlt(), вызов которой в общем виде можно записать следующим образом:
BitBlt(hdc, x,y, w,h, hdcMem, x0,y0,ROP);
Параметры hdc и hdcMem представляют собой дескрипторы контекстов приемника и источника копируемого изображения. В данном случае мы копируем из совместимой памяти (дескриптор hdcMem) в окно приложения (дескриптор hdc).
Параметры x и y задают положение копии в окне-приемнике (от начала рабочей области окна), а параметры w и h – размер копируемой части изображения. Если w или h меньше размеров изображения, в окно скопируется только его часть. Параметры x0 и y0 определяют координаты начальной точки копирования в изображении-источнике. Наиболее естественно положить x0=y0=0, чтобы скопировать изображение целиком от самого его начала, но можно и вырезать из исходного изображения любую прямоугольную область.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


