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

         

Файл string\string.rc


#include "string.hpp" /* Таблица строк */ STRINGTABLE BEGIN IDS_APPNAME, "String" IDS_WINDOWNAME, "String Application" IDS_MESSAGE, "%s Demo, (C) Frolov A.V., %d" END

Как и для любого приложения Windows, в проект включается файл определения модуля (листинг 1.4).



Файл string\string.def


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

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

cb = LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName));

Обратите внимание на последнюю строку в таблице строк:

IDS_MESSAGE, "%s Demo, (C) Frolov A.V., %d"

Эта строка содержит описание формата для функции wsprintf. После загрузки строки формата приложение передает ее адрес функции wsprintf, которая выполняет окончательное формирование сообщения, включая в него еще одну текстовую строку (также загруженную из таблицы строк) и число:

wsprintf(szBuf, szMsg, (LPSTR)szAppName, 1994);

Сообщение, которое будет выведено на экран приложением STRING, показано на рис. 1.2.

Рис. 1.2. Сообщение приложения STRING

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



Файл ico\ico.cpp


// ---------------------------------------- // Изображение пиктограмм // ---------------------------------------- #define STRICT #include <windows.h>
#include <mem.h>
#include "ico.hpp"



void Error(void);
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[80]; char szWindowTitle[80]; HINSTANCE hInst;

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

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

hInst = hInstance;

// Загружаем текстовые строки cb = LoadString(hInstance, IDS_CLASSNAME, szClassName, sizeof(szClassName));

if(!cb) { Error();
return -1; }

cb = LoadString(hInstance, IDS_WINDOWTITLE, szWindowTitle, sizeof(szWindowTitle));

if(!cb) { Error();
return -1; }

if(!InitApp(hInstance)) return FALSE;

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)) { DispatchMessage(&msg);
} return msg.wParam; }

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

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

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

// Загружаем пиктограмму, которая будет использована // для отображения главного окна в свернутом виде wc.hIcon = LoadIcon(hInstance, "AppIcon");


wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; 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);
}

// -------------------------------------------- // Функция обработки ошибки загрузки ресурса // -------------------------------------------- void Error(void) { MessageBox(NULL, "Ошибка при загрузке ресурса", "Error", MB_OK | MB_ICONSTOP);
}

Функция WinMain сохраняет идентификатор приложения в глобальной переменной hInst. Содержимое этой переменной будет впоследствии использовано для загрузки ресурсов из exe-файла приложения.

Далее функция WinMain загружает из ресурсов приложения строки с идентификаторами IDS_CLASSNAME (имя класса для главного окна приложения) и IDS_WINDOWTITLE (заголовок главного окна приложения).

После этого вызывается функция InitApp, выполняющая инициализацию приложения, заключающуюся в регистрации класса главного окна. Функция не имеет никаких особенностей, за исключением загрузки пиктограммы с идентификатором AppIcon:

wc.hIcon = LoadIcon(hInstance, "AppIcon");

В главный файл приложения и файл описания ресурсов включается файл ico.hpp (листинг 1.6), в котором определены символические константы для идентификации строк таблицы STRINGTABLE.


Файл ico\ico.hpp


#define IDS_CLASSNAME 1 #define IDS_WINDOWTITLE 2

Файл описания ресурсов приведен в листинге 1.7.



Файл ico\ico.rc


#include "ico.hpp"

/* Таблица строк */ STRINGTABLE BEGIN IDS_CLASSNAME, "ICOAppClass" IDS_WINDOWTITLE, "ICO Demo" END

/* Пиктограмма */ AppIcon ICON ico.ico

В этом файле наряду с оператором STRINGTABLE, описывающим таблицу строк, имеется оператор ICON, ссылающийся на файл пиктограммы ico.ico (листинг 1.8).



Файл ico\ico.ico


Основная задача функции главного окна приложения (листинг 1.9) - вывод в окно встроенных пиктограмм и пиктограммы ico.ico, загруженной из ресурсов приложения (рис. 1.10).

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



Файл ico\wndproc.cpp


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

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

extern HINSTANCE hInst;

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования

// Размеры пиктограммы static int xIcon, yIcon;

// Идентификатор пиктограммы HICON hIcon;

switch (msg) { case WM_CREATE: { // Определяем размеры пиктограммы xIcon = GetSystemMetrics(SM_CXICON);
yIcon = GetSystemMetrics(SM_CYICON);
break; }

case WM_PAINT: { hdc = BeginPaint(hwnd, &ps);

// Загружаем и рисуем встроенные пиктограммы hIcon = LoadIcon(0, IDI_APPLICATION);
DrawIcon(hdc, xIcon/2, yIcon/2, hIcon);

hIcon = LoadIcon(0, IDI_ASTERISK);
DrawIcon(hdc, xIcon/2 + xIcon*2, yIcon/2, hIcon);

hIcon = LoadIcon(0, IDI_EXCLAMATION);
DrawIcon(hdc, xIcon/2 + xIcon*4, yIcon/2, hIcon);

hIcon = LoadIcon(0, IDI_HAND);
DrawIcon(hdc, xIcon/2 + xIcon*6, yIcon/2, hIcon);

hIcon = LoadIcon(0, IDI_QUESTION);
DrawIcon(hdc, xIcon/2 + xIcon*8, yIcon/2, hIcon);

// Загружаем и рисуем свою пиктограмму, // указанную в файле описания ресурсов hIcon = LoadIcon(hInst, "AppIcon");
DrawIcon(hdc, xIcon/2, yIcon/2 + yIcon*2, hIcon);

EndPaint(hwnd, &ps);
return 0; }

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

При создании окна в функцию окна поступает сообщение WM_CREATE, по которому определяются размеры пиктограммы:

xIcon = GetSystemMetrics(SM_CXICON);
yIcon = GetSystemMetrics(SM_CYICON);

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

При обработке сообщения WM_PAINT функция окна загружает и рисует пиктограммы. Для загрузки пиктограмм используется функция LoadIcon, для рисования - DrawIcon.

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



Файл ico\ico.def


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



Файл cursor\cursor.cpp


// ---------------------------------------- // Изображение курсоров // ----------------------------------------

#define STRICT #include <windows.h>
#include <mem.h>
#include "cursor.hpp"

void Error(void);
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[80]; char szWindowTitle[80];

HINSTANCE hInst;

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

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

hInst = hInstance;

// Загружаем текстовые строки cb = LoadString(hInstance, IDS_CLASSNAME, szClassName, sizeof(szClassName));
if(!cb) { Error();
return -1; }

cb = LoadString(hInstance, IDS_WINDOWTITLE, szWindowTitle, sizeof(szWindowTitle));
if(!cb) { Error();
return -1; }

if(!InitApp(hInstance)) return FALSE;

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)) { DispatchMessage(&msg);
} return msg.wParam; }

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

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

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

// Загружаем пиктограмму, которая будет использована // для отображения главного окна в свернутом виде wc.hIcon = LoadIcon(hInstance, "AppIcon");


// Загружаем курсор для главного окна wc.hCursor = LoadCursor(hInst, "AppCursor");

wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = (LPSTR)NULL; wc.lpszClassName = (LPSTR)szClassName;

aWndClass = RegisterClass(&wc);
return (aWndClass != 0);
}

// -------------------------------------------- // Функция обработки ошибки загрузки ресурса // -------------------------------------------- void Error(void) { MessageBox(NULL, "Ошибка при загрузке ресурса", "Error", MB_OK | MB_ICONSTOP);
}

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

wc.hIcon = LoadIcon(hInstance, "AppIcon");
wc.hCursor = LoadCursor(hInst, "AppCursor");

Идентификаторы строк определены в файле cursor.hpp (листинг 1.12).


Файл cursor\cursor.hpp


#define IDS_CLASSNAME 1 #define IDS_WINDOWTITLE 2

Файл ресурсов (листинг 1.13) содержит таблицу текстовых строк, ссылку на файлы пиктограммы и курсора.



Файл cursor\cursor.rc


#include "cursor.hpp"

/* Таблица строк */ STRINGTABLE BEGIN IDS_CLASSNAME, "CURSORAppClass" IDS_WINDOWTITLE, "CURSOR Demo" END

/* Пиктограмма */ AppIcon ICON cursor.ico

/* Курсор */ AppCursor CURSOR cursor.cur

В листинге 1.14 приведено изображение пиктограммы, указанной при регистрации класса главного окна приложения CURSOR.



Файл cursor\cursor.ico


Когда курсор мыши находится внутри главного окна приложения CURSOR, он принимает форму, представленную в листинге 1.15.



Файл cursor\cursor.cur


Функция главного окна приложения (листинг 1.16) рисует в окне встроенные курсоры и курсор, определенный в файле описания ресурсов.



Файл cursor\wndproc.cpp


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

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

extern HINSTANCE hInst;

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования

// Размеры курсора static int xCursor, yCursor;

// Идентификатор курсора HCURSOR hCursor;

switch (msg) { case WM_CREATE: { // Определяем размеры курсора xCursor = GetSystemMetrics(SM_CXCURSOR);
yCursor = GetSystemMetrics(SM_CYCURSOR);
break; }

case WM_PAINT: { hdc = BeginPaint(hwnd, &ps);

// Загружаем и рисуем встроенные курсоры hCursor = LoadCursor(0, IDC_ARROW);
DrawIcon(hdc, xCursor/2, yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_IBEAM);
DrawIcon(hdc, xCursor/2 + xCursor, yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_WAIT);
DrawIcon(hdc,xCursor/2 + xCursor*2,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_CROSS);
DrawIcon(hdc,xCursor/2 + xCursor*3,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_UPARROW);
DrawIcon(hdc,xCursor/2 + xCursor*4,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_SIZE);
DrawIcon(hdc,xCursor/2 + xCursor*5,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_ICON);
DrawIcon(hdc,xCursor/2 + xCursor*6,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_SIZENWSE);
DrawIcon(hdc,xCursor/2 + xCursor*7,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_SIZENESW);
DrawIcon(hdc,xCursor/2 + xCursor*8,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_SIZEWE);
DrawIcon(hdc,xCursor/2 + xCursor*9,yCursor/2, hCursor);

hCursor = LoadCursor(0, IDC_SIZENS);
DrawIcon(hdc,xCursor/2 + xCursor*10,yCursor/2,hCursor);

// Загружаем и рисуем свой курсор, // указанный в файле описания ресурсов hCursor = LoadCursor(hInst, "AppCursor");
DrawIcon(hdc,xCursor/2, yCursor/2 + yCursor*2,hCursor);

EndPaint(hwnd, &ps);
return 0; }

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

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

hCursor = LoadCursor(0, IDC_ARROW);

Затем функция окна рисует курсор при помощи функции DrawIcon, передавая ей в качестве второго параметра полученный идентификатор курсора.

DrawIcon(hdc, xCursor/2, yCursor/2, hCursor);

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

case WM_CREATE: { xCursor = GetSystemMetrics(SM_CXCURSOR);
yCursor = GetSystemMetrics(SM_CYCURSOR);
break; }

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



Файл cursor\cursor.def


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



Файл brush\brush.cpp


// ---------------------------------------- // Использование кисти для закрашивания // внутренней области окна // ----------------------------------------

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

// Прототипы функций

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

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

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

// Идентификатор изображения bitmap HBITMAP hBmp;

// Идентификатор кисти HBRUSH hBrush;

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

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

// Загружаем изображение bitmap из ресурсов hBmp = LoadBitmap(hInstance, "#123");

// Создаем кисть для закрашивания окна hBrush = CreatePatternBrush(hBmp);

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

// После успешной инициализации приложения создаем // главное окно приложения 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)) { DispatchMessage(&msg);
}

// Перед завершением работы приложения // уничтожаем созданные нами изображение и кисть DeleteObject(hBmp);
DeleteObject(hBrush);

return msg.wParam; }

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


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

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

// Закраска внутренней области окна wc.hbrBackground = hBrush;

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.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) { switch (msg) { case WM_DESTROY: { PostQuitMessage(0);
return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam);
}

Перед регистрацией класса главного окна приложения функция WinMain загружает изображение bitmap, вызывая функцию LoadBitmap:

hBmp = LoadBitmap(hInstance, "#123");

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

Получив идентификатор изображения, функция WinMain создает кисть, для чего она вызывает функцию CreatePatternBrush:

hBrush = CreatePatternBrush(hBmp);

При заполнении структуры для регистрации класса окна в поле hbrBackground записывается идентификатор созданной кисти:

wc.hbrBackground = hBrush;

Перед завершением работы приложение уничтожает изображение и кисть, вызывая функцию DeleteObject:

DeleteObject(hBmp);
DeleteObject(hBrush);

Файл ресурсов приведен в листинге 1.19.


Файл brush\brush.rc


/* Изображение типа bitmap */ 123 BITMAP brush.bmp

В этом файле описан один ресурс - изображение типа bitmap с идентификатором 123. При загрузке ресурса используется ссылка на этот идентификатор как на строку символов, для чего в качестве второго параметра функции LoadBitmap передается строка "#123". Можно также использовать ссылку с использованием макроса MAKEINTRESOURCE:

hBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(123));

Файл brush.bmp (листинг 1.20) был создан при помощи Resource Workshop. Еще раз обратим ваше внимание на то, что размер изображения для создания кисти должен быть 8х8 точек.



Файл brush\brush.bmp


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



Файл brush\brush.def


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



Файл oem3ansi\oem3ansi.cpp


// ---------------------------------------- // Перекодировка текстового файла // из OEM в ANSI с использованием // дополнительной таблицы перекодировки // ----------------------------------------

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

// Прототипы функций HFILE GetSrcFile(void);
HFILE GetDstFile(void);
void Oem3Ansi(HFILE, HFILE);

// Указатель на таблицу перекодировки, // которая будет загружена из ресурсов char far * lpXlatTable;

// ------------------------------- // Функция WinMain // -------------------------------

#pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HFILE hfSrc, hfDst;

// Положение ресурса в файле HRSRC hResource;

// Идентификатор таблицы перекодировки HGLOBAL hXlatTable;

// Определяем расположение ресурса hResource = FindResource(hInstance, "XlatTable", "XLAT");

// Получаем идентификатор ресурса hXlatTable = LoadResource(hInstance, hResource);

// Фиксируем ресурс в памяти, получая его адрес lpXlatTable = (char far *)LockResource(hXlatTable);

// Если адрес равен NULL, при загрузке или // фиксации ресурса произошла ошибка if(lpXlatTable == NULL) { MessageBox(NULL, "Error", "Resource loading error", MB_OK);
return(-1);
}

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

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

// Выполняем перекодировку файла Oem3Ansi(hfSrc, hfDst);

// Закрываем входной и выходной файлы _lclose(hfSrc);
_lclose(hfDst);

UnlockResource(hXlatTable);
FreeResource(hXlatTable);

return 0; }

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

HFILE GetSrcFile(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; }

// ------------------------------- // Функция GetDstFile // Выбор файла для записи // результата перекодировки // -------------------------------

HFILE GetDstFile(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_HIDEREADONLY;

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

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

// ------------------------------- // Функция Oem3Ansi // Перекодировка файла // -------------------------------

void Oem3Ansi(HFILE hfSrcFile, HFILE hfDstFile) { // Счетчик прочитанных байт int cbRead;

// Буфер для считанных данных BYTE bBuf[2048];

// Читаем в цикле файл и перекодируем его, // записывая результат в другой файл do { // Читаем в буфер 2048 байт из входного файла cbRead = _lread(hfSrcFile, bBuf, 2048);

// Выполняем дополнительную перекодировку // по таблице, загруженной из ресурсов for(int i=0;i < cbRead; i++) { bBuf[i] = lpXlatTable[bBuf[i]]; }

// Перекодируем содержимое буфера // из OEM в ANSI OemToAnsiBuff(bBuf, bBuf, cbRead);



// Сохраняем содержимое буфера в // выходном файле _lwrite(hfDstFile, bBuf, cbRead);

// Завершаем цикл по концу входного файла } while (cbRead != 0);
}

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

hResource = FindResource(hInstance, "XlatTable", "XLAT");
hXlatTable = LoadResource(hInstance, hResource);

После этого ресурс фиксируется в памяти:

lpXlatTable = (char far *)LockResource(hXlatTable);

Адрес зафиксированного ресурса (таблицы перекодировки) записывается в глобальную переменную lpXlatTable. Если этот адрес равен значению NULL, выводится сообщение об ошибке и работа приложения завершается.

После этого приложение OEM3ANSI открывает входной и выходной файлы и выполняет перекодировку, вызывая функцию Oem3Ansi.

Перед завершением работы приложение расфиксирует и удаляет ресурс из памяти:

UnlockResource(hXlatTable);
FreeResource(hXlatTable);

Функция Oem3Ansi выполняет те же действия, что и функция Oem2Ansi из приложения OEM2ANSI, рассмотренного в предыдущем томе. Дополнительно перед перекодировкой из OEM в ANSI (которую выполняет функция OemToAnsiBuff из программного интерфейса Windows) функция Oem3Ansi в цикле перекодирует прочитанный из файла буфер, пользуясь дополнительной таблицей перекодировки, загруженной из ресурсов:

for(int i=0;i < cbRead; i++) { bBuf[i] = lpXlatTable[bBuf[i]]; } OemToAnsiBuff(bBuf, bBuf, cbRead);

Файл описания ресурсов приложения OEM3ANSI (листинг 1.23) содержит описание единственного ресурса. Тип этого ресурса мы определили сами как XLAT.


Файл oem3ansi\oem3ansi.rc


/* Таблица перекодировки */ XlatTable XLAT xlatcyr.tbl

Файл описания ресурсов ссылается на таблицу перекодировки (листинг 1.24), длина которой составляет 256 байт.



Файл oem3ansi\xlatcyr.tbl


В этой таблице для каждого возможного значения байта находится соответствующая замена. Байты со значениями от 0x00 до 0x40 при перекодировке остаются без изменения. Вместо заглавной латинской буквы "A" с кодом 0x41 в таблице стоит значение 0x80, соответствующее (в кодировке OEM) русской заглавной букве "А". Аналогичная замена выполняется и для других латинских букв, имеющих сходное с русскими буквами начертание.

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



Файл oem3ansi\oem3ansi.def


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

Для облегчения процедуры подготовки таблицы перекодировки вы можете использовать программу tabgen.exe, исходный текст которой приведен в листинге 1.26.



Файл oem3ansi\tabgen.cpp


#include <stdio.h>
#include <stdlib.h>

int main (void) { FILE *tfile;

tfile=fopen("xlat.tbl","w+b");
if(tfile == NULL) { printf("File creation error!");
exit(1);
}

for(unsigned int i=0; i < 256; i++) fputc((char)i, tfile);

fclose(tfile);
return 0; }

Эта программа создает в текущем каталоге файл с именем xlat.tbl, состоящий из 256 байт с последовательно возрастающими от 0 до 255 значениями. При использовании такого файла приложение OEM3ANSI не выполняет никакой дополнительной перекодировки, однако вы можете отредактировать этот файл, например, при помощи редактора diskedit.exe из пакета утилит Нортона.

Содержимое таблицы перекодировки можно изменять и после сборки проекта. Для этого надо воспользоваться приложением Resource Workshop.

Запустите Resource Workshop и из меню "File" выберите строку "Open Project". В меню "File type" выберите строку "EXE application" и откройте файл oem3ansi.exe. На экране появится окно с заголовком "oem3ansi.exe", в котором находится список имеющихся в файле приложения ресурсов. Вы увидите там строку "XLATTABLE". Это идентификатор ресурса, определенный в файле описания ресурсов приложения. Сделайте по нему двойной щелчок левой клавишей мыши. Появится окно редактирования "XLAT:XLATTABLE", в котором вы сможете изменить значение отдельных байт таблицы. После редактирования сохраните изменения, выбрав из меню "File" строку "Save project" и завершите приложение Resource Workshop.



Файл button\button.cpp


// ---------------------------------------- // Стандартные кнопки // ---------------------------------------- #define STRICT #include <windows.h>
#include <mem.h>

// Идентификаторы кнопок #define IDB_Button1 1 #define IDB_Button2 2

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

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

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

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

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

// Идентификаторы кнопок HWND hButton1, hButton2;

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

// После успешной инициализации приложения создаем // главное окно приложения 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);

// Создаем первую кнопку hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 20, 90, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);

// Создаем вторую кнопку hButton2 = CreateWindow("button", "Button 2", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 60, 90, 30, hwnd, (HMENU) IDB_Button2, hInstance, NULL);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { // Сообщение приходит, когда вы нажимаете // на одну из двух созданных кнопок case WM_COMMAND: { // Если нажата первая кнопка, выводим // сообщение if(wParam == IDB_Button1) { MessageBox(hwnd, "Нажата кнопка Button 1", "Message WM_COMMAND",MB_OK);
} // Если нажата вторая кнопка, выводим // другое сообщение else if(wParam == IDB_Button2) { MessageBox(hwnd, "Нажата кнопка Button 2", "Message WM_COMMAND",MB_OK);
} return 0; }

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

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


Файл button\button.def


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

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

#define IDB_Button1 1 #define IDB_Button2 2

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

После создания и вывода на экран главного окна приложения функция WinMain создает кнопки, вызывая функцию CreateWindow:

hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 20, 90, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);

Для первой кнопки указывается предопределенный класс окна 'button", заголовок "Button 1", стиль кнопки BS_PUSHBUTTON, расположение, размеры, а также идентификатор кнопки IDB_Button1.

Вторая кнопка создается аналогично. Она имеет те же размеры, но расположена ниже, имеет заголовок "Button 2" и идентификатор IDB_Button2.

В функции окна добавился обработчик сообщения WM_COMMAND. Это сообщение поступает в функцию окна, когда вы нажимаете любую из двух созданных кнопок. Обработчик анализирует идентификатор кнопки, передаваемый вместе с сообщением в параметре wParam, и выводит соответствующее сообщение:

case WM_COMMAND: { if(wParam == IDB_Button1) { MessageBox(hwnd, "Нажата кнопка Button 1", "Message WM_COMMAND",MB_OK);
} else if(wParam == IDB_Button2) { MessageBox(hwnd, "Нажата кнопка Button 2", "Message WM_COMMAND",MB_OK);
} return 0; }



Файл butnctl\butnctl.cpp


// ---------------------------------------- // Управление стандартной кнопкой // ----------------------------------------

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

// Идентификаторы кнопок #define IDB_Button1 1 #define IDB_Button2 2 #define IDB_Button3 3 #define IDB_Button4 4 #define IDB_Button5 5

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

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

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

// Идентификаторы кнопок HWND hButton1, hButton2, hButton3, hButton4, hButton5;

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

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

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

// После успешной инициализации приложения создаем // главное окно приложения 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);

// Создаем пять кнопок hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 20, 90, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);

hButton2 = CreateWindow("button", "Button 2", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 60, 90, 30, hwnd, (HMENU) IDB_Button2, hInstance, NULL);


hButton3 = CreateWindow("button", "Button 3", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 100, 90, 30, hwnd, (HMENU) IDB_Button3, hInstance, NULL);

hButton4 = CreateWindow("button", "Button 4", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 140, 90, 30, hwnd, (HMENU) IDB_Button4, hInstance, NULL);

hButton5 = CreateWindow("button", "Button 5", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 180, 90, 30, hwnd, (HMENU) IDB_Button5, hInstance, NULL);

// Увеличиваем горизонтальный размер // первой кнопки MoveWindow(hButton1, 20, 20, 180, 30, TRUE);

// Изменяем надписи на остальных кнопках SetWindowText(hButton2, "PUSH");
SetWindowText(hButton3, "POP");
SetWindowText(hButton4, "OFF");
SetWindowText(hButton5, "ON");

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { // Сообщение приходит, когда вы нажимаете // на одну из двух созданных кнопок case WM_COMMAND: { // Если нажата первая кнопка, выводим // сообщение if(wParam == IDB_Button1) { MessageBox(hwnd, "Нажата кнопка Button 1", "Message WM_COMMAND",MB_OK);
} // Если нажата вторая кнопка, // переводим первую кнопку в нажатое состояние else if(wParam == IDB_Button2) { SendMessage(hButton1, BM_SETSTATE, TRUE, 0L);
} // Если нажата третья кнопка, // возвращаем первую кнопку в исходное состояние else if(wParam == IDB_Button3) { SendMessage(hButton1, BM_SETSTATE, FALSE, 0L);
} // Если нажата четвертая кнопка, // переводим первую кнопку в неактивное состояние else if(wParam == IDB_Button4) { EnableWindow(hButton1, FALSE);
} // Если нажата пятая кнопка, // переводим первую кнопку в активное состояние else if(wParam == IDB_Button5) { EnableWindow(hButton1, TRUE);
} return 0; }



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

В приложении определены идентификаторы пяти кнопок - от IDB_Button1 до IDB_Button5, а также пять переменных для хранения идентификаторов окон класса 'button".

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

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

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

MoveWindow(hButton1, 20, 20, 180, 30, TRUE);

Для всех остальных кнопок изменяются надписи:

SetWindowText(hButton2, "PUSH");
SetWindowText(hButton3, "POP");
SetWindowText(hButton4, "OFF");
SetWindowText(hButton5, "ON");

Функция окна обрабатывает сообщение WM_COMMAND, которое может поступать от всех пяти кнопок. Кнопки различаются по параметру wParam.

Если вы нажимаете первую кнопку, на экран выводится диалоговая панель с сообщением.

Если нажать на вторую кнопку (с надписью "DOWN"), функция окна передает сообщение первой кнопке, в результате чего она переходит в нажатое состояние:

SendMessage(hButton1, BM_SETSTATE, TRUE, 0L);

Если нажать на кнопку с надписью "POP", возвращается исходное состояние первой кнопки:

SendMessage(hButton1, BM_SETSTATE, FALSE, 0L);

Кнопка с надписью "OFF" предназначена для перевода первой кнопки в неактивное состояние. Для этого вызывается функция EnableWindow со значением второго параметра, равным FALSE:

EnableWindow(hButton1, FALSE);

И, наконец, последняя, пятая кнопка с надписью "ON" снова возвращает первую кнопку в активное состояние:

EnableWindow(hButton1, TRUE);

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


Файл butnctl\butnctl.def


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



Файл ownbut\ownbut.cpp


// ---------------------------------------- // Управление нестандартной кнопкой // ----------------------------------------

#define STRICT #include <windows.h>
#include <mem.h>
#include "ownbut.hpp"

// Прототипы функций BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawButton(LPDRAWITEMSTRUCT);
void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap);

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

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

// Идентификаторы кнопок HWND hButton1, hButton2, hButton3, hButton4, hButton5;

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

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

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

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

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

// После успешной инициализации приложения создаем // главное окно приложения 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);

// Создаем пять кнопок

// Эту кнопку будет рисовать функция // родительского окна hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 20, 20, 180, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);


hButton2 = CreateWindow("button", "PUSH", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 60, 90, 30, hwnd, (HMENU) IDB_Button2, hInstance, NULL);

hButton3 = CreateWindow("button", "POP", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 100, 90, 30, hwnd, (HMENU) IDB_Button3, hInstance, NULL);

hButton4 = CreateWindow("button", "OFF", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 140, 90, 30, hwnd, (HMENU) IDB_Button4, hInstance, NULL);

hButton5 = CreateWindow("button", "ON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 180, 90, 30, hwnd, (HMENU) IDB_Button5, hInstance, NULL);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { // Сообщение приходит, когда вы нажимаете // на одну из двух созданных кнопок case WM_COMMAND: { // Если нажата первая кнопка, выводим // сообщение if(wParam == IDB_Button1) { MessageBox(hwnd, "Нажата кнопка Button 1", "Message WM_COMMAND",MB_OK);
} else if(wParam == IDB_Button2) { SendMessage(hButton1, BM_SETSTATE, TRUE, 0L);
} else if(wParam == IDB_Button3) { SendMessage(hButton1, BM_SETSTATE, FALSE, 0L);
} else if(wParam == IDB_Button4) { EnableWindow(hButton1, FALSE);
} else if(wParam == IDB_Button5) { EnableWindow(hButton1, TRUE);
} return 0; }



// Это сообщение приходит при изменении состояния // дочернего окна органа управления, когда окно // нужно перерисовать case WM_DRAWITEM: { // Перерисовываем кнопку DrawButton( (LPDRAWITEMSTRUCT)lParam );
break; }

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

// ===================================== // Функция DrawButton // Перерисовывает кнопку // =====================================

void DrawButton(LPDRAWITEMSTRUCT lpInfo) { HBITMAP hbm; int ResourceID;

// Обрабатываем сообщение WM_DRAWITEM // только если оно поступило от кнопки if(lpInfo->
CtlType != ODT_BUTTON) return;

// Так как в приложении может быть несколько // кнопок, посылающих сообщение WM_DRAWITEM, // проверяем идентификатор кнопки switch (lpInfo->
CtlID) { case IDB_Button1: { // Загружаем идентификатор изображения // кнопки в нормальном (отжатом) состоянии ResourceID = IDR_BUTTONUP; break; } default: return; }

// Если кнопка выбрана, рисуем ее в нажатом // состоянии if (lpInfo->
itemState & ODS_SELECTED) { ResourceID = IDR_BUTTONDOWN; }

// если кнопка неактивна, загружаем идентификатор // изображения кнопки в неактивном состоянии else if (lpInfo->
itemState & ODS_DISABLED) { ResourceID = IDR_BUTTONGR; }

// Загружаем изображение кнопки из ресурсов приложения hbm = LoadBitmap(hInst, MAKEINTRESOURCE(ResourceID));

// При ошибке ничего не рисуем if(!hbm) return;

// Если кнопка выбрана и ее надо целиком // перерисовать, вызываем функцию DrawBitmap if((lpInfo->
itemAction & ODA_DRAWENTIRE) (lpInfo->
itemAction & ODA_SELECT)) { // Рисуем кнопку DrawBitmap(lpInfo->
hDC, (lpInfo->
rcItem).left, (lpInfo->
rcItem).top , hbm);
}

// Удаляем изображение кнопки, так как оно // нам больше не нужно DeleteObject(hbm);
}

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

hInst = hInstance;

Затем она инициализирует главное окно приложения и создает пять кнопок, первая из которых имеет стиль BS_OWNERDRAW:



hButton1 = CreateWindow("button", "Button 1", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 20, 20, 180, 30, hwnd, (HMENU) IDB_Button1, hInstance, NULL);

Функция главного окна приложения OWNBTN кроме сообщения WM_COMMAND дополнительно обрабатывает сообщение WM_DRAWITEM, в ответ на которое она вызывает функцию DrawButton, определенную в нашем приложении:

case WM_DRAWITEM: { DrawButton( (LPDRAWITEMSTRUCT)lParam );
break; }

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

Вначале эта функция проверяет тип органа управления, приславшего сообщение WM_DRAWITEM. Тип должен быть равен ODT_BUTTON.

Затем проверяется идентификатор кнопки. В нашем приложении есть только одна кнопка со стилем BS_OWNERDRAW, но их могло бы быть и несколько. В переменную ResourceID загружается идентификатор ресурса (изображения bitmap), соответствующего изображению кнопки в нормальном (отжатом) состоянии:

switch (lpInfo->
CtlID) { case IDB_Button1: { ResourceID = IDR_BUTTONUP; break; } default: return; }

Далее в зависимости от текущего состояния кнопки на момент прихода сообщения WM_DRAWITEM содержимое переменной ResourceID заменяется на идентификатор соответствующего изображения кнопки.

Для выбранной кнопки (в поле itemState структуры DRAWITEMSTRUCT установлен бит ODS_SELECTED) в переменную ResourceID записывается идентификатор изображения нажатой кнопки IDR_BUTTONDOWN.

Для неактивной кнопки в эту же переменную записывается идентификатор изображения неактивной кнопки IDR_BUTTONGR.

После этого нужное изображение загружается из ресурсов приложения, для чего вызывается функция LoadBitmap:

hbm = LoadBitmap(hInst, MAKEINTRESOURCE(ResourceID));

Далее функция DrawButton проверяет действие, которое нужно выполнить при рисовании кнопки. Если состояние кнопки изменилось (установлен бит ODA_SELECT) и требуется перерисовать всю кнопку (установлен бит ODA_DRAWENTIRE), выполняется вызов функции DrawBitmap, определенной в нашем приложении.



Создавая функцию DrawBitmap, мы сделали так, что количество, назначение и формат ее параметров в точности соответствует функции DrawIcon, с помощью которой мы рисовали пиктограммы (вы можете легко изменить исходный текст приложения OWNBUT для того чтобы для рисования кнопки использовать не изображения bitmap, а пиктограммы; сделайте это самостоятельно). В нашем примере в качестве первого параметра функции передается контекст устройства, полученный в структуре DRAWITEMSTRUCT вместе с сообщением WM_DRAWITEM. Второй и третий параметры используются для определения координат верхнего левого угла кнопки в системе координат, связанной с родительским окном. Последний параметр - идентификатор изображения bitmap, который необходимо нарисовать:

DrawBitmap(lpInfo->
hDC, (lpInfo->
rcItem).left, (lpInfo->
rcItem).top , hbm);

После того как кнопка нарисована, следует удалить bitmap, загруженный функцией LoadBitmap. Для этого наше приложение вызывает функцию DeleteObject:

DeleteObject(hbm);

Идентификаторы кнопок и изображений bitmap определены в файле ownbut.hpp (листинг 2.6).


Файл ownbut\ownbut.hpp


// Идентификаторы кнопок

#define IDB_Button1 1 #define IDB_Button2 2 #define IDB_Button3 3 #define IDB_Button4 4 #define IDB_Button5 5

#define IDR_BUTTONUP 200 #define IDR_BUTTONDOWN 201 #define IDR_BUTTONGR 202

В файле описания ресурсов приложения (листинг 2.7) определены три изображения bitmap, используемые для рисования кнопки в различных состояниях.



Файл ownbut\ownbut.rc


#include "ownbut.hpp" IDR_BUTTONUP BITMAP mybtnup.bmp IDR_BUTTONDOWN BITMAP mybtndn.bmp IDR_BUTTONGR BITMAP mybtngr.bmp

В листинге 2.8 изображен файл mybtnup.bmp. Этот файл содержит объемное изображение кнопки в нормальном (отжатом) состоянии.



Файл ownbut\mybtnup.bmp


Файл mybtndn.bmp (листинг 2.9) хранит изображение кнопки в нажатом состоянии.



Файл ownbut\mybtndn.bmp


Для изображения кнопки в неактивном состоянии используется файл mybtngr.bmp (листинг 2.10).



Файл ownbut\mybtngr.bmp


