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

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

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

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

4.2. Функции Windows для работы с файлами

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

Открытие файлов

Для открытия файлов вы можете воспользоваться универсальной функцией OpenFile или более простой (но и более ограниченной) функцией _lopen.

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

HFILE WINAPI OpenFile(
  LPCSTR lpszFileName,        // путь к файлу
  OFSTRUCT FAR* lpOpenStruct, // адрес структуры OFSTRUCT
  UINT fuMode);               // режим работы и атрибуты

Функция возвращает идентификатор файла, который можно (и нужно) использовать во всех последующих операциях с файлом или -1 при ошибке.

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

Через параметр lpOpenStruct передается адрес структуры OFSTRUCT, которая заполняется информацией при первом открытии файла.

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

Константа Описание
OF_READ Файл открывается только для чтения
OF_WRITE Файл открывается только для записи
OF_READWRITE Файл открывается для чтения и записи
OF_SHARE_COMPAT Открытие файла в режиме совместимости. В этом режиме несколько приложений могут одновременно открыть файл, причем все эти приложения должны открывать файл в режиме совместимости
OF_SHARE_EXCLUSIVE Файл открывается в монопольном режиме. Для всех других приложений доступ к этому файлу на чтение и запись запрещен
OF_SHARE_DENY_WRITE После открытия файла к нему запрещается доступ со стороны других приложений на запись
OF_SHARE_DENY_READ После открытия файла к нему запрещается доступ со стороны других приложений на чтение
OF_SHARE_DENY_NONE Для открываемого файла не запрещается доступ к файлу ни на чтение, ни на запись
OF_PARSE Если указан этот флаг, функция OpenFile не выполняет никаких других действий, кроме заполнения структуры OFSTRUCT
OF_DELETE Уничтожение существующего файла
OF_VERIFY Если указан этот флаг, функция OpenFile сравнивает время и дату, записанную в структуре OFSTRUCT с временем и датой изменений указанного файла. Если обнаружено несоответствие, функция OpenFile возвращает значение HFILE_ERROR
OF_SEARCH Операционная система Windows выполняет поиск файла в каталогах даже в том случае, когда текстовая строка, указанная параметром lpszFileName, содержит полный путь к файлу
OF_PROMPT Если указан этот флаг, то в случае невозможности найти указанный файл Windows выдает диалоговую панель с предложением вставить в дисковод A: дискету с файлом. Этот флаг используется очень редко
OF_CANCEL Флаг OF_CANCEL используется в сочетании с флагом OF_PROMPT. Если он указан, то в описанную выше диалоговую панель будет добавлена кнопка "Cancel", позволяющая отменить открытие файла. Приложение получит в этом случае код ошибки, соответствующий ненайденному файлу, а пользователь - возможность выйти из безвыходного состояния, в которое он может попасть, не имея под рукой дискеты с нужным файлом
OF_CREATE Выполняется создание нового файла. Если указанный файл существует, он обрезается до нулевой длины
OF_EXIST При указании этого флага функция OpenFile вначале открывает файл, а затем сразу же его закрывает. Эта бесполезная на первый взгляд операция может быть использована для того чтобы убедиться в существовании указанного файла на диске
OF_REOPEN Этот флаг используется при повторном открытии файла на основе информации, хранящейся в структуре OFSTRUCT

Когда функция OpenFile вызывается в первый раз для открытия файла, она заполняет структуру OFSTRUCT , описанную в файле windows.h следующим образом:

typedef struct tagOFSTRUCT
{
  BYTE cBytes;
  BYTE fFixedDisk;
  UINT nErrCode;
  BYTE reserved[4];
  char szPathName[128];
} OFSTRUCT;

Поле cBytes содержит размер самой структуры OFSTRUCT в байтах.

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

Если при открытии файла произошла ошибка, в поле nErrCode записывается код ошибки. Возможные значения для кода ошибки приведены в приложении 1.

Поле reserved зарезервировано и не должно использоваться.

В поле szPathName находится полный путь к файлу в кодировке OEM.

Если функция OpenFile показалась вам слишком сложной в использовании, в ряде случаев для открытия файла вы сможете ограничиться функцией _lopen :

HFILE WINAPI _lopen(LPCSTR lpszFileName, int fnOpenMode);

Функция возвращает идентификатор открытого файла или HFILE_ERROR при ошибке.

