Пример 2B — работа с томами




 

Данный пример иллюстрирует применение некоторых функций для работы с томами и файлами. Это приложение построено на основе примера 1B, использующего распаковщики сообщений. В окне, созданным данным приложением, просто будут перечисляться имена томов и некоторая информация о них, а также о назначении устройств DOS.

Интересно рассмотреть, каким образом осуществляется вывод информации о томах в окно приложения. Это можно было–бы реализовать разными способами:

Хуже некуда. Изменить в примере 1B.CPP обработку сообщения WM_PAINT (функция Cls_OnPaint), так, что бы при обработке сообщения опрашивать все устройства и выводить необходимую информацию. Очень плохо в этом то, что такой опрос может занимать значительное время, и приводить к обращениям к устройствам при каждой перерисовке окна. Еще хуже то, что при попытке чтения информации с устройства возможно возникновение ошибок, о которых система будет сообщать в отдельном всплывающем окошке, например “Cannot read from drive A:”. Это окошко окажется, с большой вероятностью, поверх окна приложения, следовательно, при закрытии окна сообщения, наше приложение снова получит сообщение WM_PAINT, снова попробует обратиться к тому–же устройству... и так далее (что бы этого избежать можно воспользоваться функцией SetErrorMode, отключив вывод сообщений об ошибках).

Чуть лучше. Считывать информацию об устройствах при создании окна, формировать где–то в памяти весь текст, затем отображать его при обработке WM_PAINT. Этот способ качественно лучше тем, что получение данных и их отображение осуществляется в разных местах программы и в разное время. Однако и у него есть минусы — список устройств может изменяться во время работы приложения, а оно этого не отразит, и, кроме того, список может оказаться достаточно большим — больше размеров окна. Первую проблему мы решать сейчас даже не будем — этого легко добиться введя меню с командой Обновить (Refresh) или выполняя такое обновление через определенный интервал времени. Вторая проблема приведет к добавлению собственного интерфейса — обработке сообщений клавиатуры и мыши, что потребует написания значительного кода и продолжительной отладки.

Еще лучше. Чуть разовьем второй способ — получать информацию будем при создании окна, а отображение и работу с мышью и клавиатурой переложим на Windows. Windows предоставляет разработчикам несколько стандартных классов окон, реализующих самые распространенные элементы управления — кнопки, флажки, списки, простейшее окно–редактор и другие. Вот окном–редактором мы и воспользуемся, причем специально укажем, что текст является неизменяемым, то есть редактор будет работать как окно просмотра. В этом случае при создании главного окна приложения, мы должны создать дочернее окно–редактор, занимающее всю внутреннюю область главного окна[ii]. Затем, сформировав весь необходимый текст, передать его редактору. Окно редактирования будет снабжено полосами прокрутки, поддерживать работу с клавиатурой и мышью, осуществлять передачу текста в буфер обмена — и все само, без разработки дополнительного кода. Мы должны доделать совсем немного — обрабатывать сообщения, связанные с изменением размера главного окна (соответственно менять размер дочернего окна), при уничтожении главного окна не забыть уничтожить окно–редактор, а сообщение WM_PAINT мы можем вообще не обрабатывать.

Приложение рассчитано на работу в Win32, однако это связано только лишь с применением функций Win32 API для получения информации о томах. При создании приложения для Windows 3.x вместо функций, не декларированных в Windows API, используются функции–эмуляторы, включаемые в это приложение при компиляции 16ти разрядного приложения. Это обеспечивает возможность нормальной компиляции и работы приложения на обеих платформах, но с несколько ограниченными возможностями в случае применения Windows API (строго говоря, функции–эмуляторы можно было бы сделать и более мощными, опираясь на функции и структуры данных MS–DOS — просто это выходит за рамки данной книги).

Функции DefineDosDevice и QueryDosDevice, применяемые в числе прочих в этом приложении, работают только в виде 32х разрядного приложения и только под Windows NT, так как они имеют смысл исключительно для реализации Win32 в Windows NT.

Файл 2B.CPP

#define STRICT

