Определение доступных устройств для записи данных в Visual FoxPro

А. Горев, АО "Компьютерное образование", Санкт-Петербург

Не будем ждать милостей от природы,
возьмем их из Windows API!

(вольное изложение чего-то забытого)

При разработке любой программы для обработки данных всегда приходится решать вопрос об их резервировании, т. е. записи дублирующей информации на другое устройство (жесткий диск или дискету). Конечно, удобнее всего было бы сделать это в интерфейсе прикладной программы, не прибегая к инструкциям типа: "Запустите Norton Commander, перейдите на диск и т. д." Тем более, что в прикладной программе проще всего организовать и необходимый контроль за переписываемой информацией, проверить доступ пользователя к выполнению этих действий и, наконец, инициировать принудительное копирование данных.

Программисты FoxPro со стажем, конечно, помнят, что в ранних версиях для MS-DOS в комплект поставки входил загадочный файл ISDISKIN.BIN, о котором в документации не было ни слова, но который позволял проверить готовность дисковода для записи данных. После того, как FoxPro стал поддерживать API, написать подобную программу не представлялось затруднительным даже для начинающего программиста, владеющего языком Си. Одну из таких свободно распространяемых программ вы найдете на дискете, прилагаемой к журналу.

В то же время при работе с FoxPro в среде Windows для функций, написанных на Си, требуется другой формат. Эти функции отличаются от "ДОСовских" по расширению FLL вместо PLB. Если посмотреть на списки файлов, размещенных на почтовых серверах Internet, специализирующихся на информации, связанной с FoxPro, то бросается в глаза, что различных функций для среды Windows значительно меньше, чем для среды MS-DOS. Это объясняется совершенно различными причинами, в число которых, по-видимому входит и то, что писать на Си для Windows сложнее, чем для MS- DOS. С переходом на Visual FoxPro к тому же требуется 32- разрядный компилятор. Но не все так грустно. Вполне достойной компенсацией в Visual FoxPro является непосредственная поддержка вызовов стандартных функций в динамических библиотеках DLL. Что такое динамическая библиотека? Это специальный файл, содержащий процедуры или функции, которые будут являться внешними для работающего в среде Windows приложения. Возможность использования динамических библиотек Windows дает следующие преимущества:

Значительно расширяются стандартные возможности языка программирования Visual FoxPro;

Не требуется написание дополнительного кода для реализации дополнительных возможностей;

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

Динамические библиотеки могут использоваться одновременно несколькими приложениями;

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

Для использования динамических библиотек Windows нам потребуется всего лишь одна команда Visual FoxPro, DECLARE DLL, которая имеет следующий синтаксис:

DECLARE [cFunctionType] FunctionName 
 IN LibraryName [AS AliasName]
   [cParamType1 [@] ParamName1,
   cParamType2 [@] ParamName2, ...]

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

Сведения о функции определяются ее именем FunctionName, заданном в библиотеке DLL. Имя функции следует указывать с соблюдением регистра! Если функция возвращает какое-либо значение, перед ее именем следует указать тип возвращаемого значения cFunctionType.

Имя библиотеки DLL Windows LibraryName, содержащей требуемую функцию. Если вы хотите использовать функцию, входящую в API Windows, то здесь достаточно написать WIN32API, и это автоматически обеспечит поиск функции в одном из следующих файлов: KERNEL32.DLL, GDI32.DLL, USER32.DLL, MPR.DLL или ADVAPI32.DLL.

Псевдоним имени функции для использования в Visual FoxPro. Его удобно использовать для сокращения слишком длинного названия функции или при угрозе совпадения имени функции с зарезервированным словом Visual FoxPro.

