помогите исправить и доработать код. Построить функцию y=|sin(3x)|.
#include <windows.h> /* Содержит декларации для всех функций в Windows API, все общие макросы и все типы данных. Он определяет очень большое количество специфических функций окна */
#include <iostream> /* Содержит классы, функции и переменные для организации ввода-вывода */
#include <cmath> /* Выполняет простые математические операции */
#include <commdlg.h> /* Поддерживает реализацию общих диалоговых окон (для GetSaveFileName) */
#include <shlobj.h> /* Поддерживает работу с путём каталога к папке или файлу (для SHGetFolderPath) */
Листинг 2
// Начало функции WinMain:
/* Спецификатор WINAPI обозначает тип вызова функции; hInstance - дескриптор экземпляра выполняющейся программы (идентификатор приложения);
hPrevInstance - дескриптор предыдущего экземпляра программы, если таковой имеется, в Win32 не имеет смысла;
lpCmdLine - это указатель на строку, содержащую командную строку, запустившую программу;
nCmdShow - определяет внешний вид окна при его создании. */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
}
Листинг 3
WNDCLASS wc = {0}; // Создание объекта класса окна и обнуление полей
// Инициализация членов структуры окна
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // Задание стиля окна
wc.lpfnWndProc = WindowProc; // Задание функции обработки сообщений
wc.hInstance = hInstance; // Задание дескриптора экземпляра приложения
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Задание курсора окна
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Задание пиктограммы приложения
wc.lpszMenuName = L"hMenu"; // Инициализация указателя на имя ресурса меню
wc.lpszClassName= L"CMyWnd"; // Инициализация указателя на имя класса (установка имени класса окна)
wc.hbrBackground= static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH)); // Задание кисти цвета фона окна
RegisterClass (&wc); // Регистрация класса окна в операционной системе
Листинг 4
/* Создание экземпляра класса окна при помощи функции CreateWindow и инициализация дескриптора hWnd возвращаемым функцией значением.
Параметры функции:
L"CMyWnd" - имя класса окна; L"WinMain sample" - заголовок окна; WS_OVERLAPPEDWINDOW - стиль окна (перекрываемое);
CW_USEDEFAULT - позиция левого верхнего угла на экране по умолчанию по X;
0 - позиция левого верхнего угла на экране по умолчанию по Y;
320 - размер окна (ширина); 240 - размер окна (высота); NULL - нет родительского окна; NULL - нет меню;
hInstance - дескриптор экземпляра программы; NULL - никаких данных для создания окна: */
HWND hWnd = CreateWindow (L"CMyWnd", L"WinMain sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 320, 240, NULL, NULL, hInstance, NULL);
Листинг 5
// Создание Меню
czCreateMenu(hWnd);
Листинг 6
/* Функция получает дескриптор контекста устройства для клиентской области указанного в параметрах окна (задает область для рисования): */
dc = GetDC (hWnd);
/* Функция отображает окно, причем в соответствии с параметром nCmdShow: */
ShowWindow (hWnd, nCmdShow);
Листинг 7
/* Функция SetTimer создает таймер с указанным значением времени простоя, когда время истечет, передается сообщение */
SetTimer (hWnd, 1, USER_TIMER_MINIMUM, NULL);
Листинг 8
/* Структура сообщений Windows. Т.е. создание объекта типа MSG (создаем экземпляр структуры), который будет содержать информацию о сообщении из очереди потока сообщений: */
MSG msg;
/* Организация цикла сообщений (message loop), или так называемой подкачки сообщений (message pump).
Функция GetMessage возвращает TRUE (1), если получено любое сообщение кроме WM_QUIT для завершения программы (в этом случае возвращается FALSE (0)).
При возникновении ошибки возвращается значение -1.
Цикл будет выполняться бесконечно, пока не появится сообщение завершения программы или не возникнет ошибка: */
while (GetMessage(&msg,NULL,0,0) > 0)
{
TranslateMessage (&msg); /* Преобразует сообщения от нажатой клавиатуры в символьный вид */
DispatchMessage (&msg); /* Функция заставляет ОС Windows осуществить диспетчеризацию сообщения, т.е. вызвать функцию WindowProc() для обработки сообщения */
}
return msg.wParam; // Возврат в операционную систему
Листинг 9
// Идентификатор контекста отображения (дескриптор контекста устройства вывода, в данном случае дисплея):
HDC dc;
// Логическая переменная для проверки было ли уже произведено сохранение:
static BOOL bFirstSave = TRUE;
Листинг 10
// Прототипы используемых функций
LRESULT CALLBACK WindowProc (HWND, UINT, WPARAM, LPARAM); /* Обрабатывает события окна */
void czCreateMenu(HWND hWwnd); /* Создает меню главного окна */
void SaveScreen(HWND hWND); /* Создает диалог для сохранения изображения (получает путь) */
bool save (HWND hWnd, OPENFILENAME openFile); /* Непосредственно выполняет сохранение растрового изображения */
Листинг 11
// Функция создания меню:
void czCreateMenu(HWND hwnd)
{
HMENU MainMenu = CreateMenu(); // Функция создает меню (пока пустое)
HMENU hPopupMenu1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2_1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu3 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu1, L"&File"); // Функция добавляет "выпадающее" меню "Файл" в главную строку меню
{
AppendMenu(hPopupMenu1, MF_STRING | MF_CHECKED, 101, L"New"); // Функция добавляет пункт "Новый" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 102, L"Save"); // Функция добавляет пункт "Сохранить" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 103, L"Save as..."); // Функция добавляет пункт "Сохранить как..." в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 104, L"Exit"); // Функция добавляет пункт "Выход" в "выпадающее" меню "Файл"
}
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu2, L"&View"); // Функция добавляет "выпадающее" меню "Вид" в главную строку меню
{
AppendMenu(hPopupMenu2, MF_STRING | MF_POPUP, (UINT)hPopupMenu2_1, L"&Color"); // Функция добавляет "выпадающее" меню "Цвет" в "выпадающее" меню "Вид"
{
AppendMenu(hPopupMenu2_1, MF_STRING, 201, L"Black"); // Функция добавляет пункт "Черный" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 202, L"Blue"); // Функция добавляет пункт "Синий" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 203, L"Green"); // Функция добавляет пункт "Зеленый" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 204, L"Red"); // Функция добавляет пункт "Красный" в "выпадающее" меню "Цвет"
}
}
AppendMenu(MainMenu, MF_STRING, 300, L"&Reset"); // Функция добавляет пункт "Сброс" в главную строку меню
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu3, L"&Help"); // Функция добавляет "выпадающее" меню "Справка" в главную строку меню
{
AppendMenu(hPopupMenu3, MF_STRING, 401, L"&About"); // Функция добавляет пункт "О программе" в "выпадающее" меню "Справка"
}
SetMenu(hwnd, MainMenu); // Функция назначает новое меню для заданного окна
}
Листинг 12
#define WIN32_LEAN_AND_MEAN /* Ускоряет сборку за счет уменьшения размера заголовочных файлов, исключая наименее распространенные функции */
// Создание идентификаторов для событий выбора пунктов меню:
#define IDM_NEW 101 // Выбрано меню "New"
#define IDM_SAVE_AS 103 // Выбрано меню "Save as"
#define IDM_EXIT 104 // Выбрано меню "Exit"
#define IDM_BLACK 201 // Выбрано меню "Black"
#define IDM_BLUE 202 // Выбрано меню "Blue"
#define IDM_GREEN 203 // Выбрано меню "Green"
#define IDM_RED 204 // Выбрано меню "Red"
#define IDM_RESET 300 // Выбрано меню "Reset"
#define IDM_ABOUT 401 // Выбрано меню "About"
Листинг 13
// Функция обработки сообщений - Message processing function
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; // Создание переменных для хранения параметров сообщения
static bool Move = true; // Логическая глобальная переменная, которая показывает должен ли перерисовываться график с изменением положения
static int Phase=0, Width, Height; // Переменные для аргумента периодической функции, длины и ширины области построения графика
HPEN hPen; // Объявляем кисть
switch (message) // Использование переключателя для обработки различных событий окна
{
case WM_LBUTTONDOWN: // Сообщение - нажата левая кнопка мыши
case WM_RBUTTONDOWN: // Сообщение - нажата правая кнопка мыши
Move = !Move; // Включить/выключить перемещение графика
// Нет break, т.е. следующая ветка выполняется
case WM_TIMER: // Сообщение - истекло время таймера
if (Move) // Если Move==TRUE (если движение разрешено), то выполняется следующий оператор
Phase++; // Увеличение аргумента (фазы) функции на единицу
// Нет break, т.е. следующая ветка выполняется, если Move==TRUE
else // Если Move!=TRUE (если движение запрещено), то выполняется следующий оператор
break; // Выполнение переключателя прерывается
case WM_PAINT: // Сообщение – требуется окрасить (перерисовать) часть окна приложения
Rectangle (dc, -1, -1, Width+1, Height+1); // Прорисовка прямоугольника в клиентской области
MoveToEx (dc, 0, Height * (0.5 + 0.3*sin(0.1*Phase)), NULL); // Функция устанавливает текущую позицию пера, равную (x,y) для указанного контекста отображения dc
for (int i=0; i<Width; i++) // Цикл для рисования графика (изменение по оси X)
LineTo (dc, i, Height * (0.5 + 0.3*sin(0.1*(i+Phase))) ); // Функция рисует линию из текущей позиции пера в точку с координатами, перечисленными в параметрах функции
break; // Выполнение переключателя прерывается
case WM_SIZE: // Сообщение – изменился размер окна
Width = LOWORD(lParam), Height = HIWORD(lParam); // Изменение значений ширины и высоты окна в соответствии с параметрами сообщения
break; // Выполнение переключателя прерывается
case WM_KEYDOWN: // Сообщение - нажата несистемная клавиша (без + Alt). Посылается окну с фокусом клавиатуры
if (wParam != VK_ESCAPE) // Проверка – нажата ли клавиша «Esc»
break; // Выполнение переключателя прерывается
// else no break – в противном случае прерывания переключателя нет, т.е. переход к завершению работы окна
case WM_DESTROY: // Сообщение – окно должно быть разрушено (завершить работу приложения). После этого сообщения окно удаляется с экрана
PostQuitMessage (0); // Функция направляет сообщение WM_QUIT в функцию WinMain (обычно в ответ на сообщение WM_DESTROY). Указывает системе, что процесс, сделавший запрос необходимо прекратить
break; // Выполнение переключателя прерывается
case WM_COMMAND: // Сообщение – выбран пункт меню или элемент управления (кнопка); нажата «горячая» клавиша
break; // Выполнение переключателя прерывается
}
return DefWindowProc (hWnd, message, wParam, lParam); // Смотри комментарии выше
}
Листинг 14
wmId = LOWORD(wParam); // Выделение из 32-битового целочисленного значения младшего слова и запись в переменную
wmEvent = HIWORD(wParam); // Выделение из 32-битового целочисленного значения старшего слова и запись в переменную
// Новый переключатель для определения какой пункт меня выбран (кнопок нет, поэтому подобные события отсутствуют):
switch (wmId) // Параметром переключателя служит код-идентификатор, указанный при создании пункта меню
{
case IDM_SAVE_AS: // Сообщение - выбран пункт меню «Сохранить как…»
SaveScreen (hWnd); // Вызов функции сохранения изображения клиентской области
break; // Выполнение переключателя прерывается
case IDM_EXIT: // Сообщение - выбран пункт меню «Выход»
DestroyWindow(hWnd); // Функция уничтожает окно. Она посылает сообщения WM_DESTROY и WM_NCDESTROY окну, чтобы разрушить его и удалить фокус клавиатуры из него. Функция также уничтожает меню, очищает очередь сообщений, разрушает таймеры и т.д.
break; // Выполнение переключателя прерывается
case IDM_BLACK: // Сообщение - выбран пункт меню «Черный»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_BLUE: // Сообщение - выбран пункт меню «Синий»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,255)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_GREEN: // Сообщение - выбран пункт меню «Зеленый»
hPen=CreatePen(PS_SOLID, 1, RGB(0,255,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_RED: // Сообщение - выбран пункт меню «Красный»
hPen=CreatePen(PS_SOLID, 1, RGB(255,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_NEW: // Сообщение - выбран пункт меню «Новый»
case IDM_RESET: // Сообщение - выбран пункт меню «Сброс»
Phase=0; // Сброс значения аргумента периодической функции в нуль
break; // Выполнение переключателя прерывается
case IDM_about : // Сообщение - выбран пункт меню «О программе»
MessageBox(hWnd,L"PlotDrawer v.1.0. Copyright © M. Gorozheev Ph.D., 2015",L"About MessageBox", MB_OK | MB_ICONWARNING); /* Функция отображает модальное диалоговое окно, которое содержит набор кнопок и краткое сообщение конкретных приложений (в данном случае – информацию о программе). Окно возвращает целое значение, указывающее, какая кнопка нажата пользователем */
break; // Выполнение переключателя прерывается
default: // Эта «ветка» выполняется, если нет ни одного совпадения сообщения с метками case
return DefWindowProc(hWnd, message, wParam, lParam); /* Функция обеспечивает стандартную обработку сообщений, которые явно не обрабатываются в функции WindowProc данного приложения. Эта функция гарантирует, что каждое сообщение обрабатывается. Она вызывается с теми же параметрами, что были получены функцией WindowProc */
}
Листинг 15
// Функция создает диалог для сохранения изображения (получает путь)
void SaveScreen(HWND hWnd)
{
OPENFILENAME openFile; // Создание структуры. В нее записывается информация о выборе пользователя после закрытия диалогового окна
TCHAR szPath[MAX_PATH]; // Символьный массив для хранения пути к файлу
TCHAR szFile[MAX_PATH]; // Символьный массив для хранения имени файла
// Инициализация структуры OPENFILENAME:
ZeroMemory( &openFile, sizeof(OPENFILENAME) ); // Функция ZeroMemory заполняет блок памяти нулями
openFile.lStructSize = sizeof(OPENFILENAME); // lStructSize определяет длину структуры (байт); инициализируем оператором sizeof
szFile[0] = '
// Функция, непосредственно сохраняющая изображение клиентской области окна в файл
bool save (HWND hWnd, OPENFILENAME openFile)
{
// Создание структуры с атрибутами файла:
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Получить дескриптор контекста устройства дисплея для клиентской области окна:
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Создание совместимого DC, который используется в BitBlt из окна DC:
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC) // Если создать DC не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить клиентскую область для расчета размера:
RECT rcClient; // Создание прямоугольника для хранения координат рабочей области окна
GetClientRect(hWnd, &rcClient); // Получение координат рабочей области окна
// Создать совместимый bitmap (точечный рисунок) из окна DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
if(!hbmScreen) // Если создать bitmap не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Выбор совместимого bitmap (растрового изображения) в памяти совместимого DC:
SelectObject(hdcMemDC,hbmScreen);
/* Копирование блока битов в память совместимого DC. Функция BitBlt используется для копирования отдельных битов из области источника изображения в область-получатель. Если копирование не удалось, выводится модальное окно с сообщением об ошибке: */
if(!BitBlt(hdcMemDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcWindow, 0, 0, SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить BITMAP (точечный рисунок) из HBITMAP:
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Начиная с 32-битной Windows, GlobalAlloc и LocalAlloc реализованы как «функции-обертки», которые вызывают HeapAlloc,
// используя дескриптор, ссылающийся на кучу, выделяемую процессу по умолчанию. Таким образом, GlobalAlloc и LocalAlloc
// имеют большую нагрузку, чем HeapAlloc:
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Получаем "биты" из растрового изображения и копируем их в буфер, на который указывает lpbitmap:
GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
// Создается файл. Именно здесь происходит сохранение захваченного изображения клиентской области:
HANDLE hFile = CreateFile(openFile.lpstrFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Добавление размера заголовков к размеру растрового изображения, чтобы получить общий размер файла:
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Смещение, определяющее, где фактически начинаются биты растрового изображения:
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Размер файла:
bmfHeader.bfSize = dwSizeofDIB;
// Компонент bfType всегда должен равняться BM для растровых изображений:
bmfHeader.bfType = 0x4D42; //Тип BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Разблокировка и освободить DIB (Device Independent Bitmap, растровое изображение) из кучи (памяти):
GlobalUnlock(hDIB);
GlobalFree(hDIB);
CloseHandle(hFile); // Закрыть дескриптор для файла, который был создан
// После метки производится освобождение памяти (удаление объектов) и выход из функции:
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(hWnd,hdcWindow);
return TRUE;
}
кнопочки "вставить форматированный код" и "прикрепить файл" вам видны ?
Код:
Листинг программного кода
Листинг 1
// Подключение используемых заголовочных файлов:
#include <windows.h> /* Содержит декларации для всех функций в Windows API, все общие макросы и все типы данных. Он определяет очень большое количество специфических функций окна */
#include <iostream> /* Содержит классы, функции и переменные для организации ввода-вывода */
#include <cmath> /* Выполняет простые математические операции */
#include <commdlg.h> /* Поддерживает реализацию общих диалоговых окон (для GetSaveFileName) */
#include <shlobj.h> /* Поддерживает работу с путём каталога к папке или файлу (для SHGetFolderPath) */
Листинг 2
// Начало функции WinMain:
/* Спецификатор WINAPI обозначает тип вызова функции; hInstance - дескриптор экземпляра выполняющейся программы (идентификатор приложения);
hPrevInstance - дескриптор предыдущего экземпляра программы, если таковой имеется, в Win32 не имеет смысла;
lpCmdLine - это указатель на строку, содержащую командную строку, запустившую программу;
nCmdShow - определяет внешний вид окна при его создании. */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
}
Листинг 3
WNDCLASS wc = {0}; // Создание объекта класса окна и обнуление полей
// Инициализация членов структуры окна
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // Задание стиля окна
wc.lpfnWndProc = WindowProc; // Задание функции обработки сообщений
wc.hInstance = hInstance; // Задание дескриптора экземпляра приложения
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Задание курсора окна
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Задание пиктограммы приложения
wc.lpszMenuName = L"hMenu"; // Инициализация указателя на имя ресурса меню
wc.lpszClassName= L"CMyWnd"; // Инициализация указателя на имя класса (установка имени класса окна)
wc.hbrBackground= static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH)); // Задание кисти цвета фона окна
RegisterClass (&wc); // Регистрация класса окна в операционной системе
Листинг 4
/* Создание экземпляра класса окна при помощи функции CreateWindow и инициализация дескриптора hWnd возвращаемым функцией значением.
Параметры функции:
L"CMyWnd" - имя класса окна; L"WinMain sample" - заголовок окна; WS_OVERLAPPEDWINDOW - стиль окна (перекрываемое);
CW_USEDEFAULT - позиция левого верхнего угла на экране по умолчанию по X;
0 - позиция левого верхнего угла на экране по умолчанию по Y;
320 - размер окна (ширина); 240 - размер окна (высота); NULL - нет родительского окна; NULL - нет меню;
hInstance - дескриптор экземпляра программы; NULL - никаких данных для создания окна: */
HWND hWnd = CreateWindow (L"CMyWnd", L"WinMain sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 320, 240, NULL, NULL, hInstance, NULL);
Листинг 5
// Создание Меню
czCreateMenu(hWnd);
Листинг 6
/* Функция получает дескриптор контекста устройства для клиентской области указанного в параметрах окна (задает область для рисования): */
dc = GetDC (hWnd);
/* Функция отображает окно, причем в соответствии с параметром nCmdShow: */
ShowWindow (hWnd, nCmdShow);
Листинг 7
/* Функция SetTimer создает таймер с указанным значением времени простоя, когда время истечет, передается сообщение */
SetTimer (hWnd, 1, USER_TIMER_MINIMUM, NULL);
Листинг 8
/* Структура сообщений Windows. Т.е. создание объекта типа MSG (создаем экземпляр структуры), который будет содержать информацию о сообщении из очереди потока сообщений: */
MSG msg;
/* Организация цикла сообщений (message loop), или так называемой подкачки сообщений (message pump).
Функция GetMessage возвращает TRUE (1), если получено любое сообщение кроме WM_QUIT для завершения программы (в этом случае возвращается FALSE (0)).
При возникновении ошибки возвращается значение -1.
Цикл будет выполняться бесконечно, пока не появится сообщение завершения программы или не возникнет ошибка: */
while (GetMessage(&msg,NULL,0,0) > 0)
{
TranslateMessage (&msg); /* Преобразует сообщения от нажатой клавиатуры в символьный вид */
DispatchMessage (&msg); /* Функция заставляет ОС Windows осуществить диспетчеризацию сообщения, т.е. вызвать функцию WindowProc() для обработки сообщения */
}
return msg.wParam; // Возврат в операционную систему
Листинг 9
// Идентификатор контекста отображения (дескриптор контекста устройства вывода, в данном случае дисплея):
HDC dc;
// Логическая переменная для проверки было ли уже произведено сохранение:
static BOOL bFirstSave = TRUE;
Листинг 10
// Прототипы используемых функций
LRESULT CALLBACK WindowProc (HWND, UINT, WPARAM, LPARAM); /* Обрабатывает события окна */
void czCreateMenu(HWND hWwnd); /* Создает меню главного окна */
void SaveScreen(HWND hWND); /* Создает диалог для сохранения изображения (получает путь) */
bool save (HWND hWnd, OPENFILENAME openFile); /* Непосредственно выполняет сохранение растрового изображения */
Листинг 11
// Функция создания меню:
void czCreateMenu(HWND hwnd)
{
HMENU MainMenu = CreateMenu(); // Функция создает меню (пока пустое)
HMENU hPopupMenu1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2_1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu3 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu1, L"&File"); // Функция добавляет "выпадающее" меню "Файл" в главную строку меню
{
AppendMenu(hPopupMenu1, MF_STRING | MF_CHECKED, 101, L"New"); // Функция добавляет пункт "Новый" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 102, L"Save"); // Функция добавляет пункт "Сохранить" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 103, L"Save as..."); // Функция добавляет пункт "Сохранить как..." в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 104, L"Exit"); // Функция добавляет пункт "Выход" в "выпадающее" меню "Файл"
}
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu2, L"&View"); // Функция добавляет "выпадающее" меню "Вид" в главную строку меню
{
AppendMenu(hPopupMenu2, MF_STRING | MF_POPUP, (UINT)hPopupMenu2_1, L"&Color"); // Функция добавляет "выпадающее" меню "Цвет" в "выпадающее" меню "Вид"
{
AppendMenu(hPopupMenu2_1, MF_STRING, 201, L"Black"); // Функция добавляет пункт "Черный" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 202, L"Blue"); // Функция добавляет пункт "Синий" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 203, L"Green"); // Функция добавляет пункт "Зеленый" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 204, L"Red"); // Функция добавляет пункт "Красный" в "выпадающее" меню "Цвет"
}
}
AppendMenu(MainMenu, MF_STRING, 300, L"&Reset"); // Функция добавляет пункт "Сброс" в главную строку меню
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu3, L"&Help"); // Функция добавляет "выпадающее" меню "Справка" в главную строку меню
{
AppendMenu(hPopupMenu3, MF_STRING, 401, L"&About"); // Функция добавляет пункт "О программе" в "выпадающее" меню "Справка"
}
SetMenu(hwnd, MainMenu); // Функция назначает новое меню для заданного окна
}
Листинг 12
#define WIN32_LEAN_AND_MEAN /* Ускоряет сборку за счет уменьшения размера заголовочных файлов, исключая наименее распространенные функции */
// Создание идентификаторов для событий выбора пунктов меню:
#define IDM_NEW 101 // Выбрано меню "New"
#define IDM_SAVE_AS 103 // Выбрано меню "Save as"
#define IDM_EXIT 104 // Выбрано меню "Exit"
#define IDM_BLACK 201 // Выбрано меню "Black"
#define IDM_BLUE 202 // Выбрано меню "Blue"
#define IDM_GREEN 203 // Выбрано меню "Green"
#define IDM_RED 204 // Выбрано меню "Red"
#define IDM_RESET 300 // Выбрано меню "Reset"
#define IDM_ABOUT 401 // Выбрано меню "About"
Листинг 13
// Функция обработки сообщений - Message processing function
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; // Создание переменных для хранения параметров сообщения
static bool Move = true; // Логическая глобальная переменная, которая показывает должен ли перерисовываться график с изменением положения
static int Phase=0, Width, Height; // Переменные для аргумента периодической функции, длины и ширины области построения графика
HPEN hPen; // Объявляем кисть
switch (message) // Использование переключателя для обработки различных событий окна
{
case WM_LBUTTONDOWN: // Сообщение - нажата левая кнопка мыши
case WM_RBUTTONDOWN: // Сообщение - нажата правая кнопка мыши
Move = !Move; // Включить/выключить перемещение графика
// Нет break, т.е. следующая ветка выполняется
case WM_TIMER: // Сообщение - истекло время таймера
if (Move) // Если Move==TRUE (если движение разрешено), то выполняется следующий оператор
Phase++; // Увеличение аргумента (фазы) функции на единицу
// Нет break, т.е. следующая ветка выполняется, если Move==TRUE
else // Если Move!=TRUE (если движение запрещено), то выполняется следующий оператор
break; // Выполнение переключателя прерывается
case WM_PAINT: // Сообщение – требуется окрасить (перерисовать) часть окна приложения
Rectangle (dc, -1, -1, Width+1, Height+1); // Прорисовка прямоугольника в клиентской области
MoveToEx (dc, 0, Height * (0.5 + 0.3*sin(0.1*Phase)), NULL); // Функция устанавливает текущую позицию пера, равную (x,y) для указанного контекста отображения dc
for (int i=0; i<Width; i++) // Цикл для рисования графика (изменение по оси X)
LineTo (dc, i, Height * (0.5 + 0.3*sin(0.1*(i+Phase))) ); // Функция рисует линию из текущей позиции пера в точку с координатами, перечисленными в параметрах функции
break; // Выполнение переключателя прерывается
case WM_SIZE: // Сообщение – изменился размер окна
Width = LOWORD(lParam), Height = HIWORD(lParam); // Изменение значений ширины и высоты окна в соответствии с параметрами сообщения
break; // Выполнение переключателя прерывается
case WM_KEYDOWN: // Сообщение - нажата несистемная клавиша (без + Alt). Посылается окну с фокусом клавиатуры
if (wParam != VK_ESCAPE) // Проверка – нажата ли клавиша «Esc»
break; // Выполнение переключателя прерывается
// else no break – в противном случае прерывания переключателя нет, т.е. переход к завершению работы окна
case WM_DESTROY: // Сообщение – окно должно быть разрушено (завершить работу приложения). После этого сообщения окно удаляется с экрана
PostQuitMessage (0); // Функция направляет сообщение WM_QUIT в функцию WinMain (обычно в ответ на сообщение WM_DESTROY). Указывает системе, что процесс, сделавший запрос необходимо прекратить
break; // Выполнение переключателя прерывается
case WM_COMMAND: // Сообщение – выбран пункт меню или элемент управления (кнопка); нажата «горячая» клавиша
break; // Выполнение переключателя прерывается
}
return DefWindowProc (hWnd, message, wParam, lParam); // Смотри комментарии выше
}
Листинг 14
wmId = LOWORD(wParam); // Выделение из 32-битового целочисленного значения младшего слова и запись в переменную
wmEvent = HIWORD(wParam); // Выделение из 32-битового целочисленного значения старшего слова и запись в переменную
// Новый переключатель для определения какой пункт меня выбран (кнопок нет, поэтому подобные события отсутствуют):
switch (wmId) // Параметром переключателя служит код-идентификатор, указанный при создании пункта меню
{
case IDM_SAVE_AS: // Сообщение - выбран пункт меню «Сохранить как…»
SaveScreen (hWnd); // Вызов функции сохранения изображения клиентской области
break; // Выполнение переключателя прерывается
case IDM_EXIT: // Сообщение - выбран пункт меню «Выход»
DestroyWindow(hWnd); // Функция уничтожает окно. Она посылает сообщения WM_DESTROY и WM_NCDESTROY окну, чтобы разрушить его и удалить фокус клавиатуры из него. Функция также уничтожает меню, очищает очередь сообщений, разрушает таймеры и т.д.
break; // Выполнение переключателя прерывается
case IDM_BLACK: // Сообщение - выбран пункт меню «Черный»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_BLUE: // Сообщение - выбран пункт меню «Синий»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,255)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_GREEN: // Сообщение - выбран пункт меню «Зеленый»
hPen=CreatePen(PS_SOLID, 1, RGB(0,255,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_RED: // Сообщение - выбран пункт меню «Красный»
hPen=CreatePen(PS_SOLID, 1, RGB(255,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_NEW: // Сообщение - выбран пункт меню «Новый»
case IDM_RESET: // Сообщение - выбран пункт меню «Сброс»
Phase=0; // Сброс значения аргумента периодической функции в нуль
break; // Выполнение переключателя прерывается
case IDM_ABOUT: // Сообщение - выбран пункт меню «О программе»
MessageBox(hWnd,L"PlotDrawer v.1.0. Copyright © M. Gorozheev Ph.D., 2015",L"About MessageBox", MB_OK | MB_ICONWARNING); /* Функция отображает модальное диалоговое окно, которое содержит набор кнопок и краткое сообщение конкретных приложений (в данном случае – информацию о программе). Окно возвращает целое значение, указывающее, какая кнопка нажата пользователем */
break; // Выполнение переключателя прерывается
default: // Эта «ветка» выполняется, если нет ни одного совпадения сообщения с метками case
return DefWindowProc(hWnd, message, wParam, lParam); /* Функция обеспечивает стандартную обработку сообщений, которые явно не обрабатываются в функции WindowProc данного приложения. Эта функция гарантирует, что каждое сообщение обрабатывается. Она вызывается с теми же параметрами, что были получены функцией WindowProc */
}
Листинг 15
// Функция создает диалог для сохранения изображения (получает путь)
void SaveScreen(HWND hWnd)
{
OPENFILENAME openFile; // Создание структуры. В нее записывается информация о выборе пользователя после закрытия диалогового окна
TCHAR szPath[MAX_PATH]; // Символьный массив для хранения пути к файлу
TCHAR szFile[MAX_PATH]; // Символьный массив для хранения имени файла
// Инициализация структуры OPENFILENAME:
ZeroMemory( &openFile, sizeof(OPENFILENAME) ); // Функция ZeroMemory заполняет блок памяти нулями
openFile.lStructSize = sizeof(OPENFILENAME); // lStructSize определяет длину структуры (байт); инициализируем оператором sizeof
szFile[0] = ''; // Делаем строку пустой, т.е. символ конца строки идет первым в массиве
openFile.hwndOwner = hWnd; // hwndOwner - дескриптор окна, которое владеет блоком диалога
openFile.lpstrFile = szFile; // Указатель на буфер с именем файла. Когда функция GetOpenFileName или GetSaveFileName возвращает значение, этот буфер содержит идентификатор диска, путь, имя и расширение выбранного файла
openFile.nMaxFile = sizeof(szFile)/sizeof(*szFile); // Размер буфера, в символах (TCHAR), указанного членом lpstrFile
openFile.lpstrFilter = L"Bitmap Files (*.bmp)*.bmp"; // Указатель на буфер, содержащий пары строк фильтров с нулевым символом в конце. Последняя строка в буфере должна быть завершена двумя символами ПУСТО (NULL)
openFile.lpstrDefExt = L"*.bmp"; // Указатель на буфер, который содержит заданное по умолчанию расширение
// При первом сохранении документа пользователем путь по умолчанию приходится на папку «Мои рисунки»:
if ( TRUE == bFirstSave ) // Если логическая переменная равняется истине, то использовать путь по умолчанию
{
/* Функция SHGetFolderPath возвращает полный путь к указанному специальному каталогу по его идентификатору. Макрос SUCCEEDED предусматривает унифицированную проверку на успешное завершение при любом значении состояния: */
if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_MYPICTURES, NULL, 0, szPath ) ) )
{
// Инициализация lpstrInitialDir значением (путем), который функция SHGetFolderPath записала в szPath.
// Из-за этого функция GetSaveFileName представляет папку «Мои рисунки» в качестве начального каталога для диалогового окна:
openFile.lpstrInitialDir = szPath;
}
}
// Отображается окно стандартного диалога сохранения файла с путем по умолчанию в каталог «Мои рисунки» или выбранное пользователем
// ранее местоположение. Функция GetSaveFileName создает диалоговое окно «Сохранить», а оператор if проверяет наличие ошибок:
if ( GetSaveFileName( &openFile ) == TRUE ) // Смотри выше
{
// Пользователь нажал кнопку «Сохранить».
// Происходит сохранение файла:
UpdateWindow (hWnd); // Перерисовка главного окна в связи с появлением диалогового (иначе изображение перекрывалось бы)
save(hWnd, openFile); // Вызов функции, которая непосредственно сохраняет изображение клиентской области окна в файл
bFirstSave = FALSE; // Логическая переменная «Первое_Сохранение» становится равной FALSE
}
else // Если не нажата кнопка «Сохранить»
{
// Пользователь прервал диалог сохранения файла, или возникла ошибка
}
}
Листинг 16
// Функция, непосредственно сохраняющая изображение клиентской области окна в файл
bool save (HWND hWnd, OPENFILENAME openFile)
{
// Создание структуры с атрибутами файла:
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Получить дескриптор контекста устройства дисплея для клиентской области окна:
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Создание совместимого DC, который используется в BitBlt из окна DC:
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC) // Если создать DC не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить клиентскую область для расчета размера:
RECT rcClient; // Создание прямоугольника для хранения координат рабочей области окна
GetClientRect(hWnd, &rcClient); // Получение координат рабочей области окна
// Создать совместимый bitmap (точечный рисунок) из окна DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
if(!hbmScreen) // Если создать bitmap не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Выбор совместимого bitmap (растрового изображения) в памяти совместимого DC:
SelectObject(hdcMemDC,hbmScreen);
/* Копирование блока битов в память совместимого DC. Функция BitBlt используется для копирования отдельных битов из области источника изображения в область-получатель. Если копирование не удалось, выводится модальное окно с сообщением об ошибке: */
if(!BitBlt(hdcMemDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcWindow, 0, 0, SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить BITMAP (точечный рисунок) из HBITMAP:
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Начиная с 32-битной Windows, GlobalAlloc и LocalAlloc реализованы как «функции-обертки», которые вызывают HeapAlloc,
// используя дескриптор, ссылающийся на кучу, выделяемую процессу по умолчанию. Таким образом, GlobalAlloc и LocalAlloc
// имеют большую нагрузку, чем HeapAlloc:
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Получаем "биты" из растрового изображения и копируем их в буфер, на который указывает lpbitmap:
GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
// Создается файл. Именно здесь происходит сохранение захваченного изображения клиентской области:
HANDLE hFile = CreateFile(openFile.lpstrFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Добавление размера заголовков к размеру растрового изображения, чтобы получить общий размер файла:
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Смещение, определяющее, где фактически начинаются биты растрового изображения:
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Размер файла:
bmfHeader.bfSize = dwSizeofDIB;
// Компонент bfType всегда должен равняться BM для растровых изображений:
bmfHeader.bfType = 0x4D42; //Тип BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Разблокировка и освободить DIB (Device Independent Bitmap, растровое изображение) из кучи (памяти):
GlobalUnlock(hDIB);
GlobalFree(hDIB);
CloseHandle(hFile); // Закрыть дескриптор для файла, который был создан
// После метки производится освобождение памяти (удаление объектов) и выход из функции:
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(hWnd,hdcWindow);
return TRUE;
}
Листинг 1
// Подключение используемых заголовочных файлов:
#include <windows.h> /* Содержит декларации для всех функций в Windows API, все общие макросы и все типы данных. Он определяет очень большое количество специфических функций окна */
#include <iostream> /* Содержит классы, функции и переменные для организации ввода-вывода */
#include <cmath> /* Выполняет простые математические операции */
#include <commdlg.h> /* Поддерживает реализацию общих диалоговых окон (для GetSaveFileName) */
#include <shlobj.h> /* Поддерживает работу с путём каталога к папке или файлу (для SHGetFolderPath) */
Листинг 2
// Начало функции WinMain:
/* Спецификатор WINAPI обозначает тип вызова функции; hInstance - дескриптор экземпляра выполняющейся программы (идентификатор приложения);
hPrevInstance - дескриптор предыдущего экземпляра программы, если таковой имеется, в Win32 не имеет смысла;
lpCmdLine - это указатель на строку, содержащую командную строку, запустившую программу;
nCmdShow - определяет внешний вид окна при его создании. */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
}
Листинг 3
WNDCLASS wc = {0}; // Создание объекта класса окна и обнуление полей
// Инициализация членов структуры окна
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // Задание стиля окна
wc.lpfnWndProc = WindowProc; // Задание функции обработки сообщений
wc.hInstance = hInstance; // Задание дескриптора экземпляра приложения
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Задание курсора окна
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Задание пиктограммы приложения
wc.lpszMenuName = L"hMenu"; // Инициализация указателя на имя ресурса меню
wc.lpszClassName= L"CMyWnd"; // Инициализация указателя на имя класса (установка имени класса окна)
wc.hbrBackground= static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH)); // Задание кисти цвета фона окна
RegisterClass (&wc); // Регистрация класса окна в операционной системе
Листинг 4
/* Создание экземпляра класса окна при помощи функции CreateWindow и инициализация дескриптора hWnd возвращаемым функцией значением.
Параметры функции:
L"CMyWnd" - имя класса окна; L"WinMain sample" - заголовок окна; WS_OVERLAPPEDWINDOW - стиль окна (перекрываемое);
CW_USEDEFAULT - позиция левого верхнего угла на экране по умолчанию по X;
0 - позиция левого верхнего угла на экране по умолчанию по Y;
320 - размер окна (ширина); 240 - размер окна (высота); NULL - нет родительского окна; NULL - нет меню;
hInstance - дескриптор экземпляра программы; NULL - никаких данных для создания окна: */
HWND hWnd = CreateWindow (L"CMyWnd", L"WinMain sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 320, 240, NULL, NULL, hInstance, NULL);
Листинг 5
// Создание Меню
czCreateMenu(hWnd);
Листинг 6
/* Функция получает дескриптор контекста устройства для клиентской области указанного в параметрах окна (задает область для рисования): */
dc = GetDC (hWnd);
/* Функция отображает окно, причем в соответствии с параметром nCmdShow: */
ShowWindow (hWnd, nCmdShow);
Листинг 7
/* Функция SetTimer создает таймер с указанным значением времени простоя, когда время истечет, передается сообщение */
SetTimer (hWnd, 1, USER_TIMER_MINIMUM, NULL);
Листинг 8
/* Структура сообщений Windows. Т.е. создание объекта типа MSG (создаем экземпляр структуры), который будет содержать информацию о сообщении из очереди потока сообщений: */
MSG msg;
/* Организация цикла сообщений (message loop), или так называемой подкачки сообщений (message pump).
Функция GetMessage возвращает TRUE (1), если получено любое сообщение кроме WM_QUIT для завершения программы (в этом случае возвращается FALSE (0)).
При возникновении ошибки возвращается значение -1.
Цикл будет выполняться бесконечно, пока не появится сообщение завершения программы или не возникнет ошибка: */
while (GetMessage(&msg,NULL,0,0) > 0)
{
TranslateMessage (&msg); /* Преобразует сообщения от нажатой клавиатуры в символьный вид */
DispatchMessage (&msg); /* Функция заставляет ОС Windows осуществить диспетчеризацию сообщения, т.е. вызвать функцию WindowProc() для обработки сообщения */
}
return msg.wParam; // Возврат в операционную систему
Листинг 9
// Идентификатор контекста отображения (дескриптор контекста устройства вывода, в данном случае дисплея):
HDC dc;
// Логическая переменная для проверки было ли уже произведено сохранение:
static BOOL bFirstSave = TRUE;
Листинг 10
// Прототипы используемых функций
LRESULT CALLBACK WindowProc (HWND, UINT, WPARAM, LPARAM); /* Обрабатывает события окна */
void czCreateMenu(HWND hWwnd); /* Создает меню главного окна */
void SaveScreen(HWND hWND); /* Создает диалог для сохранения изображения (получает путь) */
bool save (HWND hWnd, OPENFILENAME openFile); /* Непосредственно выполняет сохранение растрового изображения */
Листинг 11
// Функция создания меню:
void czCreateMenu(HWND hwnd)
{
HMENU MainMenu = CreateMenu(); // Функция создает меню (пока пустое)
HMENU hPopupMenu1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu2_1 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
HMENU hPopupMenu3 = CreatePopupMenu(); // Функция создает подменю - раскрывающееся меню (пока пустое)
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu1, L"&File"); // Функция добавляет "выпадающее" меню "Файл" в главную строку меню
{
AppendMenu(hPopupMenu1, MF_STRING | MF_CHECKED, 101, L"New"); // Функция добавляет пункт "Новый" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 102, L"Save"); // Функция добавляет пункт "Сохранить" в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 103, L"Save as..."); // Функция добавляет пункт "Сохранить как..." в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_SEPARATOR, 0, L""); // Функция добавляет разделитель в "выпадающее" меню "Файл"
AppendMenu(hPopupMenu1, MF_STRING, 104, L"Exit"); // Функция добавляет пункт "Выход" в "выпадающее" меню "Файл"
}
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu2, L"&View"); // Функция добавляет "выпадающее" меню "Вид" в главную строку меню
{
AppendMenu(hPopupMenu2, MF_STRING | MF_POPUP, (UINT)hPopupMenu2_1, L"&Color"); // Функция добавляет "выпадающее" меню "Цвет" в "выпадающее" меню "Вид"
{
AppendMenu(hPopupMenu2_1, MF_STRING, 201, L"Black"); // Функция добавляет пункт "Черный" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 202, L"Blue"); // Функция добавляет пункт "Синий" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 203, L"Green"); // Функция добавляет пункт "Зеленый" в "выпадающее" меню "Цвет"
AppendMenu(hPopupMenu2_1, MF_STRING, 204, L"Red"); // Функция добавляет пункт "Красный" в "выпадающее" меню "Цвет"
}
}
AppendMenu(MainMenu, MF_STRING, 300, L"&Reset"); // Функция добавляет пункт "Сброс" в главную строку меню
AppendMenu(MainMenu, MF_STRING | MF_POPUP, (UINT)hPopupMenu3, L"&Help"); // Функция добавляет "выпадающее" меню "Справка" в главную строку меню
{
AppendMenu(hPopupMenu3, MF_STRING, 401, L"&About"); // Функция добавляет пункт "О программе" в "выпадающее" меню "Справка"
}
SetMenu(hwnd, MainMenu); // Функция назначает новое меню для заданного окна
}
Листинг 12
#define WIN32_LEAN_AND_MEAN /* Ускоряет сборку за счет уменьшения размера заголовочных файлов, исключая наименее распространенные функции */
// Создание идентификаторов для событий выбора пунктов меню:
#define IDM_NEW 101 // Выбрано меню "New"
#define IDM_SAVE_AS 103 // Выбрано меню "Save as"
#define IDM_EXIT 104 // Выбрано меню "Exit"
#define IDM_BLACK 201 // Выбрано меню "Black"
#define IDM_BLUE 202 // Выбрано меню "Blue"
#define IDM_GREEN 203 // Выбрано меню "Green"
#define IDM_RED 204 // Выбрано меню "Red"
#define IDM_RESET 300 // Выбрано меню "Reset"
#define IDM_ABOUT 401 // Выбрано меню "About"
Листинг 13
// Функция обработки сообщений - Message processing function
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; // Создание переменных для хранения параметров сообщения
static bool Move = true; // Логическая глобальная переменная, которая показывает должен ли перерисовываться график с изменением положения
static int Phase=0, Width, Height; // Переменные для аргумента периодической функции, длины и ширины области построения графика
HPEN hPen; // Объявляем кисть
switch (message) // Использование переключателя для обработки различных событий окна
{
case WM_LBUTTONDOWN: // Сообщение - нажата левая кнопка мыши
case WM_RBUTTONDOWN: // Сообщение - нажата правая кнопка мыши
Move = !Move; // Включить/выключить перемещение графика
// Нет break, т.е. следующая ветка выполняется
case WM_TIMER: // Сообщение - истекло время таймера
if (Move) // Если Move==TRUE (если движение разрешено), то выполняется следующий оператор
Phase++; // Увеличение аргумента (фазы) функции на единицу
// Нет break, т.е. следующая ветка выполняется, если Move==TRUE
else // Если Move!=TRUE (если движение запрещено), то выполняется следующий оператор
break; // Выполнение переключателя прерывается
case WM_PAINT: // Сообщение – требуется окрасить (перерисовать) часть окна приложения
Rectangle (dc, -1, -1, Width+1, Height+1); // Прорисовка прямоугольника в клиентской области
MoveToEx (dc, 0, Height * (0.5 + 0.3*sin(0.1*Phase)), NULL); // Функция устанавливает текущую позицию пера, равную (x,y) для указанного контекста отображения dc
for (int i=0; i<Width; i++) // Цикл для рисования графика (изменение по оси X)
LineTo (dc, i, Height * (0.5 + 0.3*sin(0.1*(i+Phase))) ); // Функция рисует линию из текущей позиции пера в точку с координатами, перечисленными в параметрах функции
break; // Выполнение переключателя прерывается
case WM_SIZE: // Сообщение – изменился размер окна
Width = LOWORD(lParam), Height = HIWORD(lParam); // Изменение значений ширины и высоты окна в соответствии с параметрами сообщения
break; // Выполнение переключателя прерывается
case WM_KEYDOWN: // Сообщение - нажата несистемная клавиша (без + Alt). Посылается окну с фокусом клавиатуры
if (wParam != VK_ESCAPE) // Проверка – нажата ли клавиша «Esc»
break; // Выполнение переключателя прерывается
// else no break – в противном случае прерывания переключателя нет, т.е. переход к завершению работы окна
case WM_DESTROY: // Сообщение – окно должно быть разрушено (завершить работу приложения). После этого сообщения окно удаляется с экрана
PostQuitMessage (0); // Функция направляет сообщение WM_QUIT в функцию WinMain (обычно в ответ на сообщение WM_DESTROY). Указывает системе, что процесс, сделавший запрос необходимо прекратить
break; // Выполнение переключателя прерывается
case WM_COMMAND: // Сообщение – выбран пункт меню или элемент управления (кнопка); нажата «горячая» клавиша
break; // Выполнение переключателя прерывается
}
return DefWindowProc (hWnd, message, wParam, lParam); // Смотри комментарии выше
}
Листинг 14
wmId = LOWORD(wParam); // Выделение из 32-битового целочисленного значения младшего слова и запись в переменную
wmEvent = HIWORD(wParam); // Выделение из 32-битового целочисленного значения старшего слова и запись в переменную
// Новый переключатель для определения какой пункт меня выбран (кнопок нет, поэтому подобные события отсутствуют):
switch (wmId) // Параметром переключателя служит код-идентификатор, указанный при создании пункта меню
{
case IDM_SAVE_AS: // Сообщение - выбран пункт меню «Сохранить как…»
SaveScreen (hWnd); // Вызов функции сохранения изображения клиентской области
break; // Выполнение переключателя прерывается
case IDM_EXIT: // Сообщение - выбран пункт меню «Выход»
DestroyWindow(hWnd); // Функция уничтожает окно. Она посылает сообщения WM_DESTROY и WM_NCDESTROY окну, чтобы разрушить его и удалить фокус клавиатуры из него. Функция также уничтожает меню, очищает очередь сообщений, разрушает таймеры и т.д.
break; // Выполнение переключателя прерывается
case IDM_BLACK: // Сообщение - выбран пункт меню «Черный»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_BLUE: // Сообщение - выбран пункт меню «Синий»
hPen=CreatePen(PS_SOLID, 1, RGB(0,0,255)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_GREEN: // Сообщение - выбран пункт меню «Зеленый»
hPen=CreatePen(PS_SOLID, 1, RGB(0,255,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_RED: // Сообщение - выбран пункт меню «Красный»
hPen=CreatePen(PS_SOLID, 1, RGB(255,0,0)); // Создание пера с определенным стилем, шириной и цветом
SelectObject(dc, hPen); // Функция производит выбор объекта в контекст устройства
break; // Выполнение переключателя прерывается
case IDM_NEW: // Сообщение - выбран пункт меню «Новый»
case IDM_RESET: // Сообщение - выбран пункт меню «Сброс»
Phase=0; // Сброс значения аргумента периодической функции в нуль
break; // Выполнение переключателя прерывается
case IDM_ABOUT: // Сообщение - выбран пункт меню «О программе»
MessageBox(hWnd,L"PlotDrawer v.1.0. Copyright © M. Gorozheev Ph.D., 2015",L"About MessageBox", MB_OK | MB_ICONWARNING); /* Функция отображает модальное диалоговое окно, которое содержит набор кнопок и краткое сообщение конкретных приложений (в данном случае – информацию о программе). Окно возвращает целое значение, указывающее, какая кнопка нажата пользователем */
break; // Выполнение переключателя прерывается
default: // Эта «ветка» выполняется, если нет ни одного совпадения сообщения с метками case
return DefWindowProc(hWnd, message, wParam, lParam); /* Функция обеспечивает стандартную обработку сообщений, которые явно не обрабатываются в функции WindowProc данного приложения. Эта функция гарантирует, что каждое сообщение обрабатывается. Она вызывается с теми же параметрами, что были получены функцией WindowProc */
}
Листинг 15
// Функция создает диалог для сохранения изображения (получает путь)
void SaveScreen(HWND hWnd)
{
OPENFILENAME openFile; // Создание структуры. В нее записывается информация о выборе пользователя после закрытия диалогового окна
TCHAR szPath[MAX_PATH]; // Символьный массив для хранения пути к файлу
TCHAR szFile[MAX_PATH]; // Символьный массив для хранения имени файла
// Инициализация структуры OPENFILENAME:
ZeroMemory( &openFile, sizeof(OPENFILENAME) ); // Функция ZeroMemory заполняет блок памяти нулями
openFile.lStructSize = sizeof(OPENFILENAME); // lStructSize определяет длину структуры (байт); инициализируем оператором sizeof
szFile[0] = ''; // Делаем строку пустой, т.е. символ конца строки идет первым в массиве
openFile.hwndOwner = hWnd; // hwndOwner - дескриптор окна, которое владеет блоком диалога
openFile.lpstrFile = szFile; // Указатель на буфер с именем файла. Когда функция GetOpenFileName или GetSaveFileName возвращает значение, этот буфер содержит идентификатор диска, путь, имя и расширение выбранного файла
openFile.nMaxFile = sizeof(szFile)/sizeof(*szFile); // Размер буфера, в символах (TCHAR), указанного членом lpstrFile
openFile.lpstrFilter = L"Bitmap Files (*.bmp)*.bmp"; // Указатель на буфер, содержащий пары строк фильтров с нулевым символом в конце. Последняя строка в буфере должна быть завершена двумя символами ПУСТО (NULL)
openFile.lpstrDefExt = L"*.bmp"; // Указатель на буфер, который содержит заданное по умолчанию расширение
// При первом сохранении документа пользователем путь по умолчанию приходится на папку «Мои рисунки»:
if ( TRUE == bFirstSave ) // Если логическая переменная равняется истине, то использовать путь по умолчанию
{
/* Функция SHGetFolderPath возвращает полный путь к указанному специальному каталогу по его идентификатору. Макрос SUCCEEDED предусматривает унифицированную проверку на успешное завершение при любом значении состояния: */
if ( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_MYPICTURES, NULL, 0, szPath ) ) )
{
// Инициализация lpstrInitialDir значением (путем), который функция SHGetFolderPath записала в szPath.
// Из-за этого функция GetSaveFileName представляет папку «Мои рисунки» в качестве начального каталога для диалогового окна:
openFile.lpstrInitialDir = szPath;
}
}
// Отображается окно стандартного диалога сохранения файла с путем по умолчанию в каталог «Мои рисунки» или выбранное пользователем
// ранее местоположение. Функция GetSaveFileName создает диалоговое окно «Сохранить», а оператор if проверяет наличие ошибок:
if ( GetSaveFileName( &openFile ) == TRUE ) // Смотри выше
{
// Пользователь нажал кнопку «Сохранить».
// Происходит сохранение файла:
UpdateWindow (hWnd); // Перерисовка главного окна в связи с появлением диалогового (иначе изображение перекрывалось бы)
save(hWnd, openFile); // Вызов функции, которая непосредственно сохраняет изображение клиентской области окна в файл
bFirstSave = FALSE; // Логическая переменная «Первое_Сохранение» становится равной FALSE
}
else // Если не нажата кнопка «Сохранить»
{
// Пользователь прервал диалог сохранения файла, или возникла ошибка
}
}
Листинг 16
// Функция, непосредственно сохраняющая изображение клиентской области окна в файл
bool save (HWND hWnd, OPENFILENAME openFile)
{
// Создание структуры с атрибутами файла:
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Получить дескриптор контекста устройства дисплея для клиентской области окна:
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Создание совместимого DC, который используется в BitBlt из окна DC:
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC) // Если создать DC не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить клиентскую область для расчета размера:
RECT rcClient; // Создание прямоугольника для хранения координат рабочей области окна
GetClientRect(hWnd, &rcClient); // Получение координат рабочей области окна
// Создать совместимый bitmap (точечный рисунок) из окна DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
if(!hbmScreen) // Если создать bitmap не получилось, выводится модальное окно с сообщением об ошибке
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Выбор совместимого bitmap (растрового изображения) в памяти совместимого DC:
SelectObject(hdcMemDC,hbmScreen);
/* Копирование блока битов в память совместимого DC. Функция BitBlt используется для копирования отдельных битов из области источника изображения в область-получатель. Если копирование не удалось, выводится модальное окно с сообщением об ошибке: */
if(!BitBlt(hdcMemDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcWindow, 0, 0, SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done; // Переход к завершающему коду (освобождению памяти)
}
// Получить BITMAP (точечный рисунок) из HBITMAP:
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Начиная с 32-битной Windows, GlobalAlloc и LocalAlloc реализованы как «функции-обертки», которые вызывают HeapAlloc,
// используя дескриптор, ссылающийся на кучу, выделяемую процессу по умолчанию. Таким образом, GlobalAlloc и LocalAlloc
// имеют большую нагрузку, чем HeapAlloc:
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Получаем "биты" из растрового изображения и копируем их в буфер, на который указывает lpbitmap:
GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
// Создается файл. Именно здесь происходит сохранение захваченного изображения клиентской области:
HANDLE hFile = CreateFile(openFile.lpstrFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Добавление размера заголовков к размеру растрового изображения, чтобы получить общий размер файла:
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Смещение, определяющее, где фактически начинаются биты растрового изображения:
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Размер файла:
bmfHeader.bfSize = dwSizeofDIB;
// Компонент bfType всегда должен равняться BM для растровых изображений:
bmfHeader.bfType = 0x4D42; //Тип BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Разблокировка и освободить DIB (Device Independent Bitmap, растровое изображение) из кучи (памяти):
GlobalUnlock(hDIB);
GlobalFree(hDIB);
CloseHandle(hFile); // Закрыть дескриптор для файла, который был создан
// После метки производится освобождение памяти (удаление объектов) и выход из функции:
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(hWnd,hdcWindow);
return TRUE;
}