#include <windows.h>

#include <windowsx.h>

#define UNUSED_ARG(arg) (arg)=(arg)

#ifndef __NT__

// определим необходимые функции при компиляции для Windows API

#include "2b16.cpp"

// текст файла 2b16.cpp приведен ниже

#endif

static char szWndClass[]= "test volume functions";

static HINSTANCE hInstance;

BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)

{UNUSED_ARG(lpCreateStruct);

char *temp, *p, *s;

DWORD n, i, sernum, complen, flags, spc, bps, fc, tc;

char file_system[ 128 ], buffer[ 1024 ];

UINT count, errmode;

HWND hwndView;

RECT rc;

HFILE hf;

static OFSTRUCT ofs;

static char tempfile[] = “c:\\test.txt”;

static char devZ[] = “Z:”;

// создадим окно-редактор, занимающее всю внутреннюю область окна

GetClientRect(hwnd, &rc);

// теоретически в CREATESTRUCT указаны размеры окна, но к сожалению они

// могут быть указаны как нулевые, хотя это не так. Функция GetClientRect

// в этом случае все равно возвращает корректные данные

hwndView = CreateWindow(

"EDIT", "",

WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_MULTILINE|ES_READONLY,

0, 0, rc.right, rc.bottom, hwnd, (HMENU)1, hInstance, NULL);

if (!IsWindow(hwndView)) {

// если окно просмотра создать не удалось, то завершаем приложение

// сообщив об ошибке

MessageBox(NULL, "Cannot create viewer window!", NULL, MB_OK);

return FALSE;}

// для красоты используем моноширинный шрифт (подробнее см. GDI)

SetWindowFont(hwndView, GetStockObject(ANSI_FIXED_FONT), FALSE);

// функция SetWindowFont в документации не описана, это макрос, определяемый

// в windowsx.h Подробнее — см. исходный текст этого файла.

// запоминаем хендл окна просмотра в структуре описания главного окна

// (при регистрации класса надо зарезервировать 4 байта — что бы и в

// Windows API и в Win32 API использовать одинаковые значения и функции)

SetWindowLong(hwnd, 0, (LONG)hwndView);

// 1. мы не знаем заранее, как много места понадобиться для описания всех

// томов и устройств DOS. Для простоты создадим временный файл с заранее

// заданным именем c:\test.txt (строго говоря, надо было бы проверить наличие

// переменной TEMP или TMP, убедиться, что она указывает на корректный

// каталог - в жизни часто она указывает на несуществующий каталог, диск

// или даже на защищенный диск - всякое бывает - и создать файл там — но это

// все слишком громоздко для примера).

// 2. в этом файле мы соберем нужный текст, а затем разом передадим его окну

// просмотра, после чего файл удалим.

// 3. для работы с файлами используем функции Windows API, что бы сохранить

// переносимость между разными платформами

hf = _lcreat(tempfile, 0);

if (hf == HFILE_ERROR) {

// при ошибке - сообщаем и заканчиваем (окно просмотра будет уничтожено

// позже, при обработке WM_DESTROY

MessageBox(NULL, "Cannot create temporary file!", NULL, MB_OK);

return FALSE;}

// пишем первый заголовок

_lwrite(hf, "******** GET VOLUME INFORMATION ********\r\n\r\n", 44);

SetLastError(ERROR_SUCCESS);

n = GetLogicalDriveStrings(0, NULL);

if (GetLastError()!= ERROR_SUCCESS) {

// Функция GetLogicalDriveStrings не реализована в Win32s и (в книгах

// часто утверждают) в Windows-95, возможно, иногда это и так, но у меня

// в Windows-95 версии 4.00.950a, локализованной для России, работает.

// если все же нет, то эмулируем ее работу посредством GetLogicalDrives

// которая точно есть во всех реализациях Win32

n = GetLogicalDrives();

// узнаем, сколько всего устройств

count = 0;

for (i = 1; i; i <<= 1) if (n & i) count++;

// формируем строку с именами устройств

temp = new char [ count * 4 + 1 ];

if (temp) {

p = temp;

for (i = 0; i < 32; i ++) if (n & (1L << i)) {

*p++ = (char)('A' + i); *p++ = ':'; *p++ = '\\'; *p++ = '\0';}

*p++ = '\0';}

} else {

// если функция GetLogicalDriveStrings работает, то используем ее

temp = new char [ n + 1 ];

if (temp) GetLogicalDriveStrings(n, temp);

}

if (temp) {

// исключаем обработку сообщений о критической ошибке

errmode = SetErrorMode(SEM_FAILCRITICALERRORS);

// если строка получена, то разбираем ее по частям

for (p = temp; *p; p += lstrlen(p) + 1) {

wsprintf(buffer, "Root: %s", p);

switch (GetDriveType(p)) {

case 0: s = "???"; break;

case 1: s = "invalid"; break;

case DRIVE_REMOVABLE: s = "REMOVABLE"; break;

case DRIVE_FIXED: s = "FIXED"; break;

case DRIVE_REMOTE: s = "REMOTE"; break;

case DRIVE_CDROM: s = "CD-ROM"; break;

case DRIVE_RAMDISK: s = "RAM DISK"; break;

default: s = "what?!"; break;}

// укажем явное преобразование указателя на строку ‘s’ к типу

// LPSTR — так как в Windows API для моделей памяти с одним сегментом

// данных (tiny,small,medium) указатели по умолчанию 16-ти разрядные

// а для функций Windows обязательно нужны 32-х разрядные

wsprintf(buffer+lstrlen(buffer), " type=%s volume='", (LPSTR)s);

sernum = complen = flags = 0; file_system[0] = '\0';

if (

GetVolumeInformation(

p, buffer+lstrlen(buffer), 64,

&sernum, &complen, &flags,

file_system, sizeof(file_system)))

{// если информация о томе прочитана -> получаем и выводим более

// подробные сведения

wsprintf(buffer+lstrlen(buffer), "' serial=%08lX\r\n", sernum);

_lwrite(hf, buffer, lstrlen(buffer));

spc = bps = fc = tc = 0L;

GetDiskFreeSpace(p, &spc, &bps, &fc, &tc);

bps *= spc;

wsprintf(

buffer,

" comp. length=%lu cluster=%lu total=%luK free=%luK\r\n",

complen, bps, bps*tc/1024, bps*fc/1024);

_lwrite(hf, buffer, lstrlen(buffer));

wsprintf(buffer, " file system ='%s' flags=", file_system);

s = buffer + lstrlen(buffer);

if (flags & FS_CASE_IS_PRESERVED) {

lstrcpy(s, "CASE_PRESERVED "); s+= lstrlen(s);}

if (flags & FS_CASE_SENSITIVE) {

lstrcpy(s, "CASE_SENSITIVE "); s+= lstrlen(s);}

if (flags & FS_UNICODE_STORED_ON_DISK) {

lstrcpy(s, "UNICODE "); s+= lstrlen(s);}

if (flags & FS_PERSISTENT_ACLS) {

lstrcpy(s, "ACL "); s+= lstrlen(s);}

if (flags & FS_FILE_COMPRESSION) {

lstrcpy(s, "MAY_COMPRESS "); s+= lstrlen(s);}

if (flags & FS_VOL_IS_COMPRESSED) {

lstrcpy(s, "COMPRESSED "); s+= lstrlen(s);}

lstrcpy(s, "\r\n");

_lwrite(hf, buffer, lstrlen(buffer));

} else {

// если информация о томе не получена, то просто сообщаем

lstrcpy(

buffer+lstrlen(buffer),

"' ***** NO VOLUME INFORMATION!\r\n");

_lwrite(hf, buffer, lstrlen(buffer));}}

delete temp;

// восстанавливаем режим обработки критических ошибок

SetErrorMode(errmode);} else {

// если возникла ошибка (не хватило памяти для получения списка устройств)

// то просто выводим текст с сообщением, но не заканчиваем, что бы не

// заботиться об уничтожении временного файла досрочно

_lwrite(hf, "NOT ENOUGHT MEMORY TO GET INFORMATION!\r\n", 40);}

// пишем второй заголовок

_lwrite(hf, "\r\n******** READ DOS DEVICES MAP ********\r\n\r\n", 44);

// просто для примера назначаем Z: как каталог, содержащий win.com

GetWindowsDirectory(buffer, sizeof(buffer));

SetLastError(ERROR_SUCCESS);

DefineDosDevice(0, devZ, buffer);

if (GetLastError()!= ERROR_SUCCESS) {

// функция DefineDosDevice не реализована - это не Windows NT!

_lwrite(hf, "NOTE: DefineDosDevice() is not implemented!\r\n", 45);

} else {

// если назначить удалось - это NT, получаем полный список и выводим его

// хотя может быть и Windows 98

buffer[ QueryDosDevice(NULL, buffer, 1024) ] = ‘\0’;

// функция QueryDosDevice возвращает число символов, скопированных в буфер

// и в случае Windows 98 мы получим 0, хотя сам буфер может содержать мусор.

for (p = buffer; *p; p += lstrlen(p) + 1) {

_lwrite(hf, " ", 15 - lstrlen(p));

_lwrite(hf, p, lstrlen(p));

_lwrite(hf, " = ", 3);

// используем массив file_system в качестве временной строки

QueryDosDevice(p, file_system, sizeof(file_system));

_lwrite(hf, file_system, lstrlen(file_system));

_lwrite(hf, "\r\n", 2);}

GetWindowsDirectory(buffer, sizeof(buffer));

// удаляем назначенное нами устройство

DefineDosDevice(

DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, "Z:", buffer);}

// узнаем длину файла с текстом и выделяем необходимый блок памяти

n = _llseek(hf, 0L, 1);

temp = new char [ (int)n + 1 ];

if (temp) {

// загружаем его в память и добавляем оканчивающий символ ‘\0’

_llseek(hf, 0L, 0);

temp[ _lread(hf, temp, (int)n) ] = '\0';

// и передаем окну просмотра

SetWindowText(hwndView, temp);

// наш временный буфер больше не нужен

delete temp;

} else {

// если памяти не хватило – сообщаем

SetWindowText(hwndView, "*** Cannot load text ***");}

// закрываем временный файл и удаляем его - система сама этого не сделает

_lclose(hf);

OpenFile(tempfile, &ofs, OF_DELETE);

return TRUE;}

