Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder - всякая всячина (№19. Особенности разработки консольных приложений)


Служба Рассылок Subscribe.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

№19. Особенности разработки консольных приложений (Вопрос 3 - получение информации о дисках)

"...
Из-за плеча Бендера на папку смотрели молочные братья.
- Что там внутри? - спросил любопытный Паниковский.
- О! - сказал Остап. - Там внутри есть все: пальмы, девушки, голубые экспрессы, синее море, белый пароход, мало поношенный смокинг, лакей-японец, собственный бильярд, платиновые зубы, целые носки, обеды на чистом животном масле и, главное, мои маленькие друзья, слава н власть, которую дают деньги. И он раскрыл перед изумленными антилоповцами пустую папку.
..."
(И.Ильф и Е.Петров. "Золотой теленок")

 

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

Поскольку мы создаем компактное приложение, нам придется отказаться от использования горячо мною любимого типа AnsiString и все операции со строками производить только посредством функций стандартной библиотеки. Из нее мы будем использовать функцию форматирования строки символов sprintf. Для ее использования нам необходимо включить в главный файл нашего консольного приложения (вы его уже создали?) следующую директиву препроцессора:

Кроме того, для ограничения длины некоторых строк объявим следующую константу:

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

char *DriveTypes[]=    // Возможные типы носителя
{
    "неопределенный тип диска",
    "ошибка в имени диска",
    "съемный диск",
    "жесткий диск",
    "удаленный (сетевой) диск",
    "CD-ROM",
    "виртуальный диск"
};

На этом мы заканчиваем подготовительный этап и переходим к реализации главной функции программы:

//---------------------------------------------------------------------------
 
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    char *DriveLabel= new char [MAX_STR_LEN];    // Буфер метки диска
    char *FATNameStr= new char [MAX_STR_LEN];    // Буфер названия файловой системы
    char DriveName[4]= "a:\\";    // Буфер имени диска
    DWORD Drives= GetLogicalDrives();    // Получаем закодированную информацию о дисках
    if (Drives)     // (1 бит на каждый диск)
    {
      int i= 0;
      int Last= sizeof(DWORD) * 8;
      DWORD Mask= 1;
      while (i<Last)
      {
        if ((Drives&Mask)!=0)
        {
          DWORD Serial;
          DWORD MaxFileName;
          DWORD FileSysFlags;
          DriveName[0]= ((char)('a' + i));    // Вычисляем имя диска
          DWORD DriveType= GetDriveType(DriveName);    // Получаем тип диска
          if (GetVolumeInformation(DriveName, DriveLabel, MAX_STR_LEN - 1,
            &Serial, &MaxFileName, &FileSysFlags, FATNameStr, MAX_STR_LEN - 1))
          {    // Получаем информацию о логическом диске
            ULARGE_INTEGER TotalSpace;
            ULARGE_INTEGER TotalFreeSpace;
            ULARGE_INTEGER UserFreeSpace;
            GetDiskFreeSpaceEx(DriveName, &UserFreeSpace, &TotalSpace, &TotalFreeSpace);
            char *MessageStr= new char[4096];
            sprintf(MessageStr, "Диск:\t\t\t%s\nМетка тома:\t\t%s\nСерийный номер:\t\t%u\nТип диска:\t\t%s\nФайловая система:\t%s\nМакс. длина имени файла:\t%u байт\nВсего места на диске:\t%Lu байт\nСвободно:\t\t%Lu байт\nСвободно для вас:\t%Lu байт",
              DriveName, DriveLabel, Serial, DriveTypes[DriveType], FATNameStr, MaxFileName, TotalSpace, TotalFreeSpace, UserFreeSpace);    // Переводим Серийный номер в строку
            MessageBox(NULL, MessageStr, "Информация о диске",
              MB_OK | MB_ICONINFORMATION);
            delete MessageStr;
          }
        }
        i++;
        Mask= Mask<<1;    // Переходим к обработке следующего бита информации
      }
    }
      // Удаляем строковые буферы из памяти
    delete FATNameStr;
    delete DriveLabel;
    return 0;
}
//---------------------------------------------------------------------------

Начиная с этого выпуска я постараюсь оформлять исходные тексты программ максимально приближенно к тому виду, в каком он отображается в редакторе Билдера. Могу только посочуствовать двум моим подписчикам, получающим рассылку в текстовом формате: вы очень много теряете (!!!). Если у кого-то возникнут проблемы с отображением текста программ - пишите мне. На размере выпусков это не должно сильно сказаться, а с дополнительной нагрузкой я готов смириться.

Теперь о деле: те строки, которые я пометил комментариями я подробно "обсасывать" не буду - их назначение, я надеюсь, понятно всем. Первая незнакомая функция - это GetLogicalDrives. Она возвращает целочисленную беззнаковую переменную (4 байта), в которой каждому биту информации сопоставлен диск, зарегистрированный в системе. То есть, если соответствующий бит переменной установлен в 1, то в системе присутствует диск с определенным именем, если - 0, то такого диска в системе нет. Нулевой бит переменной "отвечает" за диск "A:", первый - за диск "B:", второй - за диск "C:" и так далее... Переменной Mask мы присваиваем значение 1, соответствующее двоичной еденице. В дальнейшем, в цикле мы последовательно сдвигаем этот единственный установленный бит к старшим разрядам и в битовой операции Drives&Mask с его помощью "скрываем" все биты переменной Drives, которые "не относятся" к проверяемому в текущей итерации цикла диску. Если битовая операция дает значение отличное от нуля, то это означает, что в системе присутствует соответствующий диск и мы вычисляем его имя, присваеваем его переменной DriveName и переходим к получению информации о нем.

Во первых, мы определяем с помощью функции GetDriveType тип носителя (переменная DriveType). Во вторых, мы вызываем функцию GetVolumeInformation, с помощью которой получаем дополнительную информацию о диске. В качестве параметров передаем ей следующие данные:

  • DriveName - имя диска, например, "c:\"
  • DriveLabel - указатель на буфер для метки диска
  • MAX_STR_LEN - 1 - длина этого буфера
  • &Serial - адрес переменной, в которой будет сохранен серийный номер диска
  • &MaxFileName - адрес переменной, в которую будет помещено значение максимальной длины имени файла для файловой системы данного диска. Например, для FAT32 и NTFS это значение равно 255 символов, для CDFS - файловой системы CD-ROM - 110 символов.
  • &FileSysFlags - адрес переменной, куда записываются различные флаги свойств диска и его файловой системы. В частности, здесь будет храниться информация о том, поддерживает ли диск сжатие (в том числе и на уровне файлов), как он "относится" регистру в названиях файлов, поддерживает ли его файловая система ACL (контроль доступа) и т.д. Я не буду использовать в данной программе эте информацию, поскольку выпуск и так получается не маленьким.
  • FATNameStr - указатель на буфер, в котором будет сохранено название файловой системы (например, "FAT32", "NTFS", "CDFS" и т.д.)
  • MAX_STR_LEN - 1 - длина этого буфера

Примечание 1: серийные номера логических дисков, например, дисков "c:" и "d:", различаются, то есть, это не серийные номера "железа", а номера, назначаемые системой каждому логическому устройству.

Примечание 2: серийные номера CD-дисков уникальны и присваиваются при их штамповке или при первой операции записи на этот диск (для CD-R и CD-RW). Как это можно использовать - догадайтесь сами.

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

Есть еще одна очень полезная функция: GetDiskFreeSpaceEx, которая возвращает информацию об объеме диска и о свободном месте на этом диске. Есть еще одна реализация этой функции - GetDiskFreeSpace, которая оперирует 31-разрядными значениями и, следовательно, возвращает неверную информацию для дисков емкостью более 2 Гигабайт. К сожалению, функция GetDiskFreeSpaceEx будет работать только в системе не ниже Windows 95 OSR2, поэтому, если вы желаете в своей программе предусмотреть ВСЕ случаи, вам придется сделать дополнительную проверку версии операционной системы. Для своей работы функция GetDiskFreeSpaceEx желает иметь следующие параметры:

  • DriveName - имя диска
  • &UserFreeSpace - адрес переменной, в которую будет помещена информация о свободном месте на данном диске, которое может быть выделено данному пользователю (пользователю, вызвавшему данную функцию, запустившему программу). Эта переменная может иметь значение, отличное от значения переменной TotalFreeSpace только в том случае, если в системе введено "квотирование" дискового пространства для разных пользователей. Как правило, такую операцию можно реализовать только в системах семейства WinNT и только на файловой системе NTFS.
  • &TotalSpace - адрес переменной, которая будет содержать информацию об объщем объеме данного диска
  • &TotalFreeSpace - адрес переменной, в которой будет содержаться информация о фактическом свободном пространстве на диске независимо от того, какой пользователь вызвал данную функцию.

Примечание: данная функция оперирует 64-разрядными параметрами (ULARGE_INTEGER), которые затем без проблем конвертируются в 64-разрядные переменные типа __int64, которыми оперирует функция sprintf.

Вот, практически, и все. Осталось вывести полученную информацию пользователю. Для этого мы с помощью функции sprintf сформируем строку в памяти (под которую я, не пытаясь угадать ее длину, выделил динамический буфер размером 4Kb). Первым оператором указанной функции идет указатель на этот буфер MessageStr. За ним идет строка форматирования, с помощью которой "разбираются" все оставшиеся параметры данной функции. Мною были использованы следующие "спецификаторы форматирования": %s - для строковых аргументов, %u - для 32-разрядных целочисленных аргументов и %Lu - для 64-разрядных целочисленных аргументов. Кроме того, в строке форматирования были использованы спецсимволы: \t - символ табуляции и \n - символ перехода на новую строку. Все аргументы, перечисленные после строки форматирования повторяют порядок следования "своих" спецификаторов. Вообще, пользоваться функциями printf и sprintf учат на первых ознакомительных уроках по языку C, поэтому моя совесть строителя коммунизма абсолютна чиста.

Вывод информационной строки на экран осуществляется с помощью функции MessageBox, которой в качестве первого аргумента мы передаем хендл окна (NULL в нашем случае), информационную строку, заголовок выводимого окна и, наконец, комбинацию констант, определяющую вид окна (в нашем случае это окно с иконкой "i" в треугольнике и одной кнопкой "Ok").

Запустите ваше приложение. Рекомендую предварительно вставить дискету в дисковод и CD-диск в CD-ROM. Программа должна последовательно вывести несколько окон с информацией о дисках, установленных в системе. Надеюсь, что все, что мною было описано в данном выпуске вам когда-нибудь пригодится в ваших разработках.

Как всегда на сайте рассылки вы сможете скачать архив выпуска, содержащий все файлы данного проекта.



С уважением, Васильев Евгений...
Почта: vasco@homeline.ru
Сайт: http://www.homeline.ru/vasco


http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное