// Done by TheTutor -- 9/08/01
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
/* Это обучалка об открытии файлов с использованием диалоговых окон. Мы не будем
использовать для этого "истинное диалоговое окно" (т.е. у нас не будет диалоговой
процедуры), а просто используем windows API.
*/
#include <windows.h>
#include "resource.h"
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define BUFF_MAX 256 // Размер буфера, который нужен для открытия файлов
#define class_name "GameTutorials_OpenFileUseDialog"
// Эта функция заполнит структуру типа OPENFILENAME, которая нам понадобится для
// создания диалогового окна "по умолчанию".
void FillOpenParams(OPENFILENAME&, HWND, char*, char*);
// Загружает и выводит передаваемый .bmp файл на экран
bool BlitImage(char*, HWND);
// "Очищает" окно, заполняя его белым цветом
void ClearWindow(HWND);
// Стандартная callback функция
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
HWND hwnd;
HMENU hmenu;
MSG msg;
WNDCLASSEX wndclassex = {0};
// Заполняем нужные нам поля
wndclassex.cbSize = sizeof(WNDCLASSEX);
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = WinProc;
wndclassex.hInstance = hinstance;
wndclassex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassex.lpszClassName = class_name;
// Загружаем меню
hmenu = LoadMenu(hinstance,MAKEINTRESOURCE(IDR_MENU1));
// Проверка ошибки
if(!hmenu)
return EXIT_FAILURE; // Произошло что-то плохое :(
RegisterClassEx(&wndclassex); // Регистрируем окно
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, // У окна будет толстая рамка
class_name,
"GameTutorials -- Opening Files with a Dialog Box",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, // Окно будет размещено в позиции по умолчанию
CW_USEDEFAULT,
WIN_WIDTH,
WIN_HEIGHT,
NULL,
hmenu,
hinstance,
NULL);
// Проверка ошибки
if(!hwnd)
return EXIT_FAILURE; // Произошло что-то плохое!
ShowWindow(hwnd, ishow);
UpdateWindow(hwnd);
while(1)
{
// Получаем сообщения, если они есть
if(PeekMessage(&msg,hwnd,0,0,PM_REMOVE))
{
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Здесь мы можем проводить все основные операции
}
}
DestroyMenu(hmenu); // Освобождаем меню
UnregisterClass(class_name,hinstance); // Разрегистрируем WNDCLASSEX
return msg.wParam;
}
// WinProc
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
/* Эта структура используется либо функцией GetOpenFileName() (для открытия файлов),
либо функцией GetSaveFileName() (для сохранения файлов). Мы будем использовать
ее для открытия файла. Информация о файле, который мы будем пытаться открыть, будет
размещаться в этой структуре. */
OPENFILENAME open_params = {0};
char filter[BUFF_MAX] = {0}; // Будет заполняться "фильтром", объяснение позже
char file_name[BUFF_MAX] = {0}; // Будет использоваться для "имени файла по умолчанию" диалогового окна
// В зависимости от сообщения - выполняем различные действия
switch(message)
{
case WM_PAINT:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
return 0;
case WM_COMMAND:
// Если пользователь выбрал "Quit" из меню
if(LOWORD(wparam) == ID_FILE_QUIT)
PostQuitMessage(0); // Мы поступим как просят и выйдем из программы
// Если пользователь выбрал "Open" из меню
if(LOWORD(wparam) == ID_BMP_FILE_OPEN)
{
/* Мы собираемся заполнить нужные нам поля структуры "open_params", но
сперва нам нужно определить "filter". Это указатель на буфер, содержащий
пару строк с завершающим нулевым символом. Последняя строка в буфере
должна завершаться двумя символами NULL. Первая строка содержит описание
типа файла, который будет отображаться. Вторая строка это расширение(я)
файла(ов). Если расширений много, они разделяются точкой с запятой.
Это немного запутывает, поэтому мы будем рассматривать фильтр шаг за
шагом. Поскольку мы определили фильтр следующим образом:
char filter[BUF_MAX] = {0};
то мы можем быть уверены, что каждый символ в символьном массиве изначально
равен NULL */
strcat(filter,"BMP Files"); // Записываем в фильтр "BMP Files"
// Теперь фильтр выглядит вот так:
//
// filter[0] = 'B'
// filter[1] = 'M'
// filter[2] = 'P'
// filter[3] = ' '
// filter[4] = 'F'
// filter[5] = 'i'
// filter[6] = 'l'
// filter[7] = 'e'
// filter[8] = 's'
// filter[9] = NULL // Все оставшиеся символы также равны NULL
int index = strlen(filter) + 1; // Индекс теперь равняется первому свободному символу
// после первого NULL в фильтре
// "Вторая половина" фильтра заполняется "*.bmp"
filter[index++] = '*';
filter[index++] = '.';
filter[index++] = 'b';
filter[index++] = 'm';
filter[index++] = 'p';
// В итоге фильтр выглядит вот так:
//
// filter[0] = 'B' filter[10] = '*'
// filter[1] = 'M' filter[11] = '.'
// filter[2] = 'P' filter[12] = 'b'
// filter[3] = ' ' filter[13] = 'm'
// filter[4] = 'F' filter[14] = 'p'
// filter[5] = 'i' filter[15] = NULL
// filter[6] = 'l' filter[16] = NULL // Все остальные символы
// filter[7] = 'e' // в фильтре
// filter[8] = 's' // также равны NULL
// filter[9] = NULL
FillOpenParams(open_params, hwnd, filter, file_name);
// Если GetOpenFileName НЕ завершается неудачно (она может возвратить код ошибки
// и пройти "if test")
if(GetOpenFileName(&open_params))
{
/* Обратите внимание, что мы передаем "open_params.lpstrFile".
С нашим вызовом GetOpenFileName() она заполнилась значением полного
пути к файлу, выбранному пользователем. Мы будем использовать ее
для загрузки файла и вывода его на экран (если все пройдет хорошо).
*/
if(BlitImage(open_params.lpstrFile, hwnd) == false)
MessageBox(hwnd,"Failed to blit the image","ERROR",MB_OK | MB_ICONERROR);
}
}
return 0;
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
return 0;
} // конец switch(message)
return DefWindowProc(hwnd, message, wparam, lparam);
}
void FillOpenParams(OPENFILENAME &open_params, HWND hwnd, char *filter, char *file_name)
{
/** Инициализируем поля "open_params", нужные нам для открытия .bmp файла **/
open_params.lStructSize = sizeof(OPENFILENAME); // Размер структуры
open_params.hwndOwner = hwnd; // "Окно-собственник" диалогового окна
open_params.lpstrFilter = filter;
open_params.lpstrFile = file_name; // Это указатель на имя файла, используемое для
// инициализации диалогового окна. Так как наша
// "file_name" равна NULL, не будет использовано
// никакого "имени файла по умолчанию"
open_params.nMaxFile = BUFF_MAX; // Размер буфера "file_name"
open_params.lpstrInitialDir = NULL; // Эта переменная уже равна NULL, но я устанавливаю
// это значение снова, чтобы прокомментировать.
// Это значит (для Windows NT 5.0/Windows 98 и более
// поздних), что если "текущая директория" содержит
// файлы типов, определенных фильтром, то эта директория
// будет начальной. В противном случае начальной будет
// "директория персональных файлов"
open_params.lpstrFileTitle = NULL; // Опять же, переменная уже NULL, но она является
// заголовком диалогового окна. Так как мы установили
// NULL, то будет использован заголовок по умолчанию
// ("Open" в нашем случае)
/* Как вы можете предположить, есть набор флагов, которые могут быть установлены
для диалогового окна. Я опишу, что означают эти четыре.
OFN_FILEMUSTEXIST -- Разрешает набирать в поле набора имени файла только
имена существующих файлов.
OFN_PATHMUSTEXIST -- Разрешает пользователю набирать только верные пути и имена файлов.
OFN_NOCHANGEDIR -- Изменяет текущую директорию обратно к прежнему значению, если пользователь
изменил ее при поиске файлов
OFN_HIDEREADONLY -- Скрывает кнопку выбора "read only" (возможность открывать файлы "read only")
*/
open_params.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR
| OFN_HIDEREADONLY;
// Теперь мы заполнили все, что хотели
}
bool BlitImage(char *file_name, HWND hwnd)
{
ClearWindow(hwnd); // Сперва мы очистим окно (закрасим его в белый цвет)
// Загружаем bitmap
HBITMAP hbitmap = (HBITMAP)LoadImage(NULL,file_name,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
// Проверка ошибки
if(!hbitmap)
return false;
HDC hdc = GetDC(hwnd); // Получаем контекст устройства окна
// Проверка ошибки
if(!hdc)
return false;
HDC bmp_hdc = CreateCompatibleDC(hdc); // Создаем совместимый контекст устройства
// Проверка ошибки
if(!bmp_hdc)
return false;
HBITMAP old_bmp = (HBITMAP)SelectObject(bmp_hdc, hbitmap); // Выбираем загруженный нами bitmap
// в совместимый контекст устройства
// Проверка ошибки
if(!old_bmp)
return false;
// Если мы добрались досюда, мы можем отобразить bitmap, который мы загрузили
BitBlt(hdc,0,0,WIN_WIDTH,WIN_HEIGHT,bmp_hdc,0,0,SRCCOPY);
/*** Освобождаем память ***/
SelectObject(bmp_hdc, old_bmp); // Всегда нужно выбирать "обратно" прежний HGDIOBJ
DeleteObject(hbitmap);
DeleteDC(bmp_hdc);
ReleaseDC(hwnd,hdc);
return true; // Изображение загружено и отображено
} // конец bool BlitImage(char *file_name, HWND hwnd)
// Заполняем фон окна белым
void ClearWindow(HWND hwnd)
{
HDC hdc = GetDC(hwnd); // Получаем контекст устройства окна
RECT rect = {0}; // Прямоугольник RECT, который определяет клиент-область окна
GetClientRect(hwnd,&rect); // Записываем в "rect" клиент-область окна
// Заполняем окно белым
FillRect(hdc,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
// Освобождаем память
ReleaseDC(hwnd,hdc);
}
/* Несколько слов напоследок --
Для размера буфера мы использовали BUFF_MAX. У Windows есть собственное определение
#define - MAX_PATH. Для ваших приложений вы вероятно захотите использовать его для
размера буфера "file name". Я не использовал его, потому что подумал, что так
будет немного понятнее.
**Задание**
Мы использовали функцию windows API GetOpenFileName(), чтобы "вывести диалоговое
окно для выбора загружаемого файла". У Windows есть похожая функция API, GetSaveFileName()
которая получает имя файла для сохранения. Сможете ли вы написать небольшое приложение,
которое позволит пользователю вводить текст (как в NotePad), и затем записывать файл
с использованием GetSaveFileName().
*/
/*
| TheTutor
| thetutor@gametutorials.com
| © 2001-2002 GameTutorials
*/
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
|