void Cls_OnSize(HWND hwnd, UINT state, int cx, int cy)

{UNUSED_ARG(state);

// при изменении размеров главного окна меняем размеры окна просмотра

HWND hwndView = (HWND)GetWindowLong(hwnd, 0); if (IsWindow(hwndView)) MoveWindow(hwndView, 0,0, cx, cy, TRUE);}

void Cls_OnDestroy(HWND hwnd)

{// при закрытии главного окна закрываем его дочернее (лучше это сделать

// самим, хотя в крайнем случае система это сделает за вас)

HWND hwndView = (HWND)GetWindowLong(hwnd, 0);

if (IsWindow(hwndView)) DestroyWindow(hwndView);

PostQuitMessage(0);}

void Cls_OnSetFocus(HWND hwnd, HWND hwndOldFocus)

{UNUSED_ARG(hwndOldFocus);

// при получении фокуса главным окном - активируем окно просмотра, что бы

// работала клавиатура

HWND hwndView = (HWND)GetWindowLong(hwnd, 0);

if (IsWindow(hwndView)) SetFocus(hwndView);}

LONG WINAPI _export WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{switch (uMsg) {

HANDLE_MSG(hWnd, WM_CREATE, Cls_OnCreate);

HANDLE_MSG(hWnd, WM_DESTROY, Cls_OnDestroy);

HANDLE_MSG(hWnd, WM_SIZE, Cls_OnSize);

HANDLE_MSG(hWnd, WM_SETFOCUS, Cls_OnSetFocus);

default: break;}

return DefWindowProc(hWnd, uMsg, wParam, lParam);}