Параметр lpszFileName, так же как и для функции OpenFile, является указателем на текстовую строку в кодировке ANSI, содержащую путь к файлу и закрытую двоичным нулем. В имени файла не допускается указывать символы шаблона, такие как "*" и "?".

Параметр fuOpenMode определяет режим, в котором открывается файл. Приведем список возможных значений для этого параметра.

Константа Описание
READ Файл открывается только для чтения
WRITE Файл открывается только для записи
READWRITE Файл открывается для чтения и записи
OF_SHARE_COMPAT Открытие файла в режиме совместимости. В этом режиме несколько приложений могут одновременно открыть файл, причем все эти приложения должны открывать файл в режиме совместимости
OF_SHARE_EXCLUSIVE Файл открывается в монопольном режиме. Для всех других приложений доступ к этому файлу на чтение и запись запрещен
OF_SHARE_DENY_WRITE После открытия файла к нему запрещается доступ со стороны других приложений на запись
OF_SHARE_DENY_READ После открытия файла к нему запрещается доступ со стороны других приложений на чтение
OF_SHARE_DENY_NONE Для открываемого файла не запрещается доступ к файлу ни на чтение, ни на запись

Если вам надо открыть файл в каталоге, где находится сама операционная система Windows или в системном каталоге Windows, воспользуйтесь функциями, соответственно, GetWindowsDirectory и GetSystemDirectory.

Функция GetWindowsDirectory позволяет определить расположение каталога, в который была установлена операционная система Windows:

UINT WINAPI GetWindowsDirectory(
  LPSTR lpSysPath,  // адрес буфера
  UINT  cbSysPath); // размер буфера

Параметр lpSysPath является указателем на буфер размером не менее 144 байт, в который будет записан путь к искомому каталогу.

С помощью параметра cbSysPath необходимо указать размер буфера в байтах.

Учтите, что операционная система Windows может быть установлена в локальном и сетевом варианте. В локальном варианте пользователь имеет доступ на запись как к тому каталогу, в который установлена операционная система Windows, так и к системному каталогу Windows. В сетевом варианте системный каталог Windows расположен на сервере и обычный пользователь имеет в этом каталоге права на чтение, но не на запись.

Для определения пути к системному каталогу Windows предназначена функция GetSystemDirectory :

UINT WINAPI GetSystemDirectory(
  LPSTR lpSysPath,  // адрес буфера
  UINT  cbSysPath); // размер буфера

Назначение параметров этой функции аналогично назначению параметров функции GetWindowsDirectory.

Так как системный каталог Windows может находиться на сервере, приложение не должно пытаться создавать или изменять файлы в этом каталоге. Как правило, пользователь не имеет права записи в системный каталог Windows.

Стандартные диалоговые панели для открытия файлов

В составе операционной системы Windows версии 3.1 имеется DLL-библиотека commdlg.dll, экспортирующая среди прочих две функции, очень удобные для организации пользовательского интерфейса при открытии файлов. Это функции GetOpenFileName и GetSaveFileName . Мы уже пользовались этими функциями в приложениях OEM2ANSI и OEM3ANSI.

Функция GetOpenFileName выводит на экран стандартную или измененную приложением диалоговую панель "Open", позволяющую выбрать файл (рис. 4.1).

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

Функция GetSaveFileName выводит стандартную или измененную приложением диалоговую панель "Save As..." (рис. 4.2).

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

Внешний вид этих диалоговых панелей определяется структурой типа OPENFILENAME , определенной в файле commdlg.h (этот файл находится в каталоге include системы разработки Borland C++ или Microsoft Visual C++):

typedef struct tagOFN
{
    DWORD   lStructSize;
    HWND    hwndOwner;
    HINSTANCE hInstance;
    LPCSTR  lpstrFilter;
    LPSTR   lpstrCustomFilter;
    DWORD   nMaxCustFilter;
    DWORD   nFilterIndex;
    LPSTR   lpstrFile;
    DWORD   nMaxFile;
    LPSTR   lpstrFileTitle;
    DWORD   nMaxFileTitle;
    LPCSTR  lpstrInitialDir;
    LPCSTR  lpstrTitle;
    DWORD   Flags;
    UINT    nFileOffset;
    UINT    nFileExtension;
    LPCSTR  lpstrDefExt;
    LPARAM  lCustData;
    UINT    (CALLBACK *lpfnHook)(HWND, UINT, WPARAM, LPARAM);
    LPCSTR  lpTemplateName;
}   OPENFILENAME;