Для рисования изображения bitmap мы подготовили функцию DrawBitmap (листинг 2.11). Детальное описание этой функции мы отложим до главы, посвященной изображениям bitmap.



Файл ownbut\drawbmp.cpp


// ---------------------------------------- // Рисование изображения типа bitmap // ----------------------------------------

#define STRICT #include <windows.h>

void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap) { HBITMAP hbm, hOldbm; HDC hMemDC; BITMAP bm; POINT ptSize, ptOrg;

// Создаем контекст памяти, совместимый // с контекстом отображения hMemDC = CreateCompatibleDC(hDC);

// Выбираем изображение bitmap в контекст памяти hOldbm = (HBITMAP)SelectObject(hMemDC, hBitmap);

// Если не было ошибок, продолжаем работу if (hOldbm) { // Для контекста памяти устанавливаем тот же // режим отображения, что используется в // контексте отображения SetMapMode(hMemDC, GetMapMode(hDC));

// Определяем размеры изображения GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);

ptSize.x = bm.bmWidth; // ширина ptSize.y = bm.bmHeight; // высота

// Преобразуем координаты устройства в логические // для устройства вывода DPtoLP(hDC, &ptSize, 1);

ptOrg.x = 0; ptOrg.y = 0;

// Преобразуем координаты устройства в логические // для контекста памяти DPtoLP(hMemDC, &ptOrg, 1);

// Рисуем изображение bitmap BitBlt(hDC, x, y, ptSize.x, ptSize.y, hMemDC, ptOrg.x, ptOrg.y, SRCCOPY);

// Восстанавливаем контекст памяти SelectObject(hMemDC, hOldbm);
}

// Удаляем контекст памяти DeleteDC(hMemDC);
}

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



Файл ownbut\ownbut.def


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



Файл static\static.cpp


// ---------------------------------------- // Статические органы управления // ----------------------------------------

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

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

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

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

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

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

// Идентификаторы органов управления HWND hSt1, hSt2, hSt3, hSt4;

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

// После успешной инициализации приложения создаем // главное окно приложения 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);

// Создаем четыре статических // органа управления hSt1 = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_BLACKRECT, 20, 20, 180, 20, hwnd, (HMENU)-1, hInstance, NULL);

hSt2 = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_BLACKFRAME, 20, 50, 180, 20, hwnd, (HMENU)-1, hInstance, NULL);

hSt3 = CreateWindow("static", "", WS_CHILD | WS_VISIBLE | SS_LEFT, 20, 80, 180, 40, hwnd, (HMENU)-1, hInstance, NULL);


hSt4 = CreateWindow("static", "Simple Control", WS_CHILD | WS_VISIBLE | SS_SIMPLE, 20, 130, 180, 40, hwnd, (HMENU)-1, hInstance, NULL);

// Выводим текст в окно, имеющее стиль SS_LEFT. // Этот текст будет выведен в режиме свертки слов SetWindowText(hSt3, (LPSTR) "Этот текст будет выведен" " внутри окна в две строки");

// Выводим текст в окно, имеющее стиль SS_SIMPLE. // Этот текст будет выведен на одной строке, // причем часть строки, которая выходит за границы // окна, будет обрезана SetWindowText(hSt4, (LPSTR) "Этот текст будет выведен" " внутри окна в одну строку и обрезан");

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { case WM_DESTROY: { PostQuitMessage(0);
return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam);
}

После создания и отображения главного окна приложения функция WinMain создает четыре статических органа управления, вызывая функцию CreateWindow. Если создается прямоугольник или рамка, адрес строки текста заголовка окна указывается как NULL:



hSt1 = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_BLACKRECT, 20, 20, 180, 20, hwnd, (HMENU)-1, hInstance, NULL);

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

Если создается окно с текстом, этот текст можно задать как заголовок окна:

hSt4 = CreateWindow("static", "Simple Control", WS_CHILD | WS_VISIBLE | SS_SIMPLE, 20, 130, 180, 40, hwnd, (HMENU)-1, hInstance, NULL);

После создания статических органов управления функция WinMain изменяет текст в третьем и четвертом органе, вызывая функцию SetWindowText:

SetWindowText(hSt3, (LPSTR) "Этот текст будет выведен" " внутри окна в две строки");
SetWindowText(hSt4, (LPSTR) "Этот текст будет выведен" " внутри окна в одну строку и обрезан");

Для последнего органа управления мы намеренно выбрали текст такой длины, чтобы он не поместился в одной строке. Как и следовал ожидать, при выводе этот текст был обрезан (рис. 2.6).

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


Файл static\static.def


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



Файл scroll\scroll.cpp


// ---------------------------------------- // Работа с полосой просмотра // ----------------------------------------

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

// Идентификатор полосы просмотра #define ID_SCROLL 1

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

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

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

// Текущая позиция полосы просмотра int nPosition;

// Идентификатор окна полосы просмотра HWND hScroll;

// Идентификатор статического органа // управления HWND hStatic;

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

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

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

// После успешной инициализации приложения создаем // главное окно приложения 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);

// Создаем полосу просмотра hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 20, 60, 200, 15, hwnd, (HMENU) ID_SCROLL, hInstance, NULL);

// Устанавливаем текущую позицию nPosition = 100;

// Устанавливаем минимальное и максимальное // значения для полосы просмотра SetScrollRange(hScroll, SB_CTL, 1, 200, TRUE);


// Устанавливаем ползунок в середину // полосы просмотра SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

// Создаем статический орган управления в виде // черного прямоугольника. Длина этого // прямоугольника будет определяться текущей // позицией полосы просмотра hStatic = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_BLACKRECT, 20, 40, nPosition, 15, hwnd, (HMENU) -1,hInstance, NULL);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { // Сообщение от горизонтальной полосы просмотра case WM_HSCROLL: { // В зависимости от параметра сообщения // изменяем текущую позицию switch (wParam) { case SB_PAGEDOWN: { nPosition += 10; break; } case SB_LINEDOWN: { nPosition += 1; break; } case SB_PAGEUP: { nPosition -= 10; break; } case SB_LINEUP: { nPosition -= 1; break; } case SB_TOP: { nPosition = 0; break; } case SB_BOTTOM: { nPosition = 200; break; } case SB_THUMBPOSITION: { nPosition = LOWORD (lParam);
break; } case SB_THUMBTRACK: { nPosition = LOWORD (lParam);
break; } default: break; }



// Ограничиваем пределы изменения текущей // позиции значениями от 1 до 200 if(nPosition >
200) nPosition = 200; if(nPosition < 1) nPosition = 1;

// Устанавливаем ползунок полосы просмотра // в соответствии с новым значением // текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

// Устанавливаем новый размер статического // органа управления MoveWindow(hStatic, 20, 40, nPosition, 15, TRUE);

return 0; }

// Обеспечиваем управление полосой просмотра // при помощи клавиатуры case WM_KEYDOWN: { // В зависимости от кода клавиши функция окна // посылает сама себе сообщения, которые // обычно генерируются полосой просмотра switch (wParam) { case VK_HOME: { SendMessage(hwnd, WM_HSCROLL, SB_TOP, 0L);
break; } case VK_END: { SendMessage(hwnd, WM_HSCROLL, SB_BOTTOM, 0L);
break; } case VK_LEFT: { SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, 0L);
break; } case VK_RIGHT: { SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, 0L);
break; } case VK_PRIOR: { SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0L);
break; } case VK_NEXT: { SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L);
break; } } return 0; } case WM_DESTROY: { PostQuitMessage(0);
return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam);
}

После инициализации приложения и создания главного окна приложения функция WinMain создает на базе предопределенного класса "scrollbar" орган управления - полосу просмотра. Для этого она вызывает функцию CreateWindow:

hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 20, 60, 200, 15, hwnd, (HMENU) ID_SCROLL, hInstance, NULL);

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

Далее при помощи функции SetScrollRange функция WinMain задает диапазон полосы просмотра - от 1 до 200:

SetScrollRange(hScroll, SB_CTL, 1, 200, TRUE);

После этого ползунок устанавливается в позицию, соответствующую содержимому переменной nPosition:



SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

Так как ранее в эту переменную было записано значение 100, ползунок будет установлен в середину полосы просмотра.

Затем функция WinMain создает статический орган управления в виде черного прямоугольника, ширина которого равна значению, записанному в переменную nPosition:

hStatic = CreateWindow("static", NULL, WS_CHILD | WS_VISIBLE | SS_BLACKRECT, 20, 40, nPosition, 15, hwnd, (HMENU) -1,hInstance, NULL);

Функция главного окна приложения получает от полосы просмотра сообщения с кодом WM_HSCROLL. Обработчик этого сообщения анализирует параметр wParam, определяя действие, послужившее причиной появления сообщения от полосы просмотра. В зависимости от значения параметра wParam обработчик увеличивает или уменьшает содержимое переменной nPosition. При этом он следит, чтобы это содержимое находилось в диапазоне от 1 до 200.

После этого ползунок устанавливается в новое положение:

SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

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

MoveWindow(hStatic, 20, 40, nPosition, 15, TRUE);

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

Параметр wParam сообщения WM_KEYDOWN содержит код виртуальной клавиши. Этот код анализируется. Если вы нажали, например, клавишу <Home>
, функция окна посылает сама себе сообщение с кодом WM_HSCROLL с параметром wParam, имеющим значение SB_TOP. С этой целью вызывается функция SendMessage:

SendMessage(hwnd, WM_HSCROLL, SB_TOP, 0L);

Обработчик этого сообщения устанавливает начальную позицию, равную нулю:

case SB_TOP: { nPosition = 0; break; }

При этом ползунок будет установлен в крайнее левое положение.

Аналогично обрабатываются сообщения, попадающие в функцию окна, когда вы нажимаете другие клавиши.Для плавного изменения размера статического органа управления вы можете использовать клавиши перемещения курсора по горизонтали. Клавиши <PgUp>
и <PgDn>
обеспечивают скачкообразное изменение размера. И, наконец, для установки минимального и максимального размера вы можете использовать, соответственно, клавиши <Home>
и <End>
.

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


Файл scroll\scroll.def


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



Файл scrlmet\scrlmet.cpp


// ---------------------------------------- // Просмотр метрик шрифта // ----------------------------------------

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

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char const szClassName[] = "ScrlMetAppClass"; char const szWindowTitle[] = "SCRLMET Application";

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

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

if(!InitApp(hInstance)) return FALSE;

hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна

WS_OVERLAPPEDWINDOW | WS_VSCROLL, // стиль окна

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)) { DispatchMessage(&msg);
} return msg.wParam; }

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

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

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

// Определяем стиль класса окна, при // использовании которого окно требует // перерисовки в том случае, если // изменилась его ширина или высота wc.style = CS_HREDRAW | CS_VREDRAW;

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);
}

При создании главного окна в качестве третьего параметра функции CreateWindow передается значение WS_OVERLAPPEDWINDOW| WS_VSCROLL. Благодаря этому в правой части главного окна приложения появляется вертикальная полоса просмотра.

Никаких других особенностей функция WinMain не имеет.

Исходный текст функции главного окна представлен в листинге 2.18.



Файл scrlmet\wndproc.cpp


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

#define STRICT #include <windows.h>
#include <stdio.h>
#include <string.h>

void Print(HDC, int, char *);

static int cxChar, cyChar; static int cxCurrentPosition; static int cyCurrentPosition; static int nScrollPos;

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static WORD cxClient, cyClient;

HDC hdc; // индекс контекста устройства PAINTSTRUCT ps; // структура для рисования static TEXTMETRIC tm; // структура для записи метрик // шрифта

switch (msg) { case WM_CREATE: { // Получаем контекст отображения, // необходимый для определения метрик шрифта hdc = GetDC(hwnd);

// Заполняем структуру информацией // о метрике шрифта, выбранного в // контекст отображения GetTextMetrics(hdc, &tm);

// Запоминаем значение ширины для // самого широкого символа cxChar = tm.tmMaxCharWidth;

// Запоминаем значение высоты букв с // учетом межстрочного интервала cyChar = tm.tmHeight + tm.tmExternalLeading;

// Инициализируем текущую позицию // вывода текста cxCurrentPosition = cxChar; cyCurrentPosition = 0;

// Освобождаем контекст ReleaseDC(hwnd, hdc);

// Начальное значение позиции nScrollPos = 0;

// Задаем диапазон изменения значений SetScrollRange(hwnd, SB_VERT, 0, 20, FALSE);

// Устанавливаем ползунок в начальную позицию SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);

return 0; }

// Определяем размеры внутренней области окна case WM_SIZE: { cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0; }

// Сообщение от вертикальной полосы просмотра case WM_VSCROLL: { switch(wParam) { case SB_TOP: { nScrollPos = 0; break; } case SB_BOTTOM: { nScrollPos = 20; break; } case SB_LINEUP: { nScrollPos -= 1; break; } case SB_LINEDOWN: { nScrollPos += 1; break; } case SB_PAGEUP: { nScrollPos -= cyClient / cyChar; break; } case SB_PAGEDOWN: { nScrollPos += cyClient / cyChar; break; } case SB_THUMBPOSITION: { nScrollPos = LOWORD(lParam);
break; } // Блокируем для того чтобы избежать // мерцания содержимого окна при // перемещении ползунка case SB_THUMBTRACK: { return 0; } default: break; }


// Ограничиваем диапазон изменения значений if(nScrollPos >
20) nScrollPos = 20; if(nScrollPos < 0) nScrollPos = 0;

// Устанавливаем ползунок в новое положение SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);

// Обновляем окно InvalidateRect(hwnd, NULL, TRUE);

return 0; }

case WM_PAINT: { // Инициализируем текущую позицию // вывода текста cxCurrentPosition = cxChar; cyCurrentPosition = 0;

hdc = BeginPaint(hwnd, &ps);

// Выводим параметры шрифта, полученные во // время создания окна при обработке // сообщения WM_CREATE Print(hdc, tm.tmHeight, "tmHeight");
Print(hdc, tm.tmAscent, "tmAscent");
Print(hdc, tm.tmDescent, "tmDescent");
Print(hdc, tm.tmInternalLeading, "tmInternalLeading");
Print(hdc, tm.tmExternalLeading, "tmExternalLeading");
Print(hdc, tm.tmAveCharWidth, "tmAveCharWidth");
Print(hdc, tm.tmMaxCharWidth, "tmMaxCharWidth");
Print(hdc, tm.tmWeight, "tmWeight");
Print(hdc, tm.tmItalic, "tmItalic");
Print(hdc, tm.tmUnderlined, "tmUnderlined");
Print(hdc, tm.tmStruckOut, "tmStruckOut");
Print(hdc, tm.tmFirstChar, "tmFirstChar");
Print(hdc, tm.tmLastChar, "tmLastChar");
Print(hdc, tm.tmDefaultChar, "tmDefaultChar");
Print(hdc, tm.tmBreakChar, "tmBreakChar");
Print(hdc, tm.tmPitchAndFamily, "tmPitchAndFamily");
Print(hdc, tm.tmCharSet, "tmCharSet");
Print(hdc, tm.tmOverhang, "tmOverhang");
Print(hdc, tm.tmDigitizedAspectX,"tmDigitizedAspectX");
Print(hdc, tm.tmDigitizedAspectY,"tmDigitizedAspectY");

EndPaint(hwnd, &ps);
return 0; }

// Обеспечиваем управление полосой просмотра // при помощи клавиатуры case WM_KEYDOWN: { // В зависимости от кода клавиши функция окна // посылает сама себе сообщения, которые // обычно генерируются полосой просмотра switch (wParam) { case VK_HOME: { SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L);
break; } case VK_END: { SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L);
break; } case VK_UP: { SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L);
break; } case VK_DOWN: { SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L);
break; } case VK_PRIOR: { SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L);
break; } case VK_NEXT: { SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L);
break; } } return 0; }



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

// ========================================== // Функция для вывода параметров шрифта // в окно // ==========================================

void Print(HDC hdc, int tmValue, char *str) { char buf[80]; int i, y;

// Вычисляем начальную позицию для вывода y = cyCurrentPosition + cyChar * (1 - nScrollPos);

// Подготавливаем в рабочем буфере // и выводим в окно начиная с текущей // позиции название параметра sprintf(buf, "%s", str);
i = strlen(str);

TextOut(hdc, cxCurrentPosition, y, buf, i);

// Подготавливаем в рабочем буфере // и выводим в текущей строке окна // со смещением значение параметра sprintf(buf, "= %d", tmValue);
i = strlen(buf);

TextOut(hdc, cxCurrentPosition + 12 * cxChar, y, buf, i);

// Увеличиваем текущую позицию по // вертикали на высоту символа cyCurrentPosition += cyChar; }

При создании главного окна обработчик сообщения WM_CREATE получает информацию о метрике шрифта и сохраняет ее в структуре tm. Затем обработчик этого сообщения запоминает высоту и ширину букв системного шрифта и инициализирует текущую позицию для вывода текста. Далее устанавливается начальное значение позиции полосы просмотра, устанавливается диапазон изменения значений и устанавливается ползунок полосы просмотра:

nScrollPos = 0; SetScrollRange(hwnd, SB_VERT, 0, 20, FALSE);
SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);

Так как всего выводится 20 параметров, окно содержит 20 строк. Поэтому устанавливается диапазон полосы просмотра (0, 20).

При изменении размера окна (а также в процессе создания окна) функция окна получает сообщение WM_SIZE, которое используется для определения размеров внутренней области окна:

case WM_SIZE: { cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0; }

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



Небольшое замечание относительно сообщения WM_VSCROLL, для которого значение wParam равно SB_THUMBTRACK.

Напомним, что такое сообщение поступает в окно при передвижении ползунка по полосе просмотра. Мы обрабатывали это сообщение в предыдущем приложении, изменяя ширину статического органа управления синхронно с передвижениями ползунка. Теперь же мы игнорируем это сообщение:

case SB_THUMBTRACK: { return 0; }

Зачем мы так поступаем? Дело в том, что вслед за передвижением ползунка нам надо перерисовать главное окно приложения. А это длительный процесс. В тех случаях, когда невозможно обеспечить большую скорость перерисовки окна, нет смысла обрабатывать сообщение полосы просмотра с кодом SB_THUMBTRACK. Вы можете ограничиться обработкой сообщения с кодом SB_THUMBPOSITION, перерисовывая окно только после окончания процесса перемещения ползунка.

После обновления содержимого переменной nScrollPos функция окна устанавливает ползунок в новое положение и объявляет все окно требующим перерисовки:

SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);
InvalidateRect(hwnd, NULL, TRUE);

Это приведет к тому, что функции главного окна приложения будет передано сообщение WM_PAINT.

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

Функция Print вычисляет начальную позицию по вертикали для вывода текста на основании текущей позиции полосы просмотра:

y = cyCurrentPosition + cyChar * (1 - nScrollPos);

Если значение переменной nScrollPos таково, что начальная позиция по вертикали меньше нуля, текст будет "выводиться" выше внутренней области окна. Строки, выведенные вне внутренней области окна, будут обрезаны.

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


Файл scrlmet\scrlmet.def


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



Файл sizebox\sizebox.cpp


// ---------------------------------------- // Работа с полосой просмотра, // имеющей стиль SBS_SIZEBOX // ----------------------------------------

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

// Идентификатор полосы просмотра #define ID_SCROLL 1

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

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

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

// Идентификатор окна полосы просмотра HWND hScroll;

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

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

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

// После успешной инициализации приложения создаем // главное окно приложения hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна

// стиль окна WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,

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

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

// Создаем полосу просмотра hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_SIZEBOX | SBS_SIZEBOXTOPLEFTALIGN, 30, 30, 0, 0, hwnd, (HMENU) ID_SCROLL, hInstance, NULL);

// Запускаем цикл обработки сообщений while(GetMessage(&msg, 0, 0, 0)) { 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) { switch (msg) { case WM_DESTROY: { PostQuitMessage(0);
return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam);
}

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

WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

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

Далее после инициализации главного окна мы создаем орган управления - полосу просмотра:

hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_SIZEBOX | SBS_SIZEBOXTOPLEFTALIGN, 30, 30, 0, 0, hwnd, (HMENU) ID_SCROLL, hInstance, NULL);

Этот орган управления создается в главном окне приложения в точке с координатами (30, 30). Размер органа управления не указан, так как стиль SBS_SIZEBOXTOPLEFTALIGN предполагает использование предопределенных размеров.

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

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


Файл sizebox\sizebox.def


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



Файл 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.


Файл 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.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.


Файл 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



Файл listbox\listbox.cpp


// ---------------------------------------- // Использование органа управления // класса "listbox" // ----------------------------------------

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

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

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

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

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

// Заголовок окна char const szWindowTitle[] = "ListBox 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) { // Идентификатор органа управления "listbox" static HWND hListBox;

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

switch (msg) { case WM_CREATE: { // Создаем список hListBox = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_WANTKEYBOARDINPUT, 30, 30, 200, 100, hwnd, (HMENU) ID_LIST, hInst, NULL);

// Отменяем режим перерисовки списка SendMessage(hListBox, WM_SETREDRAW, FALSE, 0L);

// Добавляем в список несколько строк

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Зеленый");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Красный");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Розовый");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Пурпурный");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Синий");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Желтый");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Фиолетовый");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Черный");

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Белый");

// Включаем режим перерисовки списка SendMessage(hListBox, WM_SETREDRAW, TRUE, 0L);



// Перерисовываем список InvalidateRect(hListBox, NULL, TRUE);

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

return 0; }

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

case WM_COMMAND: { // Обработка извещения списка об ошибке if(wParam == ID_LIST) { // Преобразуем к типу unsigned, так как // константа LBN_ERRSPACE определена как // отрицательное число if(HIWORD(lParam) == (unsigned)LBN_ERRSPACE) { MessageBox(hwnd, "Мало памяти", szWindowTitle, MB_OK);
}

// Если пользователь сделал двойной щелчок // по строке списка, выводим эту строку // на экран else if(HIWORD(lParam) == LBN_DBLCLK) { int uSelectedItem; char Buffer[256];

// Определяем номер выделенной строки uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

// Если в списке есть выделенная строка, // выводим ее на экран if(uSelectedItem != LB_ERR) { // Получаем выделенную строку SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

// Выводим ее на экран MessageBox(hwnd, (LPSTR)Buffer, szWindowTitle, MB_OK);
} }

// Если пользователь изменил выделенную // строку, выводим в окно новую // выделенную строку else if(HIWORD(lParam) == LBN_SELCHANGE) { int uSelectedItem, nSize; char Buffer[256]; HDC hdc;

// Определяем номер новой выделенной строки uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

if(uSelectedItem != LB_ERR) { // Получаем строку SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

// Получаем контекст отображения hdc = GetDC(hwnd);

// Определяем длину строки nSize = lstrlen(Buffer);

// Очищаем место для вывода строки TextOut(hdc, 250, 60, (LPSTR)" ", 25);

// Выводим строку TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

// Освобождаем контекст отображения ReleaseDC(hwnd, hdc);
} } }

// Сообщение от кнопки else if(wParam == ID_BUTTON) { int uSelectedItem; char Buffer[256];



// Определяем номер выделенной строки, // получаем текст строки и выводим // этот текст в окно uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

if(uSelectedItem != LB_ERR) { SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

MessageBox(hwnd, (LPSTR)Buffer, szWindowTitle, MB_OK);
} } return 0; }

// Это сообщение поступает от списка, если он // имеет фокус ввода и пользователь работает // с клавиатурой case WM_VKEYTOITEM: { // Если пользователь нажал клавишу <Enter>
, // посылаем в окно сообщение WM_COMMAND с // параметром wParam, равным идентификатору // кнопки if(wParam == VK_RETURN) { SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);
} return -1; }

case WM_PAINT: { HDC hdc; PAINTSTRUCT ps;

hdc = BeginPaint(hwnd, &ps);

TextOut(hdc, 30, 10, "Выберите цвет и нажмите кнопку 'OK'", 35);

EndPaint(hwnd, &ps);
return 0; }

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

Функция WinMain создает главное окно приложения и запускает цикл обработки сообщений.

Функция главного окна создает список во время обработки сообщения WM_CREATE:

hListBox = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_WANTKEYBOARDINPUT, 30, 30, 200, 100, hwnd, (HMENU) ID_LIST, hInst, NULL);

Для того чтобы родительское окно могло получать от списка клавиатурные сообщения, дополнительно к стилю LBS_STANDARD указан стиль LBS_WANTKEYBOARDINPUT.

После создания списка его надо заполнить строками. Перед началом этой процедуры функция главного окна приложения отменяет режим перерисовки списка после добавления в него очередной строки, посылая списку сообщение WM_SETREDRAW со значением параметр wParam, равным FALSE:

SendMessage(hListBox, WM_SETREDRAW, FALSE, 0L);

Вслед за этим функция окна добавляет в список несколько строк, посылая списку несколько раз сообщение LB_ADDSTRING:

SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"Зеленый");

После заполнения списка вновь включается режим перерисовки, вслед за чем список перерисовывается:



SendMessage(hListBox, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hListBox, NULL, TRUE);

Далее обработчик сообщения WM_CREATE создает кнопку, которая будет использована для выбора из списка выделенной строки.

Так как для работы со списком используется клавиатура, получив сообщение WM_SETFOCUS, функция главного окна приложения отдает фокус ввода списку:

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

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

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

Если пользователь сделал двойной щелчок по строке списка, приходит извещение LBN_DBLCLK.

Обработчик этого извещения определяет номер выделенной строки (т. е. строки по которой был сделан двойной щелчок), посылая списку сообщение LB_GETCURSEL:

uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

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

SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

Номер строки передается через параметр wParam, адрес буфера указывается через параметр lParam.

Далее полученная строка выводится на экран при помощи функции MessageBox.

Обработчик извещения LBN_SELCHANGE получает управление, когда пользователь изменяет выделенную строку (т. е. выделяет другую строку) списка. В этом случае обработчик извещения посылает окну сообщение LB_GETCURSEL для определения номера новой выделенной строки. Вслед за этим он переписывает эту строку в буфер, посылая списку сообщение LB_GETTEXT:

SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

Полученная строка отображается в главном окне приложения при помощи функции TextOut:

TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

Действия, выполняемые обработчиком сообщения WM_COMMAND при получении сообщения от кнопки, полностью аналогичны действиям при обработке извещения LBN_DBLCLK.


В этом случае обработчик сообщения определяет номер выделенной строки, переписывает эту строку в буфер и выводит ее на экран.

Так как при создании списка был указан класс LBS_WANTKEYBOARDINPUT, список посылает в родительское окно сообщение WM_VKEYTOITEM (если бы мы создавали список без стиля LBS_HASSTRINGS, вместо этого сообщения в родительское окно посылалось бы сообщение WM_CHARTOITEM). Параметр wParam для этого сообщения содержит значение виртуального кода нажатой клавиши. Наше приложение пользуется этим, отслеживая выбор строки клавишей <Enter>
(клавише <Enter>
соответствует виртуальный код VK_RETURN):

case WM_VKEYTOITEM: { if(wParam == VK_RETURN) { SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);
} return -1; }

Обработчик сообщения WM_VKEYTOITEM посылает функции главного окна сообщение WM_COMMAND, имитируя выбор строки кнопкой "OK".

Обработчик возвращает значение -1, позволяя списку выполнить обработку кода клавиши, назначенную по умолчанию. Вместо этого можно было бы вернуть значение -2. В таком случае список игнорирует сообщения клавиатуры, а все изменения в списке должны выполняться родительским окном. Если вернуть нулевое или положительное значение, список выделит выбранную строку и выполнит обработку кода клавиши по умолчанию.

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


Файл listbox\listbox.def


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



Файл listdir\listdir.cpp


// ---------------------------------------- // Использование органа управления // класса "listbox" для просмотра // содержимого каталога // ----------------------------------------

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

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

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

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

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

// Заголовок окна char const szWindowTitle[] = "Выбор файла";

// Идентификатор копии приложения 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) { // Идентификатор органа управления "listbox" static HWND hListBox;

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

switch (msg) { case WM_CREATE: { // Создаем список hListBox = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_WANTKEYBOARDINPUT, 30, 30, 200, 200, hwnd, (HMENU) ID_LIST, hInst, NULL);

// Добавляем в список содержимое текущего // каталога

SendMessage(hListBox, LB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

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

return 0; }

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

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

// Двойной щелчок по имени файла, каталога // или диска else if(HIWORD(lParam) == LBN_DBLCLK) { // Имитируем приход сообщения от кнопки SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);
return 0; }



// Если пользователь изменил выделенную // строку, выводим в окно новую // выделенную строку else if(HIWORD(lParam) == LBN_SELCHANGE) { int uSelectedItem, nSize; char Buffer[256]; HDC hdc;

// Определяем номер новой выделенной строки uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

if(uSelectedItem != LB_ERR) { // Получаем строку SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

hdc = GetDC(hwnd);
nSize = lstrlen(Buffer);

TextOut(hdc, 250, 60, (LPSTR)" ", 25);
TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

ReleaseDC(hwnd, hdc);
} } }

// Сообщение от кнопки else if(wParam == ID_BUTTON) { int uSelectedItem; char Buffer[256];

// Определяем номер выделенной строки uSelectedItem = (int)SendMessage(hListBox, LB_GETCURSEL, 0, 0L);

if(uSelectedItem != LB_ERR) { // Получаем выделенную строку SendMessage(hListBox, LB_GETTEXT, uSelectedItem, (LPARAM)Buffer);

// Если выбрали имя каталога или диска, // пытаемся изменить сначала текущий // каталог, а затем текущий диск if(Buffer[0] == '[') { // Выделяем в выбранной строке имя каталога Buffer[lstrlen(Buffer) - 1] = '\0';

// Пытаемся изменить каталог if(chdir(&Buffer[1]) != 0) { // Если не удалось, значит выбрали имя // диска. В этом случае выделяем букву // имени диска и добавляем строку ":\\: Buffer[3] = '\0'; lstrcat(Buffer, ":\\");

// Изменяем текущий каталог if(chdir(&Buffer[2]) == 0) { // Преобразуем букву диска AnsiLowerBuff(&Buffer[2], 1);

// Устанавливаем новый диск setdisk(Buffer[2] - 'a');
} }

// Сбрасываем содержимое списка SendMessage(hListBox, LB_RESETCONTENT, 0, 0L);

// Заполняем список информацией о файлах // и каталогах в текущем каталоге, а также // вносим туда имена дисков SendMessage(hListBox, LB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");
}

// Если выбрали файл, выводим его имя на экран else { MessageBox(hwnd, Buffer, szWindowTitle, MB_OK);
} } } return 0; }



// Это сообщение поступает от списка, если он // имеет фокус ввода и пользователь работает // с клавиатурой case WM_VKEYTOITEM: { // Если пользователь нажал клавишу <Enter>
, // посылаем в окно сообщение WM_COMMAND с // параметром wParam, равным идентификатору // кнопки if(wParam == VK_RETURN) { SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);
} return -1; }

case WM_PAINT: { HDC hdc; PAINTSTRUCT ps;

hdc = BeginPaint(hwnd, &ps);

TextOut(hdc, 30, 10, "Выберите файл, каталог или диск", 31);

EndPaint(hwnd, &ps);
return 0; }

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

Функция WinMain приложения LISTDIR почти ничем не отличается от аналогичной функции предыдущего приложения. Она создает главное окно приложения и запускает цикл обработки сообщений.

Функция главного окна при обработке сообщения WM_CREATE создает одноколоночный список:

hListBox = CreateWindow("listbox", NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_WANTKEYBOARDINPUT, 30, 30, 200, 200, hwnd, (HMENU) ID_LIST, hInst, NULL);

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

SendMessage(hListBox, LB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

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

Флаг Описание
DDL_ARCHIVE Файлы, для которых в байте атрибутов установлен бит архивирования
DDL_DIRECTORY Каталоги
DDL_DRIVES Дисковые устройства
DDL_EXCLUSIVE В список не будут включены имена обычных файлов
DDL_HIDDEN Скрытые файлы
DDL_READONLY Только читаемые файлы
DDL_READWRITE Файлы, над которыми можно выполнять операции чтения/записи, не имеющие дополнительных атрибутов
DDL_SYSTEM Системные файлы
<


Параметр lParam сообщения LB_DIR должен содержать указатель на текстовую строку, используемую в качестве шаблона имени. В нашем случае используется шаблон "*.*", поэтому в список попадают имена всех файлов.

Когда в функцию главного окна приложения приходит сообщение WM_COMMAND с извещением LBN_DBLCLK о двойном щелчке по строке списка, функция окна посылает сама себе сообщение WM_COMMAND с параметром wParam, равным идентификатору кнопки "OK", созданной приложением в главном окне справа от списка.

Извещение LBN_SELCHANGE обрабатывается таким же образом, что и в предыдущем приложении.

Обработчик сообщения от кнопки определяет номер выделенной строки и копирует эту строку в свой буфер, посылая в список последовательно сообщения LB_GETCURSEL и LB_GETTEXT. Далее выполняется анализ полученной строки.

Если строка начинается с символа "[", то это может быть либо имя каталога (например, "[dos]"), либо имя диска (например, "[-c-]"). Вначале обработчик предполагает, что получено имя каталога (для простоты мы считаем что каталоги с экзотическими именами "-c-" и т. п. не используются). Закрывающая квадратная скобка заменяется на двоичный ноль, вслед за чем вызывается функция chdir (из стандартной библиотеки компилятора), предназначенная для смены текущего каталога.

Если функция chdir вернула ненулевое значение, текущий каталог не содержит подкаталога с указанным именем. В этом случае обработчик сообщения считает, что полученная строка содержит имя диска, состоящее из буквы в обрамлении квадратных скобок и символов "-". Буква имени диска выделяется и к ней дописывается строка ":\\", вслед за чем выполняется повторная попытка изменения текущего каталога на корневой нового диска. Для изменения текущего диска вызывается функция setdisk из стандартной библиотеки компилятора.

Обратите внимание на способ, которым мы преобразуем букву обозначения диска:

AnsiLowerBuff(&Buffer[2], 1);

Мы использовали функцию AnsiLowerBuff, которая выполняет преобразование из заглавной буквы в прописную (маленькую) с учетом используемого национального алфавита.

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

SendMessage(hListBox, LB_RESETCONTENT, 0, 0L);
SendMessage(hListBox, LB_DIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES | DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

Если обработчик так и не смог сменить каталог или диск, полученная от списка строка содержит имя файла. Это имя выводится на экран:

MessageBox(hwnd, Buffer, szWindowTitle, MB_OK);

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


Файл listdir\listdir.def


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