static BOOL init_instance(HINSTANCE hInstance)

{WNDCLASS wc;

wc.style = 0;

wc.lpfnWndProc = WinProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 4; // для хендла окна просмотра

wc.hInstance = hInstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wc.lpszMenuName = NULL;

wc.lpszClassName = szWndClass;

return RegisterClass(&wc) == NULL? FALSE: TRUE;}

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)

{UNUSED_ARG(lpszCmdLine);

MSG msg;

HWND hWnd;

if (!hPrevInst) {

if (!init_instance(hInst)) return 1;}

hWnd= CreateWindow(

szWndClass, // class name

"window header", // window name

WS_OVERLAPPEDWINDOW, // window style

CW_USEDEFAULT,CW_USEDEFAULT, // window position

CW_USEDEFAULT,CW_USEDEFAULT, // window size

NULL, // parent window

NULL, // menu

hInst, // current instance

NULL // user-defined parameters);

if (!hWnd) return 1;

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

while (GetMessage(&msg, NULL, NULL, NULL)) {

TranslateMessage(&msg);

DispatchMessage(&msg);}

return msg.wParam;}

Файл 2B16.CPP

// включаем описания необходимых функций библиотеки времени выполнения

#include <dos.h>

#include <io.h>

// функции GetLastError и SetLastError не описаны в Windows API, эмулируем их

#define ERROR_SUCCESS 0

static int error_code = ERROR_SUCCESS;

int GetLastError(void)

{return error_code;}

int SetLastError(int err)

{int prev = error_code; return error_code = err;}

// функция GetLogicalDriveStrings не реализована в Windows API, причем в некоторых // реализациях Win32 она представлена заглушкой, которую мы и эмулируем

int GetLogicalDriveStrings(DWORD size, char* buf)

{UNUSED_ARG(size);

UNUSED_ARG(buf);

SetLastError(1);

return 0;}

// функция GetLogicalDrives не реализована в Windows API, но нам она нужна

// для нормальной работы приложения - эмулируем ее

DWORD GetLogicalDrives(void)

{DWORD dwDevices = 0L;

unsigned n, uTotal, uCurrent, uTest;

// запоминаем текущее устройство

_dos_getdrive(&uCurrent);

_dos_setdrive(uCurrent, &uTotal);

for (n = 1; n <= uTotal; n++) {

_dos_setdrive(n, &uTotal);

_dos_getdrive(&uTest);

// если устройство удалось сделать текущим - оно описано в системе

if (uTest == n) dwDevices |= 1L << (n -1);}

// восстанавливаем прежнее устройство

_dos_setdrive(uCurrent, &uTotal);

return dwDevices;}

// функция GetDriveType в Win32 API реализована несколько иначе, чем в Windows;

// переопределим функцию Win32 API UINT GetDriveType(LPSTR) через функцию

// Windows API UINT GetDriveType(UINT), заодно определим пару недостающих // символов

#ifndef DRIVE_CDROM

#define DRIVE_CDROM 5

#endif

#ifndef DRIVE_RAMDISK

#define DRIVE_RAMDISK 6

#endif

UINT GetDriveType(char* pRoot)

{char c;

if (pRoot) {

c = (char)AnsiUpper((LPSTR)(DWORD)(UINT)(unsigned char)(pRoot[ 0 ]));

if (c >= 'A' && c <= 'Z') return GetDriveType(c - 'A');}

return 0;}

// функция GetVolumeInformation не определена в Windows API. Эмулируем ее // в сильно упрощенном виде, заодно определяем необходимые символы.

#define FS_CASE_IS_PRESERVED 1

#define FS_CASE_SENSITIVE 2

#define FS_UNICODE_STORED_ON_DISK 4

#define FS_PERSISTENT_ACLS 16

#define FS_FILE_COMPRESSION 32

#define FS_VOL_IS_COMPRESSED 64

BOOL GetVolumeInformation(

char *pRoot, char* pVolume, int cbVolume,

DWORD *sernum, DWORD *complen, DWORD *flags,

char *pFS, int cbFS)

{BOOL fReturnCode = FALSE;

char c;

unsigned uDrive, uCurrent, n;

UNUSED_ARG(cbVolume);

UNUSED_ARG(cbFS);

if (pRoot) {

c= (char)AnsiUpper((LPSTR)(DWORD)(UINT)(unsigned char)(pRoot[ 0 ]));

if (c >= 'A' && c <= 'Z') {

uDrive = (unsigned)(c - 'A' + 1);

_dos_getdrive(&uCurrent);

_dos_setdrive(uDrive, &n);

_dos_getdrive(&n);

if (uDrive == n) {// убедимся, что устройство определено

// строго говоря все это стоит определить точнее:

pVolume[0] = '\0';

*sernum = 0L;

*complen = 12;

*flags = (long)FS_CASE_IS_PRESERVED;

pFS = '\0';

// проверим, есть ли доступ к устройству - если мы вернем

// TRUE, то позже нами будет предпринята попытка узнать

// размер тома

if (!access(pRoot, F_OK)) fReturnCode = TRUE;}

// восстановим текущее устройство

_dos_setdrive(uCurrent, &n);}}

return fReturnCode;}

// функция GetDiskFree не определена в Windows API, эмулируем ее посредством

// функции _dos_getdiskfree стандартной библиотеки времени выполнения.

void GetDiskFreeSpace(char* pRoot, DWORD* spc, DWORD* bps, DWORD* fc, DWORD* tc)

{char c;

diskfree_t df;

if (pRoot) {

c = (char)AnsiUpper((LPSTR)(DWORD)(UINT)(unsigned char)(pRoot[ 0 ]));

if (c >= 'A' && c <= 'Z') {

if (!_dos_getdiskfree(c - 'A' + 1, &df)) {

*spc = df.sectors_per_cluster;

*bps = df.bytes_per_sector;

*fc = df.avail_clusters;

*tc = df.total_clusters;}}}}

// функции DefineDosDevice и QueryDosDevice не реализованы в Windows API.

// используем вместо них заглушки, так как они имеют смысл только для NT

#define DDD_REMOVE_DEFINITION 1

#define DDD_EXACT_MATCH_ON_REMOVE 2

BOOL DefineDosDevice(DWORD dwFlags, LPSTR lpDosDevice, LPSTR lpPath)

{UNUSED_ARG(dwFlags);

UNUSED_ARG(lpDosDevice);

UNUSED_ARG(lpPath);

SetLastError(1);

return FALSE;}

DWORD QueryDosDevice(LPSTR lpDosDevice, LPSTR lpPath, DWORD ucchMax)

{UNUSED_ARG(lpDosDevice);

UNUSED_ARG(lpPath);

UNUSED_ARG(ucchMax);

SetLastError(1);

return 0L;}

Резюме

Любопытно, что в существующем виде приложение по–разному работает на разных платформах — для получения информации о томах и доступном пространстве предпринимается попытка чтения с устройства. Однако том в устройстве может отсутствовать вовсе — например, в дисководе может не быть дискеты. Разные платформы реагируют на такое событие разным образом — Windows–95, убедившись что тома нет, просто возвращает нули, а Windows NT или Windows 3.x выдают сообщение о системной ошибке.

В приложении специально применяется функция SetErrorMode, которая позволяет отключить вывод сообщений о невозможности чтения с диска. Любопытно, что в документации часто указано, что эта функция для Win32 API реализована только для RISC процессоров — странно, но на обычных Intel Pentium она тоже сработала. В пояснениях к результатам теста реакция системы на критические ошибки приводится так, как будто эта функция не применяется. На самом деле в приведенном приложении (с применяемой функцией SetErrorMode) сообщений об ошибках не будет.

Приложение 2B было протестировано на 2х компьютерах:

А) компьютер с Windows–95[iii] и Windows 3.11 + Win32s

диск A — 3.5”, дискета не вставлена

диск C — EIDE HDD, том “Bootable”

диск D — SCSI HDD, под Windows 3.11 доступен через ASPI драйвер, том “SCSI_VOL”

диск E — IDE CD–ROM, диск не вставлен

Б) компьютер с Windows NT

диск A — 3.5”, дискета не вставлена

диск C — SCSI HDD,

диск D — IDE CD–ROM, диск не вставлен

Всего было выполнено 6 тестов — запускались 16ти и 32х разрядные версии приложения в среде Windows 3.11 + Win32s, Windows–95 и Windows NT Server 4.0. Результаты тестов следующие:

Среда Windows 3.11+Win32s, 16ти разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: A:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: C:\ type=FIXED volume='' serial=00000000

maximal component length=12 cluster=16384 total=822272K free=71712K

file system ='' flags=CASE_PRESERVED

Root: D:\ type=FIXED volume='' serial=00000000

maximal component length=12 cluster=32768 total=1065792K free=45408K

file system ='' flags=CASE_PRESERVED

Root: E:\ type=REMOTE volume='' ***** NO VOLUME INFORMATION!

******** READ DOS DEVICES MAP ********

NOTE: DefineDosDevice() is not implemented!

16ти разрядное приложение пытается обратиться к дискам в функции access (в эмуляции GetVolumeInformation), что для дисков A (гибкий диск) и E (CD–ROM) приводит к системному сообщению об ошибке “Cannot read from drive...”;

Локальный CD–ROM распознается как сетевое устройство.

Среда Windows 3.11+Win32s, 32х разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: A:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: C:\ type=FIXED volume='BOOTABLE' serial=00000000

maximal component length=12 cluster=16384 total=822272K free=71728K

file system ='FAT' flags=

Root: D:\ type=RAM DISK volume='SCSI_VOL' serial=00000000

maximal component length=12 cluster=32768 total=1065792K free=45408K

file system ='FAT' flags=

Root: E:\ type=CD-ROM volume='SCSI_VOL' serial=00000000

maximal component length=12 cluster=0 total=0K free=0K

file system ='FAT' flags=

******** READ DOS DEVICES MAP ********

NOTE: DefineDosDevice() is not implemented!

Реакция приложения совершенно другая (используются функции, входящие в реализацию Win32 API, а не их эмуляция нашим приложением).

При обращении к диску A генерируется сообщение об ошибке,

При обращении к диску E (CD–ROM) функция GetVolumeInformation (причем именно ее реализация в Win32s, а не ее эмуляция у нас) дает сообщение об ошибке и сообщает, что информация о томе успешно (?!) получена, после чего следует попытка узнать свободное пространство на отсутствующем диске E — с еще одним сообщением об ошибке (вместо сообщения “no volume information”).

Характерно, что тип диска E — CD–ROM — определен корректно, файловая система — CDFS (файловая система CD дисков) почему–то распознана как FAT, а метку тома позаимствовали у предыдущего диска[iv].

Кроме того, SCSI диск, доступный через ASPI, был распознан как RAM DISK.

Среда Windows 95, 16ти разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: A:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: C:\ type=FIXED volume='' serial=00000000

maximal component length=12 cluster=16384 total=822272K free=70208K

file system ='' flags=CASE_PRESERVED

Root: D:\ type=FIXED volume='' serial=00000000

maximal component length=12 cluster=32768 total=1065792K free=45408K

file system ='' flags=CASE_PRESERVED

Root: E:\ type=REMOTE volume='' ***** NO VOLUME INFORMATION!

******** READ DOS DEVICES MAP ********

NOTE: DefineDosDevice() is not implemented!

Уже неплохо — результаты совпадают с тем, что было получено для 16ти разрядного приложения в среде Windows 3.11, реакция на получение информации об отсутствующем диске такая–же — сообщение об ошибке.

Среда Windows 95, 32х разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: a:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: c:\ type=FIXED volume='BOOTABLE' serial=0E3219D9

maximal component length=255 cluster=16384 total=822272K free=69696K

file system ='FAT' flags=CASE_PRESERVED UNICODE

Root: d:\ type=FIXED volume='SCSI_VOL' serial=025511DA

maximal component length=255 cluster=32768 total=1065792K free=45408K

file system ='FAT' flags=CASE_PRESERVED UNICODE

Root: e:\ type=CD-ROM volume='' ***** NO VOLUME INFORMATION!

******** READ DOS DEVICES MAP ********

NOTE: DefineDosDevice() is not implemented!

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

Среда Windows NT, 16ти разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: A:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: C:\ type=FIXED volume='' serial=00000000

maximal component length=12 cluster=32768 total=999968K free=615136K

file system ='' flags=CASE_PRESERVED

Root: D:\ type=REMOTE volume='' serial=00000000

maximal component length=12 cluster=16384 total=601632K free=0K

file system ='' flags=CASE_PRESERVED

******** READ DOS DEVICES MAP ********

NOTE: DefineDosDevice() is not implemented!

Поведение приложения вполне соответствует обычному 16ти разрядному приложению в среде Windows 3.x, но вот полный размер диска, превышающий 1Г определяется с ошибкой — диск C на 1.33Г был определен как диск размером 999М.

Среда Windows NT, 32х разрядное приложение:

******** GET VOLUME INFORMATION ********

Root: A:\ type=REMOVABLE volume='' ***** NO VOLUME INFORMATION!

Root: C:\ type=FIXED volume='' serial=00AE5141

maximal component length=255 cluster=512 total=1405655K free=615143K

file system ='NTFS' flags=CASE_PRESERVED CASE_SENSITIVE UNICODE ACL MAY_COMPRESS

Root: D:\ type=CD-ROM volume='ASART3' serial=E2F025BC

maximal component length=221 cluster=2048 total=601644K free=0K

file system ='CDFS' flags=CASE_SENSITIVE

******** READ DOS DEVICES MAP ********

DISPLAY1 = \Device\Video0

NDIS = \Device\Ndis

DISPLAY2 = \Device\Video1

Z: = \??\C:\WINNT

D: = \Device\CdRom0

$VDMLPT1 = \Device\ParallelVdm0

COM1 = \Device\Serial0

COM2 = \Device\Serial10000

PIPE = \Device\NamedPipe

UNC = \Device\Mup

PhysicalDrive0 = \Device\Harddisk0\Partition0

PRN = \DosDevices\LPT1

A: = \Device\Floppy0

Scsi0: = \Device\ScsiPort0

DC21X41 = \Device\DC21X41

LPT1 = \Device\Parallel0

Scsi1: = \Device\ScsiPort1

C: = \Device\Harddisk1\Partition1

AUX = \DosDevices\COM1

MAILSLOT = \Device\MailSlot

NUL = \Device\Null

Единственное, что кажется несколько странным, так это сообщение о критической ошибке при попытке вызова GetVolumeInformation для отсутствующего диска, хотя под Windows–95 эта функция работает молча — она же и так возвращает результат “не удалось”, так зачем же еще давать сообщение? Сообщение об ошибке отключается с помощью SetErrorMode, хотя это и не согласуется с формальным описанием функции.

Итого:

Если ваше приложение может работать в Windows 3.x с Win32s, то необходимо особенно тщательное тестирование всех операций с файлами и томами, так как реакция системы может существенно отличаться от той, которая будет в случае Windows–95 и Windows NT. Кроме того особое внимание стоит уделить функциям, возвращающим информацию о томе (типа GetVolumeInformation) — они производят попытку реального обращения к томам, что может привести либо к возникновению сообщений об ошибках либо даже к «зависанию» всего приложения, если работа с томом сопровождается какой–либо ошибкой. Так, например, отдельной проверки и отладки потребуют все случаи работы со сменными дисками (гибкими, компакт–дисками, магнито–оптическими дисками и т.д.) и с сетевыми томами (особенно случаи, когда удаленный компьютер, предоставляющий свои тома в общее пользование, зависает, отключается или происходят какие–либо неполадки в работе сети — в такой ситуации возможно даже зависание компьютера, с которого производится вызов функции GetVolumeInformation).

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-06-03 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: