// ===================================== // Функция WinMain // ===================================== #pragma argsused
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения
case WM_SIZE: { // Устанавливаем размер органа управления // (текстового редактора) в соответствии // с размерами главного окна приложения MoveWindow(hEdit, 0, 20, LOWORD(lParam), HIWORD(lParam) - 20, TRUE); return 0; }
// Когда главное окно приложения получает // фокус ввода, отдаем фокус редактору текста case WM_SETFOCUS: { SetFocus(hEdit); return 0; }
// Произошло изменение в редактируемом // тексте else if(HIWORD(lParam) == EN_UPDATE) { // Устанавливаем флаг обновления текста bUpdate = TRUE; } return 0; }
// Нажата кнопка сохранения текста else if(wParam == ID_SAVE) { WORD wSize; HANDLE hTxtBuf; NPSTR npTextBuffer;
// Определяем размер текста wSize = GetWindowTextLength(hEdit);
// Получаем идентификатор блока памяти, // в котором находится редактируемый текст hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);
// Фиксируем блок памяти и получаем указатель // на него npTextBuffer = (NPSTR)LocalLock(hTxtBuf);
// Записываем содержимое блока памяти в файл if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize)) { // При ошибке закрываем файл и выдаем сообщение _lclose(hfDstFile); MessageBox(hwnd, "Ошибка при записи файла", szWindowTitle, MB_OK); return 0; }
// Закрываем файл _lclose(hfDstFile);
// Расфиксируем блок памяти LocalUnlock(hTxtBuf);
// Так как файл был только что сохранен, // сбрасываем флаг обновления bUpdate = FALSE;
return 0; }
// Создание нового файла else if(wParam == ID_NEW) { // Проверяем флаг обновления if(bUpdate) { if(IDYES == MessageBox(hwnd, "Файл был изменен. Желаете сохранить?", szWindowTitle, MB_YESNO | MB_ICONQUESTION)) return 0; }
// Выбираем выходной файл if (GetSaveFileName(&ofn)) {
// При необходимости создаем файл hf = _lcreat(ofn.lpstrFile, 0); return hf; } else return 0; }
Функция WinMain приложения создает главное окно и запускает цикл обработки сообщений. В этом цикле вызывается функция TranslateMessage, необходимая для получения символьных сообщений.
Как обычно, внешний вид и функциональные возможности нашего приложения определяет функция главного окна.
В нашем приложении используется многострочный редактор текста с рамкой, вертикальной и горизонтальной полосой просмотра, выполняющий функции автоматической свертки текста по вертикали и горизонтали, с выравниванием текста по левой границе.
Пусть вас не смущает, что мы указали нулевые значения для координат и размеров редактора текста. Размеры редактора текста зависят от размеров главного окна приложения, поэтому они будут установлены при обработке сообщения WM_SIZE.
Далее обработчик сообщения WM_CREATE устанавливает максимальную длину редактируемого текста, для чего посылает редактору сообщение EM_LIMITTEXT:
SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);
После этого сбрасывается флаг обновления текста:
bUpdate = FALSE;
Этот флаг будет устанавливаться при внесении в текст любых изменений и проверяться перед созданием или загрузкой нового текста, а также перед завершением работы приложения.
Далее в верхней части главного окна приложения создаются четыре кнопки: "New", "Open", "Save" и "Exit".
Во время обработки сообщения WM_SIZE (поступающего в функцию окна при создании окна и при изменении его размера) устанавливаются правильные размеры и расположение редактора текста:
Редактор текста располагается на 20 пикселов ниже верхней границы внутренней области окна и имеет высоту, на 20 пикселов меньшую, чем высота внутренней области окна. В результате в верхней части основного окна приложения остается место для четырех кнопок, управляющих работой приложения.
Так же как и в предыдущем приложении, обработчик сообщения WM_SETFOCUS передает фокус ввода текстовому редактору, для чего вызывает функцию SetFocus.
Обработчик сообщения WM_COMMAND получает сообщения, приходящие от окна редактирования и кнопок.
Если сообщение пришло от окна редактирования, проверяется код извещения.
Код извещения EN_ERRSPACE соответствует ошибке при запросе дополнительной памяти. При его обработке выдается предупреждающее сообщение. Код извещения EN_UPDATE поступает при любом изменении содержимого редактируемого текста. Обработчик этого кода извещения устанавливает флаг обновления текста, сигнализируя о том, что вы изменили текст и его необходимо сохранить:
else if(HIWORD(lParam) == EN_UPDATE) { // Устанавливаем флаг обновления текста bUpdate = TRUE; }
Если нажата кнопка сохранения текста, обработчик сообщения WM_COMMAND открывает выходной файл, вызывая функцию OpenSaveFile, определенную в нашем приложении. Последняя использует стандартную диалоговую панель "Save As", с которой вы уже знакомы.
Далее обработчик определяет размер текста, находящегося в окне редактирования (в байтах), вызывая функцию GetWindowTextLength:
wSize = GetWindowTextLength(hEdit);
Далее, посылая сообщение EM_GETHANDLE, функция определяет идентификатор блока памяти, используемой редактором для хранения текста:
Управление памятью в Windows мы рассмотрим позже. Сейчас только отметим, что получив идентификатор блока памяти, мы еще не имеем к этой памяти непосредственного доступа. Для получения доступа, а заодно и адреса блока памяти, этот блок следует зафиксировать, вызвав (в нашем случае) функцию LocalLock:
npTextBuffer = (NPSTR)LocalLock(hTxtBuf);
После этого мы записываем весь блок памяти в файл, закрываем файл и освобождаем зафиксированный блок памяти:
if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize)) { _lclose(hfDstFile); MessageBox(hwnd, "Ошибка при записи файла", szWindowTitle, MB_OK); return 0; } _lclose(hfDstFile); LocalUnlock(hTxtBuf);
Так как файл был только что записан на диск, мы сбрасываем флаг обновления:
bUpdate = FALSE;
При создании нового файла прежде всего проверяется флаг обновления. Если он сброшен, содержимое текстового редактора сбрасывается, для чего в него записывается пустая строка:
SetWindowText(hEdit, "\0");
При загрузке файла для редактирования после проверки флага обновления вызывается функция OpenFile. Эта функция, определенная в нашем приложении, открывает файл с помощью стандартной диалоговой панели "Open", с которой вы уже знакомы.
Далее определяется размер файла, который не должен превосходить 32000 байт. Если файл имеет подходящий размер, приложение заказывает буфер для загрузки файла, вызывая функцию malloc (для приложений Windows есть и другие способы получения памяти, но этот тоже работает):
lpTextBuffer = (LPSTR)malloc(32000);
Далее файл читается в буфер, после чего буфер закрывается двоичным нулем:
Для переноса текста из буфера в редактор вызывается функция SetWindowText:
SetWindowText(hEdit, lpTextBuffer);
После этого буфер можно освободить, вызвав функцию free.
При завершении работы приложения с помощью кнопки "Exit" после проверки флага обновления в функцию главного окна приложения посылается сообщение WM_CLOSE:
SendMessage(hwnd, WM_CLOSE, 0, 0L);
Файл определения модуля приложения TEDIT приведен в листинге 2.25.