Адрес структуры передается функциям GetOpenFileName и GetSaveFileName в качестве параметра lpofn:

BOOL WINAPI GetOpenFileName(OPENFILENAME FAR* lpofn);
BOOL WINAPI GetSaveFileName(OPENFILENAME FAR* lpofn);

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

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

Опишем назначение отдельных полей структуры OPENFILENAME.

lStructSize

Поле lStructSize перед вызовом функций должно содержать размер структуры OPENFILENAME в байтах.

Flags

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

Флаг Описание
OFN_ALLOWMULTISELECT Разрешается выбор нескольких файлов одновременно. Если указан этот флаг, после выбора поле lpstrFile будет указывать на буфер, заполненный именами выбранных файлов (или путями к выбранным файлам), разделенными пробелом
OFN_CREATEPROMPT При использовании этого флага если указанный файл не существует, создается диалоговая панель, в которой предлагается создать файл. Этот флаг устанавливается автоматически при использовании флагов OFN_PATHMUSTEXIST и OFN_FILEMUSTEXIST
OFN_ENABLEHOOK Разрешается использовать функцию фильтра, адрес которой указан в поле lpfnHook
OFN_ENABLETEMPLATE Если указан этот флаг, для создания диалоговой панели Windows будет использовать шаблон, определяемый содержимым полей hInstance и lpTemplateName
OFN_ENABLETEMPLATEHANDLE При использовании этого флага поле hInstance используется для идентификации блока памяти, содержащий предварительно загруженный шаблон диалоговой панели. В этом случае содержимое поля lpTemplateName игнорируется
OFN_EXTENSIONDIFFERENT Устанавливается после возвращения из функции и указывает, что расширение возвращенного имени файла отличается от заданного в поле lpstrDefExt. Этот флаг не устанавливается, если перед вызовом функции в поле lpstrDefExt было записано значение NULL, или если файл не имеет расширения имени
OFN_FILEMUSTEXIST Можно выбирать только имена тех файлов, которые существуют. Если в поле "File Name" диалоговой панели набрать имя несуществующего файла, на экране появится диалоговая панель с предупреждающим сообщением
OFN_HIDEREADONLY Убрать переключатель "Read Only"
OFN_NOCHANGEDIR Для выбора используется каталог, который был текущим при вызове функции
OFN_NOREADONLYRETURN Выбранные файлы не могут иметь атрибут "только чтение" или располагаться в защищенном от записи каталоге
OFN_NOTESTFILECREATE Перед завершением работы диалоговой панели создание файла не выполняется. Не выполняются и проверки на переполнение диска, защиту записи или наличие доступа в сети
OFN_NOVALIDATE В возвращаемом имени файла могут присутствовать неразрешенные символы
OFN_OVERWRITEPROMPT Используется для диалоговой панели "Save As...". Если выбранный файл существует, на экран выводится диалоговая панель с предупреждением
OFN_PATHMUSTEXIST Можно вводить только существующие пути к файлам
OFN_READONLY После вызова функции переключатель "Read Only" будет находиться во включенном состоянии
OFN_SHAREWARE Флаг устанавливается после возвращения из функции и указывает, что при вызове функции OpenFile произошла ошибка при совместном доступе к файлу в сети
OFN_SHOWHELP Если указан этот флаг, в диалоговой панели будет создана кнопка "Help". Если указан этот флаг, поле hwndOwner не должно содержать значение NULL

hwndOwner

Поле hwndOwner должно содержать идентификатор окна, создавшего диалоговую панель. Можно указать значение NULL, при этом диалоговая панель не будет иметь окно-владельца. В этом случае нельзя использовать флаг OFN_SHOWHELP.

hInstance

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

lpstrFilter

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

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

Первая строка в паре строк описывает название фильтра, например "Text Files" (текстовые файлы), во второй строке пары через символ ";" перечисляются возможные шаблоны для имен файлов.

lpstrCustomFilter

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

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

Первая строка в паре строк описывает название фильтра, например "Text Files" (текстовые файлы), во второй строке пары через символ ";" перечисляются возможные шаблоны для имен файлов.

Если поле lpstrFilter содержит NULL, используется фильтр lpstrCustomFilter.

nMaxCustFilter

Определяет размер буфера в байтах, указанного в поле lpstrCustomFilter. Размер этого буфера должен быть не меньше 40 байт.

nFilterIndex

Поле nFilterIndex определяет номер пары строк, используемой для фильтра, указанного в поле lpstrFilter. Если в качестве значения для этого поля указать 0, будет использован фильтр, определенный в поле lpstrCustomFilter.

lpstrFile

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

Если по указанному выше адресу перед вызовом функции GetOpenFileName или GetSaveFileName расположить текстовую строку, содержащую путь к файлу, этот путь будет выбран по умолчанию сразу после отображения диалоговой панели "Open" или "Save As...".

nMaxFile

Поле nMaxFile должно содержать размер в байтах буфера, расположенного по адресу, указанному в поле lpstrFile.

Размер этого буфера должен быть достаточным для записи полного пути к файлу. Файловая система MS-DOS допускает использование для указания пути к файлу не более 128 символов.

lpstrFileTitle

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

nMaxFileTitle

Поле nMaxFileTitle должно содержать размер указанного выше буфера.

lpstrInitialDir

Поле lpstrInitialDir позволяет указать начальный каталог, который будет выбран для поиска файла сразу после отображения диалоговой панели "Open". Для того чтобы начать поиск в текущем каталоге, в это поле следует записать значение NULL.

lpstrTitle

С помощью этого поля можно определить заголовок диалоговой панели, появляющейся при вызове функции. Если это поле содержит NULL, будут использованы стандартные заголовки "Open" и "Save As...".

nFileOffset

После возврата из функции это поле содержит смещение первого символа имени файла относительно начала буфера lpstrFile.

nFileExtension

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

lpstrDefExt

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

lCustData

Значение, передаваемой функции фильтра через параметр lParam.

lpfhHook

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

lpTemplatename

Идентификатор ресурса, содержащего шаблон диалоговой панели, используемого вместо имеющегося в DLL-библиотеке commdlg.dll. Для ссылки на ресурс можно использовать макрокоманду MAKEINTRESOURCE. Для использования альтернативного шаблона (и, соответственно, данного поля), в поле Flags следует установить флаг OFN_ENABLETEMPLATE.

Закрытие файлов

Теперь о том, как закрыть файл. Для закрытия файла вы должны использовать функцию _lclose :

HFILE WINAPI _lclose(HFILE hf);

Идентификатор закрываемого файла передается функции через параметр hf.

Если файл закрыт успешно, функция _lclose возвращает нулевое значение. При ошибке возвращается значение HFILE_ERROR.

Создание файлов

Для создания файлов вы можете использовать как универсальную функцию OpenFile, описанную нами ранее, так и более простую функцию _lcreat :

HFILE WINAPI _lcreat(LPCSTR lpszFileName, int fuAttribute);

В качестве параметра lpszFileName этой функции необходимо передать адрес строки, содержащей путь к создаваемому файлу в кодировке ANSI.

С помощью параметра fuAttribute можно определить атрибуты создаваемого файла:

Значение атрибута Описание
0 Нормальный файл, для которого разрешено выполнение операций чтения и записи
1 Этот файл можно открыть только для чтения
2 Скрытый файл
3 Системный файл

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

Чтение и запись

Для выполнения операций чтения и записи в программном интерфейсе операционной системы Windows версии 3.1 предусмотрены четыре функции: _lread, _hread, _lwrite, _hwrite.

Функция _lread предназначена для чтения из открытого файла:

UINT WINAPI _lread(
  HFILE hf,              // идентификатор файла
  void _huge* hpvBuffer, // адрес буфера
  UINT cbBuffer);        // размер буфера

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

Через параметр hf функции следует передать идентификатор файла, для которого необходимо выполнить операцию чтения.

Прочитанные данные будут записаны в буфер hpvBuffer, имеющий размер cbBuffer байт. Этот буфер можно получить динамически, вызывав, например, функцию GlobalAlloc или LocalAlloc. Размер буфера не должен превышать 65534 байт.

В программном интерфейсе операционной системы Windows версии 3.1 появилась функция _hread , с помощью которой можно выполнять чтение из файла блоков практически любого размера:

long WINAPI _hread(
  HFILE hf,              // идентификатор файла
  void _huge* hpvBuffer, // адрес буфера
  long cbBuffer);        // размер буфера

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

Вы можете с помощью функции GlobalAlloc заказать для функции _hread буфер размером, большим 64 Кбайт.

С помощью функции _lwrite вы можете выполнить запись данных в файл:

UINT WINAPI _lwrite(
  HFILE hf,              // идентификатор файла
  void _huge* hpvBuffer, // адрес буфера
  UINT cbBuffer);        // размер буфера

Назначение параметров этой функции аналогично назначению параметров функции _lread. Перед вызовом функции _lwrite буфер должен содержать записываемые в файл данные.

Функция возвращает количество байт данных, записанных в файл, или значение HFILE_ERROR при ошибке.

Если вам надо писать в файл блоки, имеющие размер больше 64 Кбайт, воспользуйтесь функцией _hwrite , которая впервые появилась в программном интерфейсе Windows версии 3.1:

long WINAPI _hwrite(
  HFILE hf,              // идентификатор файла
  void _huge* hpvBuffer, // адрес буфера
  long cbBuffer);        // размер буфера

Назначение параметров функции аналогично назначению параметров функции _lwrite. Функция возвращает количество байт данных, записанных в файл, или значение HFILE_ERROR при ошибке.

Позиционирование

Для выполнения операции позиционирования внутри файла приложения Windows могут использовать функцию _llseek :

LONG WINAPI _llseek(
  HFILE hf,       // идентификатор файла
  LONG  lOffset,  // смещение
  int   nOrigin); // способ использования смещения

Функция _llseek перемещает указатель текущей позиции в файле на lOffset байт, причем направление смещения зависит от значения параметра nOrigin следующим образом:

Значение Описание
SEEK_SET Указатель текущей позиции в файле перемещается на lOffset байт от начала файла
SEEK_CUR Указатель текущей позиции в файле перемещается на lOffset байт от текущей позиции
SEEK_END Указатель текущей позиции в файле перемещается на lOffset байт от конца файла

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

Функция возвращает новое значение текущей позиции от начала файла или HFILE_ERROR при ошибке.

Определение типа устройства ввода/вывода

Иногда приложению требуется определить тип и расположение используемого дискового устройства ввода/вывода. Для этого можно воспользоваться функцией GetDriveType :

UINT WINAPI GetDriveType(int DriveNumber);

Параметр DriveNumber определяет номер диска, для которого требуется определить тип и расположение (0 соответствует устройству A:, 1 - B:, и т. д.).

Функция может вернуть 0 при ошибке или одно из следующих значений:

Значение Описание
DRIVE_REMOVABLE Сменный диск
DRIVE_FIXED Несменный диск
DRIVE_REMOTE Удаленный диск, расположен на другой машине в сети

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

Для работы с файлами в приложениях Windows вы можете использовать функции из стандартной библиотеки транслятора C или C++, такие как read , fread , write , fwrite , lseek , fstat , tell , close .

Можно также использовать функции потокового ввода/вывода , если преобразовать идентификатор файла в указатель на структуру FILE при помощи функции fdopen . Для того чтобы закрыть такой файл следует воспользоваться функцией fclose .

Перечисленные выше функции были описаны в третьей книге первого тома "Библиотеки системного программиста" в главе "Файловая система DOS".

Проверка присутствия share.exe

Как мы уже говорили, в многозадачной среде утилита MS-DOS share.exe приобретает особое значение, выступая координатором доступа работающих параллельно приложений к файлам. Для того чтобы приучить забывчивых или беспечных пользователей не удалять команду загрузки этой утилиты из файла autoexec.bat вы можете сделать так, чтобы ваше приложение выдавала предупреждающее сообщение, если утилита share.exe не загружена.

Однако проблема не так проста, как кажется. Для того чтобы определить, загружена ли утилита share.exe , программы MS-DOS могли воспользоваться функцией 1000h прерывания INT 2Fh. Эта функция используется самой утилитой share.exe для предотвращения повторной загрузки.

Но вызвав эту функцию из приложения Windows, вы можете, к своему огорчению, убедиться, что она всегда сообщает о том, что share.exe загружена в память, даже если вы вообще стерли файл share.exe с диска. Это сделано специально, но не для того чтобы затруднить обнаружение share.exe, а для того чтобы предотвратить ее загрузку из виртуальной машины MS-DOS, работающей в среде Windows.

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

В качестве такой функции проще всего использовать блокирование участка файла (функция 0x5c прерывания INT 21h). Как мы уже говорили, Windows выполняет эмуляцию большого числа функций прерывания MS-DOS, позволяя приложениям Windows обращаться к этим функциям.

В листинге 4.1 приведены исходные тексты приложения ISSHARE, которое проверяет присутствие share.exe, выполняя попытку заблокировать первый байт созданного временного файла.


Листинг 4.1. Файл isshare/isshare.cpp


// =====================================================
// Приложение определяет, загружена ли утилита MS-DOS
// share.exe, и выводит соответствующее сообщение
// =====================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <dos.h>

int ShareLoaded(void);

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

  // Проверяем, загружена ли share.exe
  rc = ShareLoaded();

  if (!rc) {
    // Если код возврата равен -1, функция не смогла
    // создать на диске временный файл, необходимый
    // для выполнения проверки
    if(rc == -1)
    {
      MessageBox(NULL, "File creation error", "", MB_OK);
    }

    // Если код возврата равен 0, share.exe не загружена
    else
    {
      MessageBox(NULL, "Share NOT loaded", "", MB_OK);
    }
  }

  // Если код возврата равен 1, share.exe загружена
  else
  {
    MessageBox(NULL, "Share loaded", "SHARE Test", MB_OK);
  }

  return 0;
}

// -------------------------------------------------------
// Функция ShareLoaded
// Проверяет, загружена ли утилита share.exe
// -------------------------------------------------------

int ShareLoaded(void)
{
  HFILE hfTempFile;
  OFSTRUCT ofs;
  char szBuf[144];
  union REGS regs;
  int rc;

  // Создаем временный файл на диске 
  GetTempFileName(0, "tst", 0, szBuf);
  hfTempFile = _lcreat(szBuf, 0);

  // Если файл создать не удалось, возвращаем -1
  if (hfTempFile == HFILE_ERROR) {
    return(-1);
  }

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

  regs.x.bx = hfTempFile; // идентификатор файла
  regs.h.ah = 0x5c;       // код функции MS-DOS
  regs.h.al = 0;          // код операции блокирования
  regs.x.cx = 0;          // CX:DX - смещение в файле
  regs.x.dx = 0;
  regs.x.si = 0;         // SI:DI - длина блокируемой области
  regs.x.di = 1;

  // Вызываем функцию MS-DOS
  intdos(&regs, &regs);

  // Если установлен флаг переноса, выполнение блокирования
  // невозможно. Считаем, что в этом случае
  // утилита share.exe не загружена
  if(regs.x.cflag == 1)
  {
    rc = 0;
  }

  // Если блокирование прошло успешно,
  // разблокируем и удаляем временный файл 
  else
  {
    regs.x.bx = hfTempFile;
    regs.h.ah = 0x5c;
    regs.h.al = 1;    // код операции разблокирования
    regs.x.cx = 0;
    regs.x.dx = 0;
    regs.x.si = 0;
    regs.x.di = 1;
    intdos(&regs, &regs);
    rc = 1;
  }

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

  // Удаляем временный файл
  OpenFile(szBuf, &ofs, OF_DELETE);

  return rc;
}

Функция WinMain проверяет, загружена ли утилита share.exe, вызывая функцию ShareLoaded, определенную в приложении.

Эта функция может вернуть 0, 1 или -1. Если функция вернула 0, share.exe не загружена. Если функция вернула -1, произошла ошибка при создании временного файла. И, наконец, если функция ShareLoaded вернула 1, share.exe загружена.

Для создания временного файла используется функции GetTempFileName и _lcreat. Первая из этих двух функций получает имя временного файла, вторая - создает и открывает временный файл.

Для выполнения блокировки приложение вызывает функцию MS-DOS, пользуясь известной вам функцией intdos.

Если утилита share.exe установлена и блокировка файла выполнена успешно, функция ShareLoaded разблокирует файл, вызывая функцию MS-DOS с кодом 0x5c еще раз, но с другим значением регистра AL.

В любом случае перед возвратом из функции временный файл закрывается функцией _lclose и затем удаляется функцией OpenFile.

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


Листинг 4.2. Файл isshare/isshare.def


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

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