Список передаваемых параметров включает обозначение типа и имени параметров, передаваемых из Visual FoxPro в функцию DLL Windows. Знак `@' после типа параметра показывает, что он будет передаваться по ссылке, а не по значению. Имя параметра никак не используется ни Visual FoxPro, ни функцией DLL и может использоваться в команде только в информационных целях. Если функция DLL требует в качестве аргумента передачи нулевого указателя (null pointer), то его следует передавать как целое число по значению. А вот в случае необходимости использования в качестве аргумента функции DLL-структуры, нам придется воздержаться от ее применения, по крайне мере до того момента, пока не станет доступен драйвер для описания структур средствами Visual FoxPro.

Если вернуться к теме нашей статьи, то используя функции API Windows, которые содержатся в соответствующих файлах DLL, мы можем без особого труда решить задачи по определению параметров среды, которые невозможно выполнить из приложения с использованием стандартных средств Visual FoxPro.

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

Список доступных логических устройств для данного компьютера мы можем получить с помощью следующей функции API Windows (в синтаксисе Си, как приведено в файле WIN32API.HLP, который поставляется вместе с профессиональной версией Visual FoxPro на CD-ROM):

DWORD GetLogicalDriveStrings
 (DWORD  nBufferLength, LPTSTR  lpBuffer);

Эта функция возвращает список доступных устройств в виде строки символов.

Тип устройства можно определить с помощью функции:

UINT GetDriveType(LPCTSTR lpRootPathName);   

Эта функция возвращает одно из следующих значений:

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

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

UINT SetErrorMode(UINT  fuErrorMode);   

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

В Visual FoxPro нет такого многообразия типов данных, как в Си ++, поэтому тип возвращаемого этими тремя функциями значений DWORD (двойное слово) или UINT (целое без знака) заменим имеющим 4 байта INTEGER.

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

Очевидно, что после прочтения в 12-м номере журнала за 1995 год статьи Михаила Корнеева создавать требуемый список вы будете на основе собственного класса списков.

Дадим этому классу имя lstDrives и установим для него следующие свойства:

BoundColumn = 2 && две колонки
ColumnLines = .F. && без разграничительных линий
FontSize = 12 && размер шрифта

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

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

* Определяем функцию для получения 
* списка доступных устройств
DECLARE INTEGER GetLogicalDriveStrings ;
  IN Win32API AS GetDrive;
   INTEGER, STRING 

* Определяем функцию для определения 
* типа устройства
DECLARE INTEGER GetDriveType IN Win32API ;
   AS GetType STRING 

* Описываем локальные переменные
LOCAL cLetter, nLetter, nType, nDrivesNum

* Резервируем строку для размещения 
* списка доступных устройств
lpString=SPACE(200)

* Определяем размер буфера для строки
nBuffSize=LEN(lpString)

* Получаем список устройств
= GetDrive(nBuffSize,@lpString)

* Определяем количество устройств
nDrivesNum = OCCURS(":",lpString)

* Получаем их имена
FOR  nLetter = 1 TO nDrivesNum
   cLetter = UPPER("  " + SUBSTR(lpString,AT;
     (":",lpString,nLetter)-1,1))
   * Определяем тип устройства
   nType = GetType(ALLTRIM(cLetter + ":\"))
   * Добавляем обозначение устройства 
   * (первая колонка) и его тип 
   * (вторая колонка) в список
   This.AddItem(cLetter, nLetter, 1)
   This.List(nLetter, 2) = STR(nType)
   * Задаем соответствующее изображение
   DO CASE
      CASE nType = 2
         This.Picture(nLetter) = "FLOPPY.BMP"
      CASE nType = 3
         This.Picture(nLetter) = "DRIVE.BMP"
      CASE nType = 4
         This.Picture(nLetter) = "NET.BMP"
      CASE nType = 5
         This.Picture(nLetter) = "CDROM.BMP"
   ENDCASE
ENDFOR

Все необходимые файлы изображений вы найдете на дискете. Мне их пришлось создать заново, т. к. обширная библиотека изображений, поставляемая с Visual FoxPro, содержала подходящие изображения только в формате ICO 32x32. В подобных списках требуются изображения 16х16. Впрочем, это легко сделать с помощью утилиты Imagedit из состава Visual FoxPro. Здесь только необходимо учесть, что при выводе изображения Visual FoxPro очень своеобразно обходится с белым цветом, считая, что его вообще нет. Если вы выведете изображение, содержащее белый цвет, например, на серую кнопку, то те места изображения, которые были белые, станут серыми. Для сохранения белого цвета для каждого изображения надо создать двойника (файл с расширением MSK), в котором на месте белого цвета должен располагаться черный. В качестве примера этой операции можно руководствоваться изображениями, которые использует Wizard (мастер) для графических кнопок управления в форме.

В событии Click класса запишем следующий код:

LOCAL cLetter
cLetter = This.DisplayValue + ":"
* Если устройством является дисковод 
* для гибких дисков, то проверяем наличие
* дискеты с помощью метода IsDiskIn
IF ALLTRIM(This.Value) = "2"
   IF This.IsDiskIn(cLetter) = .T.
      This.Parent.cmdOk.Enabled = .T.
   ELSE
      = MESSAGEBOX("Вставьте дискету " +;
         "в выбранное устройство!",0,;
         "Не готов дисковод")
      IF This.IsDiskIn(cLetter) = .T.
         This.Parent.cmdOk.Enabled = .T.
      ELSE
         This.Parent.cmdOk.Enabled = .F.
      ENDIF
   ENDIF
   RETURN
ENDIF
This.Parent.cmdOk.Enabled = .T.

Для определения наличия дискеты в дисководе создадим в нашем классе специальный метод IsDiskIn() и запишем в него следующий код:

LPARAMETERS cDrive
LOCAL lOldError 
* Регистрируем функцию SetErrorMode, 
* которая определяет, как ОС реагирует
* на некоторые критические ошибки, 
* в том числе дисковые операции
DECLARE INTEGER SetErrorMode ;
  IN Win32API INTEGER

* Определяем наличие и сохраняем 
* имя обработчика ошибок
lOldError = ON('ERROR')
ON ERROR lDiskError = .T.

* Задание функции SetErrorMode 
* с аргументом 1 позволяет приложению самому
* обрабатывать критические ошибки вместо ОС
nOldErrorState = SetErrorMode(1)

* По умолчанию считаем, что ошибки нет, 
* если не так, значение будет изменено
lDiskError = .f.

* Пытаемся найти NUL файл в загрузочной 
* области указанного устройства
lDriveState = FILE(cDrive + "\NUL")
IF .NOT. lDiskError
   IF lDriveState
         lDriveOk = .t.
   ELSE
      lDriveOk = .f.
   ENDIF
ELSE
   lDriveOk = .f.
ENDIF

* Восстанавливаем прежний обработчик ошибок
IF .NOT. EMPTY(lOldError)
   ON ERROR DO (lOldError)
ELSE
   ON ERROR
ENDIF

* Восстанавливаем реакцию на ошибки ОС
nRestState = SetErrorMode(nOldErrorState)
RETURN lDriveOk

После этого класс списка можно помещать в форме, ну, например так, как это показано на рисунке.

Если вам понравились приведенные идеи и вы их будете использовать в своих разработках, то хотелось бы сделать пару замечаний. Во- первых, если вы пока используете FoxPro 2.x for Windows, то можете обеспечить такую же функциональность списка (конечно, в виде функции или процедуры, а не класса), использовав вместо команды DECLARE DLL библиотеку FOXTOOLS.FLL. Регистрировать функцию из динамической библиотеки можно с помощью функции RegFn(), а вызывать CallFn().

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

Hosted by uCoz