Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 12, М.: Диалог-МИФИ, 1993, 255 стр.

[Назад] [Содеожание] [Дальше]

2.4. Редактор текста

В операционной системе Microsoft Windows зарегистрирован класс окна с именем "edit", на базе которого вы можете создать однострочный или многострочный текстовый редактор. Такой редактор может быть использован для ввода значений текстовых или числовых переменных, а также для создания и редактирования текстовых файлов (без функций форматирования текста). Встроенный текстовый редактор умеет выполнять функции выделения текста, может работать с универсальным буфером обмена Clipboard.

Для того чтобы в среде Windows сделать свой собственный текстовый редактор, вам достаточно создать на базе класса "edit" орган управления, вызвав функцию CreateWindow. После этого функция родительского окна будет получать от редактора сообщение с кодом WM_COMMAND (как и от других аналогичных органов управления). Вместе с этим сообщением в функцию окна будут передаваться коды извещения, отражающие изменения состояния редактора текста. Вы также можете с помощью функции SendMessage посылать текстовому редактору около трех десятков различных управляющих сообщений, с помощью которых можно изменять редактируемый текст, получать отдельные строки, выделять фрагменты текста, копировать выделенный фрагмент текста в Clipboard и т. д.

Использование предопределенного класса "edit" - самый простой способ создания в приложении редактора текста. Фактически в этом случае вы будете использовать готовый текстовый редактор. Вам не придется самостоятельно обрабатывать сообщения от клавиатуры, управлять текстовым курсором, учитывать ширину букв в пропорциональном шрифте, выполнять свертку текста в окне редактирования по вертикали или горизонтали, заниматься выделением фрагментов текста, работать с Clipboard или решать другие задачи, возникающие при создании текстовых редакторов.

Создание редактора текста

Для создания редактора текста (однострочного или многострочного) следует вызвать функцию CreateWindow, передав ей в качестве первого параметра указатель на строку "edit":

hwndEdit = CreateWindow("edit", NULL,
       WS_CHILD | WS_VISIBLE | WS_BORDER |
         ES_LEFT,
         30, 30, 300, 30,
         hwnd, (HMENU) ID_EDIT, hInst, NULL);

Заголовок окна не используется, поэтому второй параметр следует указать как NULL.

Если при создании текстового редактора не указать стиль окна WS_BORDER, область редактирования не будет выделена. Это неудобно для пользователя, особенно если в окне имеется несколько редакторов. При использовании стиля WS_BORDER вокруг редактора будет нарисована рамка.

Кроме обычных стилей окна для текстового редактора указывают стили, символическое имя которых начинается с префикса ES_. Это стили редактора текста. Они влияют на внешний вид редактора и выполняемые им функции. Подробно стили редактора текста будут описаны в следующем разделе.

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

Стили редактора текста

Приведем список стилей окна, которые используются при создании редактора текста.

Стиль Описание
ES_AUTOHSCROLL Выполняется автоматическая свертка текста по горизонтали. Когда при наборе текста достигается правая граница окна ввода, весь текст сдвигается влево на 10 символов
ES_AUTOVSCROLL Выполняется автоматическая свертка текста по вертикали. Когда при наборе текста достигается нижняя граница окна ввода, весь текст сдвигается вверх на одну строку
ES_CENTER Центровка строк по горизонтали в многострочном текстовом редакторе
ES_LEFT Выравнивание текста по левой границе окна редактирования
ES_LOWERCASE Выполняется автоматическое преобразование введенных символов в строчные (маленькие)
ES_MULTILINE Создается многострочный редактор текста
ES_NOHIDESEL Если редактор текста теряет фокус ввода, при использовании данного стиля выделенный ранее фрагмент текста отображается в инверсном цвете. Если этот стиль не указан, при потере фокуса ввода выделение фрагмента пропадает и появляется вновь только тогда, когда редактор текста вновь получает фокус ввода
ES_OEMCONVERT Выполняется автоматическое преобразование кодировки введенных символов из ANSI в OEM и обратно. Обычно используется для ввода имен файлов
ES_PASSWORD Этот стиль используется для ввода паролей или аналогичной информации. Вместо вводимых символов отображается символ "*" или другой, указанный при помощи сообщения EM_SETPASSWORDCHAR (см. ниже раздел, посвященный сообщениям для редактора текста)
ES_READONLY Создаваемый орган управления предназначен только для просмотра текста, но не для редактирования. Этот стиль можно использовать в версии 3.1 операционной системы Windows или в более поздней версии
ES_RIGHT Выравнивание текста по правой границе окна редактирования
ES_UPPERCASE Выполняется автоматическое преобразование введенных символов в заглавные (большие)
ES_WANTRETURN Стиль используется в комбинации со стилем ES_MULTILINE. Используется только в диалоговых панелях. При использовании этого стиля клавиша <Enter> действует аналогично кнопке диалоговой панели, выбранной по умолчанию. Этот стиль можно использовать в версии 3.1 операционной системы Windows или в более поздней версии

Для создания однострочного редактора текста достаточно указать стиль ES_LEFT (который, кстати, определен в файле windows.h как 0). Для обеспечения свертки текста по горизонтали используйте дополнительно стиль ES_AUTOHSCROLL.

Если вам нужен многострочный редактор текста, укажите стиль ES_MULTILINE. Для обеспечения автоматической свертки текста по горизонтали и вертикали следует также указать стили ES_AUTOHSCROLL и ES_AUTOVSCROLL.

Если в многострочном редакторе текста не указан стиль ES_AUTOHSCROLL, но указан стиль ES_AUTOVSCROLL, при достижении в процессе ввода текста правой границы окна ввода выполняется автоматический перенос слова на новую строку. Если свертка не используется, в описанной выше ситуации будет выдан звуковой сигнал.

Многострочный редактор текста может иметь вертикальную и горизонтальную полосы просмотра. Для создания полос просмотра достаточно в стиле редактора указать константы WS_HSCROLL и WS_VSCROLL.

Коды извещения

Текстовый редактор посылает в родительское окно сообщение WM_COMMAND с параметром wParam, равным идентификатору редактора. Этот идентификатор можно использовать для того чтобы различать сообщения, поступающие от разных органов управления (в частности, от разных текстовых редакторов, если в одном окне их создано несколько штук).

Младшее слово параметра lParam содержит идентификатор окна, полученный от функции CreateWindow при создании редактора.

Старшее слово параметра lParam содержит код извещения. Анализируя этот код, приложение может определить событие, послужившее причиной появления сообщения WM_COMMAND.

Приведем список кодов извещений.

Код извещения Описание
EN_CHANGE Изменилось содержимое текста в окне редактирования
EN_ERRSPACE Произошла ошибка при попытке получить дополнительную память
EN_HSCROLL Выполнена свертка текста по горизонтали. Пользователь использовал горизонтальную полосу просмотра для свертки текста, но изменения в окне редактирования еще не произошли
EN_KILLFOCUS Текстовый редактор потерял фокус ввода
EN_MAXTEXT При вводе очередного символа произошло переполнение, так как было превышен максимально допустимый для редактора размер текста
EN_SETFOCUS Текстовый редактор получил фокус ввода
EN_UPDATE Содержимое текстового редактора будет изменено. Пользователь ввел один символ текста или выполнил другую операцию редактирования, но выполнение этой операции еще не отразилось на содержимом окна редактирования. После этого извещения после отображения изменений придет извещение с кодом EN_CHANGE
EN_VSCROLL Выполнена свертка текста по вертикали. Пользователь использовал вертикальную полосу просмотра для свертки текста, но изменения в окне редактироания еще не произошли

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

Сообщения для редактора текста

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

EM_CANUNDO

С помощью этого сообщения можно проверить, поддерживает ли редактор текста операцию отмены последнего действия редактирования. Эта операция выполняется по сообщению WM_UNDO, когда оно посылается в редактор текста.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

TRUE, если операция поддерживается, FALSE - если нет

EM_EMPTYUNDOBUFFER

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

EM_FMTLINES

Управление режимом добавления или удаления символов конца строки в процессе переноса слов на новую строку.

Параметры:

wParam = (WPARAM)(BOOL)fAddEOL;

lParam = 0L;

Значение флага fAddEOL: TRUE - вставка, FALSE - удаление.

Возвращаемое значение:TRUE - вставка, FALSE - удаление

EM_GETFIRSTVISIBLELINE

Получение номера самой верхней видимой строки в окне редактирования. Используется в Windows версии 3.1 и более поздних версиях.

Параметры: не используются.

Возвращаемое значение:

Номер строки. Первой строке соответствует значение 0

EM_GETHANDLE

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

Возвращаемое значение:

Идентификатор блока памяти

EM_GETLINE

Копирование строки из редактора текста в буфер.

Параметры:

wParam = (WPARAM)nLine;

lParam = (LPARAM)(LPSTR)lpCh;

nLine - номер строки, lpCh - адрес буфера для строки.

Возвращаемое значение:

Номер скопированных в буфер байт данных или 0, если указанный номер строки превосходит количество строк в тексте

EM_GETLINECOUNT

Определение количества строк в тексте.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

Количество строк текста или 1, если окно редактирования не содержит ни одной строки текста

EM_GETMODIFY

Определение значения флага обновления.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

TRUE, если текст был изменен или FALSE - если нет

EM_GETPASSWORDCHAR

Получение символа, используемого для вывода при вводе пароля. Используется в Windows версии 3.1 и более поздних версиях.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

Код символа

EM_GETRECT

Определение координат прямоугольной области, используемой для редактирования текста. Эта область по своим размерам не обязательно совпадает с областью, занятой самим органом управления.

Параметры:

wParam = 0;

lParam = (LPARAM)(RECT FAR *)lprc;

lprc - указатель на структуру RECT, в которую будут записаны искомые координаты.

Возвращаемое значение: не используется

EM_GETSEL

Определение положения первого и последнего символа в выделенном фрагменте текста.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

Двойное слово. Младшее слово содержит положение первого символа в выделенном фрагменте, старшее - положение символа, следующего за выделенным фрагментом текста

EM_GETWORDBREAKPROC

Получение адреса текущей функции, которая используется для переноса слов с одной строки на другую. Используется в Windows версии 3.1 и более поздних версиях.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение:

Адрес функции или NULL, если такая функция не существует

EM_LIMITTEXT

Определение максимального количества символов, которое можно ввести в окно редактирования.

Параметры:

wParam = (WPARAM)cCmax;lParam = 0L;

cCMax - размер текста.

Возвращаемое значение: не используется

EM_LINEFROMCHAR

Определение номера строки, содержащей символ в заданной позиции.

Параметры:

wParam = (WPARAM) iChar;

lParam = 0L;

iChar - номер позиции. Можно задать как -1, в этом случае используется текущая строка (строка, в которой установлен текстовый курсор), или строка, в которой начинается выделенный фрагмент текста (если в тексте есть выделенный фрагмент).

Возвращаемое значение:

Номер строки. Первой строке соответствует значение 0

EM_LINEINDEX

Определение смещения в байтах от начала текста заданной строки.

Параметры:

wParam = (WPARAM) nLine;

lParam = 0L;

nLine - номер строки. Можно задать как -1, в этом случае используется текущая строка.

Возвращаемое значение:

Смещение в байтах или -1, если указана строка с номером, превосходящим количество строк в окне редактирования

EM_LINELENGTH

Определение размера строки в байтах.

Параметры:

wParam = (WPARAM) iChar;

lParam = 0L;

iChar - номер позиции символа, который находится в строке. Можно задать как -1, в этом случае используется текущая строка, для которой возвращается количество невыбранных символов

Возвращаемое значение:

Размер строки в байтах

EM_LINESCROLL

Свертка заданного количества строк.

Параметры:

wParam = 0;

lParam = MAKELPARAM(dv, dh);

dv - количество сворачиваемых строк по вертикали,dh - количество символов для свертки по горизонтали, не используется для текста, выравненного по правой границе или центрированного.

Возвращаемое значение:

TRUE, если сообщение был послано многострочному редактору, или FALSE - если однострочному

EM_REPLACESEL

Заменить выделенный фрагмент текста. Если в тексте нет выделенных фрагментов, строка будет вставлена в текущей позиции.

Параметры:

wParam = 0;

lParam = (LPARAM)(LPCSTR)lpszStr

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

Возвращаемое значение: не используется

EM_SETHANDLE

Назначение буфера для хранения текста.

Параметры:

wParam = (WPARAM)(HLOCAL)hLoc;

lParam = 0L;

hLoc - идентификатор локального блока памяти, полученный с помощью функции LocalAlloc.

Возвращаемое значение: не используется

EM_SETMODIFY

Установка флага обновления.

Параметры:

wParam = (WPARAM)(UINT)fMod;

lParam = 0L;

fMod - новое значение для флага обновления. TRUE, если текст надо отметить, как обновленный, FALSE - если как необновленный.

Возвращаемое значение: не используется

EM_SETPASSWORDCHAR

Установка символа, который используется для вывода текста (в редакторе, имеющим стиль ES_PASSWORD).

Параметры:

wParam = (WPARAM)(UINT)chChar;

lParam = 0L;

chChar - код символа.

Возвращаемое значение:

TRUE, если сообщение посылается редактору, созданному как орган управления

EM_SETREADONLY

Установка или сброс состояния редактора, в котором пользователю позволяется только просматривать текст, но не редактировать (режим "только чтение").

Параметры:

wParam = (WPARAM)(UINT)fReadOnly;

lParam = 0L;

fReadOnly - TRUE для установки режима "только чтение", FALSE - для сброса.

Возвращаемое значение:

TRUE, если установка выполнена без ошибок или FALSE при ошибке

EM_SETRECT

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

Параметры:

wParam = 0;

lParam = (LPARAM)(RECT FAR *)lprc;

lprc - указатель на структуру RECT, в которую будут записаны новые координаты области.

Возвращаемое значение: не используется

EM_SETRECTNP

Аналогично предыдущему, за исключением того что окно редактирования не перерисовывается

EM_SETSEL

Выделение заданных символов в окне редактирования.

Параметры:

wParam = (WPARAM)(UINT)fScroll;

lParam = MAKELPARAM(ichStart, ichEnd);

fScroll - если этот параметр равен 1, текстовый курсор сворачивается, если 0 - нет.

ichStart - начальная позиция.

ichEnd - конечная позиция. Если начальная позиция равна 0, а конечная -1, выбирается весь текст. Если начальная позиция равна -1, выделение фрагмента (если оно было) исчезает.

Возвращаемое значение:

TRUE, если сообщение посылается редактору, созданному как орган управления

EM_SETTABSTOPS

Установка позиций табуляции.

Параметры:

wParam = (WPARAM)cTabs;

lParam = (LPARAM)(const int FAR *) lpTabs;

cTabs - расстояние для табуляции. Если этот параметр указан как 0, используется значение по умолчанию - 32.

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

Возвращаемое значение:

TRUE, если позиции табуляции были установлены ил FALSE при ошибке

EM_SETWORDBREAKPROC

Установка новой функции для переноса слов с одной строки на другую. Используется в Windows версии 3.1 и более поздних версиях.

Параметры:

wParam = 0;

lParam = (LPARAM)(EDITWORDBREAKPROC) ewpbrc;

ewpbrc - адрес переходника для новой функции, которая будет использована для переноса слов. Этот адрес необходимо получить при помощи функции MakeProcInstance, указав последней адрес функции переноса слов.

Возвращаемое значение: не используется

EM_UNDO

Отмена последней операции редактирования текста.

Параметры:

wParam = 0;

lParam = 0L;

Возвращаемое значение: не используется

Помимо описанных выше, текстовому редактору можно посылать некоторые сообщения, символические имена которых начинаются с префикса WM_. Это сообщения WM_COPY, WM_PASTE, WM_CUT, WM_CLEAR. Приведем краткое описание этих сообщений.

WM_COPY

Копирование выделенного фрагмента текста в универсальный буфер обмена Clipboard.

Параметры: wParam = 0; lParam = 0L;

Возвращаемое значение: не используется

WM_PASTE

Вставка текста из буфера обмена Clipboard в текущую позицию редактируемого текста.

Параметры: wParam = 0; lParam = 0L;

Возвращаемое значение: не используется

WM_CUT

Удаление выделенного текста с записью его в Clipboard. Удаленный текст можно восстановить, если послать в редактор сообщение EM_UNDO.

Параметры: wParam = 0; lParam = 0L;

Возвращаемое значение: не используется

WM_CLEAR

Удаление выделенного текста без записи в Clipboard. Удаленный текст можно восстановить, если послать в редактор сообщение EM_UNDO.

Параметры: wParam = 0; lParam = 0L;

Возвращаемое значение: не используется

Приложение EDIT

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

Приложение EDIT (рис. 2.11) демонстрирует работу с простейшим однострочным текстовым редактором.

Рис. 2.11. Главное окно приложения EDIT

Это приложение создает в своем главном окне однострочный редактор текста и кнопку с надписью "OK". Вы можете вводить текст при помощи клавиатуры, выделять фрагменты текста мышью или клавиатурой, а также копировать текст в буфер Clipboard или вставлять текст из этого буфера в окно редактирования. Для выделения текста и работы с буфером Clipboard вы можете использовать стандартные приемы, описанные в руководстве пользователя операционной системы Windows.

После ввода текста нажмите клавишу "OK". На экране появится сообщение, состоящее из введенного вами текста (рис. 2.12).

Рис. 2.12. Сообщение приложения EDIT

Главный файл приложения EDIT приведен в листинге 2.22.


Листинг 2.22. Файл edit\edit.cpp


// ----------------------------------------
// Редактор текста
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>

// Идентификатор редактора текста
#define ID_EDIT   1

// Идентификатор кнопки
#define ID_BUTTON 2

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна
char const szClassName[]   = "EditAppClass";

// Заголовок окна
char const szWindowTitle[] = "Edit Demo";

// Идентификатор копии приложения
HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance, 
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  // Сохраняем идентификатор копии приложения
  // в глобальной переменной
  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем расположение и размеры
    CW_USEDEFAULT,       // окна, принятые по умолчанию
    CW_USEDEFAULT,       // 
    CW_USEDEFAULT,       // 
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры
  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    // Создаем символьные сообщения 
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Идентификатор редактора текста
  static HWND hEdit;

  // Идентификатор кнопки
  static HWND hButton;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Создаем редактор текста
      hEdit = CreateWindow("edit", NULL,
         WS_CHILD | WS_VISIBLE | WS_BORDER |
         ES_LEFT,
         30, 30, 300, 30,
         hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Создаем кнопку
      hButton = CreateWindow("button", "OK",
         WS_CHILD | WS_VISIBLE |
         BS_PUSHBUTTON,
         30, 80, 100, 30,
         hwnd, (HMENU) ID_BUTTON, hInst, NULL);

      return 0;
    }

    // Когда главное окно приложения получает
    // фокус ввода, отдаем фокус редактору текста
    case WM_SETFOCUS:
    {
      SetFocus(hEdit);
      return 0;
    }

    case WM_COMMAND:
    {
      // Обработка извещения текстового редактора
      // об ошибке
      if(wParam == ID_EDIT)
      {
        if(HIWORD(lParam) == EN_ERRSPACE)
        {
          MessageBox(hwnd, "Мало памяти",
           szWindowTitle, MB_OK);
        }
      }

      // Сообщение от кнопки
      else if(wParam == ID_BUTTON)
      {
         BYTE chBuff[80];
         WORD cbText;

         // Записываем в первое слово буфера
         // значение размера буфера в байтах
         * (WORD *) chBuff = sizeof (chBuff) - 1;

         // Получаем от редактора текста содержимое
         // первой строки. Функция возвращает количество
         // байт, скопированных в буфер
         cbText = SendMessage(hEdit, EM_GETLINE, 0,
           (LPARAM)(LPSTR)chBuff);

         // Закрываем буфер двоичным нулем
         chBuff[cbText] = '\0';

         // Выводим содержимое буфера на экран
         MessageBox(hwnd, chBuff,
         szWindowTitle, MB_OK);
      }
      return 0;
    }

    case WM_PAINT:
    {
      HDC hdc;
      PAINTSTRUCT ps;

      // Получаем индекс контекста устройства
      hdc = BeginPaint(hwnd, &ps);

      // Выводим текстовую строку
      TextOut(hdc, 30, 10,
        "Введите строку и нажмите кнопку 'OK'", 36);

      // Отдаем индекс контекста устройства
      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

В начале исходного текста определены идентификаторы редактора текста и кнопки:

#define ID_EDIT   1
#define ID_BUTTON 2

Функция WinMain приложения EDIT не имеет никаких особенностей. Она создает одно главное окно и организует цикл обработки сообщений. Так как текстовый редактор работает с символьными сообщениями, в цикле обработки сообщений вызывается функция TranslateMessage:

while(GetMessage(&msg, 0, 0, 0))
{
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

В функции главного окна приложения определены две статические переменные для хранения идентификаторов созданных органов управления - редактора текста и кнопки:

static HWND hEdit;
static HWND hButton;

По сообщению WM_CREATE функция окна создает редактор текста и кнопку, вызывая функцию CreateWindow. При создании текстового редактора используется комбинация стилей WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT. При этом создается дочернее окно, которое изначально является видимым и имеет рамку. Для текста задается выравнивание по левой границе:

hEdit = CreateWindow("edit", NULL,
   WS_CHILD | WS_VISIBLE | WS_BORDER |
   ES_LEFT,
   30, 30, 300, 30,
   hwnd, (HMENU) ID_EDIT, hInst, NULL);

Когда главное окно приложения получает фокус ввода, в его функцию передается сообщение WM_SETFOCUS. Но нам нужно, чтобы фокус ввода получил редактор текста, так как именно он будет обрабатывать символьные сообщения, поступающие от клавиатуры. Поэтому в ответ на сообщение WM_SETFOCUS функция главного окна отдает фокус ввода текстовому редактору, вызывая функцию SetFocus:

case WM_SETFOCUS:
{
  SetFocus(hEdit);
  return 0;
}

Сообщение WM_COMMAND может приходить в функцию главного окна от текстового редактора или от кнопки.

Если сообщение пришло от текстового редактора, параметр wParam этого сообщения содержит идентификатор редактора ID_EDIT. В этом случае обработчик сообщения получает код извещения из старшего байта параметра lParam. Обрабатывается только одно извещение с кодом EN_ERRSPACE (мало памяти).

Если же сообщение WM_COMMAND пришло от кнопки, параметр wParam этого сообщения содержит идентификатор кнопки ID_BUTTON. Обработчик такого сообщения читает содержимое первой (и единственной) строки текстового редактора в специальным образом подготовленный буфер и выводит строку на экран, вызывая функцию MessageBox.

Подготовка буфера заключается в том, что в его первое слово записывается размер буфера в байтах:

* (WORD *) chBuff = sizeof (chBuff) - 1;

Затем текстовому редактору посылается сообщение EM_GETLINE:

cbText = SendMessage(hEdit, EM_GETLINE, 0,
  (LPARAM)(LPSTR)chBuff);

В качестве парамера lParam используется адрес подготовленного буфера.

После посылки сообщения функция SendMessage возвращает количество скопированных в буфер символов, причем буфер нулем не закрывается. Далее обработчик сообщения закрывает буфер нулем и выводит содержимое буфера на экран:

chBuff[cbText] = '\0';
MessageBox(hwnd, chBuff, szWindowTitle, MB_OK);

Обработчик сообщения WM_PAINT выводит в окно приглашение для ввода текста, вызывая функцию TextOut:

TextOut(hdc, 30, 10,
   "Введите строку и нажмите кнопку 'OK'", 36);

Файл определения модуля для приложения EDIT приведен в листинге 2.23.


Листинг 2.23. Файл edit\edit.def


; =============================
; Файл определения модуля
; =============================
NAME EDIT
DESCRIPTION 'Приложение EDIT, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

Приложение TEDIT

Приложение TEDIT представляет собой текстовый редактор, аналогичный редактору Notepad. В отличие от последнего наш редактор пока не имеет меню. Он способен создавать новые файлы, загружать и редактировать имеющиеся, сохранять текст в старом или новом файле (рис. 2.13).

Рис. 2.13. Главное окно приложения TEDIT

В верхней части главного окна приложения TEDIT расположены четыре кнопки. Окно редактирования имеет горизонтальную и вертикальную полосу просмотра. Размер главного окна приложения можно изменять при помощи толстой рамки. При этом также изменяется размер окна редактирования.

Кнопка "New" предназначена для создания нового текста. Если перед тем как нажать на эту кнопку вы загрузили в редактор текст (или набрали его при помощи клавиатуры), на экран будет выдано предупреждающее сообщение о том, что содержимое редактируемого файла была изменено и его нужно сохранить (рис. 2.14).

Рис. 2.14. Предупреждающее сообщение

Если нажать на кнопку "Yes", вы вернетесь в режим редактирования и сможете сохранить текст, нажав кнопку "Save". Если же нажать на кнопку "No", содержимое окна редактирования будет стерто и вы сможете набирать новый текст.

Кнопка "Open" предназначена для загрузки в редактор текстового файла. Если нажать на эту кнопку, на экране появится стандартная диалоговая панель "Open", с помощью которой вы сможете загрузить файл (рис. 2.15).

Рис. 2.15. Диалоговая панель "Open"

Если перед загрузкой нового файла окно редактирования содержало несохраненный текст, на экран будет выведено предупреждающее сообщение (рис. 2.14).

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

Кнопка "Save" предназначена для сохранения текста в файле. Если нажать на эту кнопку, на экране появится стандартная диалоговая панель "Save As" (рис. 2.16), с помощью которой можно выбрать файл для сохранения текста.

Рис. 2.16. Диалоговая панель "Save As"

И, наконец, последняя кнопка с надписью "Exit" завершает работу приложения. Если перед завершением в окне редактирования имеется несохраненный текст, на экране появляется знакомое вам предупреждающее сообщение (рис. 2.14).

Из приведенного выше описания видно, что наш редактор текста выполняет достаточно сложные функции. Однако тем не менее листинг основного файла исходного текста приложения (листинг 2.24) занимает чуть больше десяти страниц, а размер загрузочного модуля составляет примерно 7 Кбайт. Это возможно благодаря тому, что вся основная работа выполняется не приложением, а модулями, расположенными в библиотеках динамической загрузки операционной системы Windows.


Листинг 2.24. Файл tedit\tedit.cpp


// ----------------------------------------
// Редактор текстовых файлов
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <commdlg.h>
#include <mem.h>
#include <string.h>
#include <stdlib.h>

// Идентификатор редактора текста
#define ID_EDIT   1

// Идентификаторы кнопок
#define ID_NEW    2
#define ID_OPEN   3
#define ID_SAVE   4
#define ID_EXIT   5

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
HFILE OpenFile(void);
HFILE OpenSaveFile(void);

// Имя класса окна
char const szClassName[]   = "TEditAppClass";

// Заголовок окна
char const szWindowTitle[] = "Text Editor";

// Идентификатор копии приложения
HINSTANCE hInst;

// Флаг изменений в тексте
BOOL bUpdate;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance, 
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение
  if(!InitApp(hInstance))
      return FALSE;

  // Сохраняем идентификатор копии приложения
  // в глобальной переменной
  hInst = hInstance;

  // После успешной инициализации приложения создаем
  // главное окно приложения
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем расположение и размеры
    CW_USEDEFAULT,       // окна, принятые по умолчанию
    CW_USEDEFAULT,       // 
    CW_USEDEFAULT,       // 
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры
  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  // Идентификатор редактора текста
  static HWND hEdit;

  // Идентификаторы кнопок
  static HWND hButtNew;
  static HWND hButtOpen;
  static HWND hButtSave;
  static HWND hButtExit;

  // Идентификаторы файлов
  static HFILE hfSrcFile, hfDstFile;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Создаем редактор текста
      hEdit = CreateWindow("edit", NULL,
         WS_CHILD | WS_VISIBLE | WS_BORDER |
         WS_HSCROLL | WS_VSCROLL |
         ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
         ES_MULTILINE,
         0, 0, 0, 0,
         hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Устанавливаем максимальную длину
      // редактируемого текста, равную 32000 байт
      SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

      // Сбрасываем флаг обновления текста
      bUpdate = FALSE;

      // Создаем кнопки
      hButtNew = CreateWindow("button", "New",
         WS_CHILD | WS_VISIBLE |
         BS_PUSHBUTTON,
         0, 0, 50, 20,
         hwnd, (HMENU) ID_NEW, hInst, NULL);

      hButtOpen = CreateWindow("button", "Open",
         WS_CHILD | WS_VISIBLE |
         BS_PUSHBUTTON,
         50, 0, 50, 20,
         hwnd, (HMENU) ID_OPEN, hInst, NULL);

      hButtSave = CreateWindow("button", "Save",
         WS_CHILD | WS_VISIBLE |
         BS_PUSHBUTTON,
         100, 0, 50, 20,
         hwnd, (HMENU) ID_SAVE, hInst, NULL);

      hButtExit = CreateWindow("button", "Exit",
         WS_CHILD | WS_VISIBLE |
         BS_PUSHBUTTON,
         150, 0, 50, 20,
         hwnd, (HMENU) ID_EXIT, hInst, NULL);

      return 0;
    }

    case WM_SIZE:
    {
      // Устанавливаем размер органа управления
      // (текстового редактора) в соответствии
      // с размерами главного окна приложения
      MoveWindow(hEdit, 0, 20, LOWORD(lParam),
        HIWORD(lParam) - 20, TRUE);
      return 0;
    }

    // Когда главное окно приложения получает
    // фокус ввода, отдаем фокус редактору текста
    case WM_SETFOCUS:
    {
      SetFocus(hEdit);
      return 0;
    }

    case WM_COMMAND:
    {
      // Обработка извещений текстового редактора
      if(wParam == ID_EDIT)
      {
        // Ошибка
        if(HIWORD(lParam) == EN_ERRSPACE)
        {
           MessageBox(hwnd, "Мало памяти",
             szWindowTitle, MB_OK);
        }

        // Произошло изменение в редактируемом
        // тексте
        else if(HIWORD(lParam) == EN_UPDATE)
        {
           // Устанавливаем флаг обновления текста
           bUpdate = TRUE;
        }
        return 0;
      }

      // Нажата кнопка сохранения текста
      else if(wParam == ID_SAVE)
      {
        WORD wSize;
        HANDLE hTxtBuf;
        NPSTR  npTextBuffer;

        // Открываем выходной файл
        hfDstFile = OpenSaveFile();
        if(!hfDstFile) return 0;

        // Определяем размер текста
        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;
        }

        // Сбрасываем содержимое текстового редактора
        SetWindowText(hEdit, "\0");

        return 0;
      }

      // Загрузка файла для редактирования
      else if(wParam == ID_OPEN)
      {
        LPSTR lpTextBuffer;
        DWORD dwFileSize, dwCurrentPos;

        // Проверяем флаг обновления
        if(bUpdate)
        {
          if(IDYES == MessageBox(hwnd,
           "Файл был изменен. Желаете сохранить?",
           szWindowTitle, MB_YESNO | MB_ICONQUESTION))
             return 0;
        }

        // Открываем входной файл.
        hfSrcFile = OpenFile();
        if(!hfSrcFile) return 0;

        // Определяем размер файла
        dwCurrentPos = _llseek(hfSrcFile, 0L, 1);
        dwFileSize   = _llseek(hfSrcFile, 0L, 2);
        _llseek(hfSrcFile, dwCurrentPos, 0);

        // Размер файла не должен превосходить 32000 байт
        if(dwFileSize >= 32000)
        {
          _lclose(hfSrcFile);
          MessageBox(hwnd, "Размер файла больше 32000 байт",
             szWindowTitle, MB_OK);
          return 0;
        }

        // Заказываем память для загрузки файла
        lpTextBuffer = (LPSTR)malloc(32000);
        if(lpTextBuffer == NULL)
           return 0;

        // Загружаем текст из файла в буфер
        _lread(hfSrcFile, lpTextBuffer, dwFileSize);

        // Закрываем буфер двоичным нулем
        lpTextBuffer[(WORD)dwFileSize] = '\0';

        // Закрываем файл
        _lclose(hfSrcFile);

        // Переносим содержимое буфера в
        // текстовый редактор
        SetWindowText(hEdit, lpTextBuffer);

        // Освобождаем буфер
        free((void *)lpTextBuffer);

        // сбрасываем флаг обновления
        bUpdate = FALSE;
      }

      else if(wParam == ID_EXIT)
      {
        // Проверяем флаг обновления
        if(bUpdate)
        {
          if(IDYES == MessageBox(hwnd,
           "Файл был изменен. Желаете сохранить?",
           szWindowTitle, MB_YESNO | MB_ICONQUESTION))
             return 0;
        }

        // Посылаем в функцию главного окна
        // сообщение WM_CLOSE
        SendMessage(hwnd, WM_CLOSE, 0, 0L);
        return 0;
      }
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// -------------------------------
// Функция OpenFile
// Сохранение файла
// -------------------------------

HFILE OpenFile(void)
{
  // Структура для выбора файла
  OPENFILENAME ofn;

  // Буфер для записи пути к выбранному файлу
  char szFile[256];

  // Буфер для записи имени выбранного файла
  char szFileTitle[256];

  // Фильтр расширений имени файлов
  char szFilter[256] =
         "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";

  // Идентификатор открываемого файла
  HFILE hf;

  // Инициализация имени выбираемого файла
  // не нужна, поэтому создаем пустую строку
  szFile[0] = '\0';

  // Записываем нулевые значения во все поля
  // структуры, которая будет использована для
  // выбора файла
  memset(&ofn, 0, sizeof(OPENFILENAME));

  // Инициализируем нужные нам поля

  // Размер структуры
  ofn.lStructSize       = sizeof(OPENFILENAME);

  // Идентификатор окна
  ofn.hwndOwner         = NULL;

  // Адрес строки фильтра
  ofn.lpstrFilter       = szFilter;

  // Номер позиции выбора
  ofn.nFilterIndex      = 1;

  // Адрес буфера для записи пути
  // выбранного файла
  ofn.lpstrFile         = szFile;

  // Размер буфера для записи пути
  // выбранного файла
  ofn.nMaxFile          = sizeof(szFile);

  // Адрес буфера для записи имени
  // выбранного файла
  ofn.lpstrFileTitle    = szFileTitle;

  // Размер буфера для записи имени
  // выбранного файла
  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  // В качестве начального каталога для
  // поиска выбираем текущий каталог
  ofn.lpstrInitialDir   = NULL;

  // Определяем режимы выбора файла
  ofn.Flags =   OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
          | OFN_HIDEREADONLY;
  // Выбираем входной файл
  if (GetOpenFileName(&ofn)) {

    // Открываем выбранный файл
    hf = _lopen(ofn.lpstrFile, OF_READ);

    // Возвращаем идентификатор файла
    return hf;
  }
  // При отказе от выбора возвращаем
  // нулевое значение
  else return 0;
}

// -------------------------------
// Функция OpenSaveFile
// Выбор файла для редактирования
// -------------------------------

HFILE OpenSaveFile(void)
{
  OPENFILENAME ofn;

  char szFile[256];
  char szFileTitle[256];
  char szFilter[256] =
         "Text Files\0*.txt\0Any Files\0*.*\0";

  HFILE hf;

  szFile[0] = '\0';

  memset(&ofn, 0, sizeof(OPENFILENAME));

  ofn.lStructSize       = sizeof(OPENFILENAME);
  ofn.hwndOwner         = NULL;
  ofn.lpstrFilter       = szFilter;
  ofn.nFilterIndex      = 1;
  ofn.lpstrFile         = szFile;
  ofn.nMaxFile          = sizeof(szFile);
  ofn.lpstrFileTitle    = szFileTitle;
  ofn.nMaxFileTitle     = sizeof(szFileTitle);
  ofn.lpstrInitialDir   = NULL;
  ofn.Flags             = OFN_HIDEREADONLY;

  // Выбираем выходной файл
  if (GetSaveFileName(&ofn)) {

    // При необходимости создаем файл
    hf = _lcreat(ofn.lpstrFile, 0);
    return hf;
  }
  else return 0;
}

Функция WinMain приложения создает главное окно и запускает цикл обработки сообщений. В этом цикле вызывается функция TranslateMessage, необходимая для получения символьных сообщений.

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

По сообщению WM_CREATE создается редактор текста:

hEdit = CreateWindow("edit", NULL,
   WS_CHILD   | WS_VISIBLE | WS_BORDER |
   WS_HSCROLL | WS_VSCROLL |
   ES_LEFT    | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
   ES_MULTILINE,
   0, 0, 0, 0,
   hwnd, (HMENU) ID_EDIT, hInst, NULL);

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

Пусть вас не смущает, что мы указали нулевые значения для координат и размеров редактора текста. Размеры редактора текста зависят от размеров главного окна приложения, поэтому они будут установлены при обработке сообщения WM_SIZE.

Далее обработчик сообщения WM_CREATE устанавливает максимальную длину редактируемого текста, для чего посылает редактору сообщение EM_LIMITTEXT:

SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

После этого сбрасывается флаг обновления текста:

bUpdate = FALSE;

Этот флаг будет устанавливаться при внесении в текст любых изменений и проверяться перед созданием или загрузкой нового текста, а также перед завершением работы приложения.

Далее в верхней части главного окна приложения создаются четыре кнопки: "New", "Open", "Save" и "Exit".

Во время обработки сообщения WM_SIZE (поступающего в функцию окна при создании окна и при изменении его размера) устанавливаются правильные размеры и расположение редактора текста:

case WM_SIZE:
{
  MoveWindow(hEdit, 0, 20, LOWORD(lParam),
    HIWORD(lParam) - 20, TRUE);
  return 0;
}

Редактор текста располагается на 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, функция определяет идентификатор блока памяти, используемой редактором для хранения текста:

hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

Управление памятью в 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);

Далее файл читается в буфер, после чего буфер закрывается двоичным нулем:

_lread(hfSrcFile, lpTextBuffer, dwFileSize);
lpTextBuffer[(WORD)dwFileSize] = '\0';

Для переноса текста из буфера в редактор вызывается функция SetWindowText:

SetWindowText(hEdit, lpTextBuffer);

После этого буфер можно освободить, вызвав функцию free.

При завершении работы приложения с помощью кнопки "Exit" после проверки флага обновления в функцию главного окна приложения посылается сообщение WM_CLOSE:

SendMessage(hwnd, WM_CLOSE, 0, 0L);

Файл определения модуля приложения TEDIT приведен в листинге 2.25.


Листинг 2.25. Файл tedit\tedit.def


; =============================
; Файл определения модуля
; =============================
NAME TEDIT
DESCRIPTION 'Приложение TEDIT, (C) 1994, Frolov A.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 5120
HEAPSIZE 1024
CODE preload moveable discardable
DATA preload moveable multiple

[Назад] [Содеожание] [Дальше]