Получить действительную букву диска и занято

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

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

var
  FreeAvail, totalSpace: Int64;
begin
  if SysUtils.GetDiskFreeSpaceEx(PChar('F:\'), FreeAvail, totalSpace, nil) = True
  then
  begin
    F1.Liner('Drive F total space ');
    F1.pBold(IntToStr(totalSpace div (1024 * 1024 * 1024)) + ' GB ,');
    F1.Liner(' available free space ');
    F1.pBold(IntToStr(FreeAvail div (1024 * 1024 * 1024)) + ' GB.');
  end;
end;

Но если диск не занят, мне такая ситуация не нравится.

сообщение об ошибке, если нет носителя

Вопрос: Как сделать доступными ВСЕ диски - CDROM, USB Stick и т.д. Чтобы быть более конкретным, я хочу, чтобы результат отображался, как в этом примере;

Диск E [Локальный диск] — TotalSpace 500 ГБ — FreeSpace 200 ГБ

Диск F [CD-привод] - Незанят - FreeSpace 0

Диск G [Съемный] — TotalSpace 8 ГБ — FreeSpace 2 ГБ


person Bianca    schedule 18.10.2014    source источник
comment
Помогают ли вам GetVolumeInformation и GetDriveType?   -  person Graymatter    schedule 19.10.2014
comment
Спасибо, буду копать больше об этих двух вещах.   -  person Bianca    schedule 19.10.2014


Ответы (2)


Я предоставил пару функций, которые могут помочь. Первый использует функцию Win32 API GetLogicalDriveStrings для получения списка назначенных букв дисков на компьютере. Второй запрашивает диск, чтобы узнать, готов ли он к использованию (есть ли в нем диск). (Есть также служебная функция, которая преобразует букву диска в целочисленное значение, необходимое для DiskSize, старой функции ввода-вывода Pascal.)

Код работает со времен Win95 и только что был протестирован на 64-разрядной версии Win7 в консольном приложении Delphi 2007. Ниже приведено консольное тестовое приложение.

program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows, Types;

// Returns an array filled wit the assigned
// drive letters on the current computer.
function  GetDriveList: TStringDynArray;
var
  Buff: array[0..128] of Char;
  ptr: PChar;
  Idx: Integer;
begin
  if (GetLogicalDriveStrings(Length(Buff), Buff) = 0) then
    RaiseLastOSError;
  // There can't be more than 26 lettered drives (A..Z).
  SetLength(Result, 26);      

  Idx := 0;
  ptr := @Buff;
  while StrLen(ptr) > 0 do
  begin
    Result[Idx] := ptr;
    ptr := StrEnd(ptr);
    Inc(ptr);
    Inc(Idx);
  end;
  SetLength(Result, Idx);
end;

// Converts a drive letter into the integer drive #
// required by DiskSize().
function DOSDrive( const sDrive: String ): Integer;
begin
  if (Length(sDrive) < 1) then
    Result := -1
  else
    Result := (Ord(UpCase(sDrive[1])) - 64);
end;

// Tests the status of a drive to see if it's ready
// to access. 
function DriveReady(const sDrive: String): Boolean;
var
  ErrMode: Word;
begin
  ErrMode := SetErrorMode(0);
  SetErrorMode(ErrMode or SEM_FAILCRITICALERRORS);
  try
    Result := (DiskSize(DOSDrive(sDrive)) > -1);
  finally
    SetErrorMode(ErrMode);
  end;
end;

// Demonstrates using the above functions.
var
  DrivesArray: TStringDynArray;
  Drive: string;
const
  StatusStr = 'Drive %s is ready: %s';
begin
  DrivesArray := GetDriveList;
  for Drive in  DrivesArray do
    WriteLn(Format(StatusStr, [Drive, BoolToStr(DriveReady(Drive), True)]));
  ReadLn;
end.

Пример вывода при запуске в моей системе (Win7 64, два физических жестких диска (C: и D:), устройство ISO без смонтированного образа (E:) и DVD-привод (Z:).

Drive C:\ is ready: True 
Drive D:\ is ready: True 
Drive E:\ is ready: False
Drive Z:\ is ready: True
person Ken White    schedule 18.10.2014
comment
Это хорошо, но вызовы SetLastError немного не те. Это очень необычный API. Как вы написали, вы удалите все уже установленные флаги. Я думаю, что в соответствии с моим ответом требуется два звонка. - person David Heffernan; 19.10.2014
comment
@ Дэвид: Нет, это не так. Он сохраняет предыдущее значение (которое возвращается при первом вызове SetErrorMode) и впоследствии восстанавливает его. (Есть два вызова - см. блок finally.) - person Ken White; 19.10.2014
comment
Нет. Между первым и вторым вызовами вы потеряли все флаги, которые были там с самого начала. Вам потребуется три вызова, чтобы добавить флаг, а затем восстановить. Лучше всего добавить их все и запустить и оставить на этом. - person David Heffernan; 19.10.2014
comment
@David: № SetErrorMode возвращает предыдущие флаги в соответствии с документы. Я сохраняю эти флаги, устанавливаю только SEM_FAILCRITICALERRORS, а затем восстанавливаю предыдущее значение флага в блоке finally. - person Ken White; 19.10.2014
comment
Осторожно, ErrorMode является глобальным для процесса и поэтому уязвим, если потоки меняют режим. - person LU RD; 19.10.2014
comment
@LURD: Да, это так. Win7 добавляет SetThreadErrorMode для решения этой проблемы, но он недоступен в более ранних версиях ОС. Вот почему я меняю и восстанавливаю его как можно быстрее, а не устанавливаю для всего процесса. Однако вполне возможно, что за этот короткий промежуток времени он мог быть изменен потоком. - person Ken White; 19.10.2014
comment
Я попробую еще раз. Учтите, что SetErrorMode(SEM_FAILCRITICALERRORS) может снять другие флаги, если они были установлены. Да, вы добавите их обратно чуть позже, но что, если они нужны вам все время? В идеале вы хотите добавить SEM_FAILCRITICALERRORS к тому, что уже было. Даже тогда это такой опасный API для всего процесса, что один вызов при запуске — это путь. - person David Heffernan; 19.10.2014
comment
@David: Извините, но я не согласен. Один вызов при запуске изменяет флаги для всего вашего процесса на время его выполнения, и здесь этого делать не нужно. Очень быстрое изменение флагов почти не влияет, и мой код быстро сохраняет и восстанавливает предыдущие флаги. Единственный риск (который применим только в том случае, если ваше приложение является многопоточным, как упоминалось в комментарии LURD ранее) — это короткая продолжительность вызова одной функции. У вас есть цитата, указывающая на то, что мой код неверен? В документации по API этого нет. - person Ken White; 19.10.2014
comment
Есть два риска. Threading и для кода между двумя вызовами SetErrorMode. blogs.msdn.com/b/oldnewthing/archive/ 2004/07/27/198410.aspx - person David Heffernan; 19.10.2014
comment
@David: Вашей цитате (из блога Рэймонда) уже десять лет, и все изменилось. Например, в сообщении блога конкретно указано, что нет GetErrorMode. Я не уверен, что это все еще применимо, хотя может. У меня никогда не было проблем с кодом, размещенным здесь. Я принципиально против отключения критических ошибок при запуске процесса. - person Ken White; 19.10.2014
comment
Так что используйте GetErrorMode для увеличения. Это делает код чище. Но не удаляйте все остальные флаги так, как делаете вы. Ваш код удаляет SEM_NOOPENFILEERRORBOX. Единственный надежный способ использовать SetErrorMode — один раз в начале процесса, когда выполняется только один поток. - person David Heffernan; 19.10.2014
comment
И никто не говорит об отключении критических ошибок. Дело в том, что мы контролируем, как обрабатываются эти ошибки. Либо системой с ее диалогом, либо приложением из-за сбоя функции API. - person David Heffernan; 19.10.2014
comment
И вот этот msdn.microsoft. com/en-us/library/windows/desktop/ Рекомендуется, чтобы все приложения вызывали функцию SetErrorMode для всего процесса с параметром SEM_FAILCRITICALERRORS при запуске. Это необходимо для предотвращения зависания приложения в диалоговых окнах режима ошибок. - person David Heffernan; 19.10.2014
comment
@ Дэвид: Вау. Немного одержим? :-) Я явно имел в виду диалоги критических ошибок. Однако спасибо за последнюю ссылку; Я этого не видел. Я соответствующим образом изменю свой ответ завтра утром, когда смогу его проверить - здесь уже поздно, и, в отличие от вас, большинству из нас иногда нужно спать. :-) - person Ken White; 19.10.2014
comment
Не одержим. Но обязательно бессонница. - person David Heffernan; 19.10.2014
comment
@David: Не хотел добавлять к причинам, по которым ты не мог спать. Изменения кода работают на вас? - person Ken White; 19.10.2014
comment
да. Лучше. Однако я действительно считаю, что эти флаги должны быть установлены один раз при запуске. То, что это не так, на самом деле связано с обратной совместимостью. MSDN говорит вам добавить их при запуске процесса. В любом случае, мы высказали свое мнение. Читатели могут сформировать собственное мнение. Здесь все хорошо. - person David Heffernan; 19.10.2014
comment
FWIW GetLogicalDriveStrings принимает длину в символах (а не в байтах). Таким образом, этот код семантически неверен для Unicode Delphi. И легче сохранить возвращаемое значение, которое является длиной строки. - person David Heffernan; 19.10.2014
comment
@David: Только что протестировал его в XE6, точно так, как написано здесь, и он отлично работает. Что семантически неправильно? Выделенный буфер достаточен (128 символов), GetLogicalDriveStrings никогда не может возвращать больше, чем 26 * 3 * SizeOf(Char), и вопрос о том, проще ли использовать возвращаемое значение, зависит от мнения. - person Ken White; 19.10.2014
comment
@Ken Вы лжете API о размере буфера, который, как вы говорите, в два раза больше, чем он есть. 26 букв в алфавите означают, что это не вызывает ошибки. Это все равно неправильно. Этого нельзя отрицать. Документация не может быть более ясной. Очень распространенная ошибка. Лучше не вводить новичков в заблуждение, подавая плохой пример. Если вы обычно используете D2007, вы не будете так остро реагировать на эту проблему. Оставьте ответ как есть. Или нет. Мне, честно говоря, все равно, что ты делаешь. Вам решать. Бессмысленно что-либо тестировать. Это все есть в документах. - person David Heffernan; 19.10.2014
comment
@David: Ты просто ведешь себя глупо. Все значения `A..Z, :, \` всегда будут однобайтовыми, а Windows не локализует буквы дисков. Таким образом, максимальная длина каждой назначенной буквы диска составляет 4 байта (буква диска + ':\' + нулевой терминатор. Максимальное количество букв диска составляет 26, что означает, что максимальный буфер, когда-либо необходимый, будет 26 * 4 байта плюс один для последний двойной терминатор или байты 105. Пока то, что предоставляется, больше по размеру, это совершенно безопасно, а выделение фиксированного массива символов упрощает код. - person Ken White; 19.10.2014
comment
Для Unicode Delphi вы передаете размер буфера, измеренный в байтах, но API хочет длину в символах. Для UTF16 один символ равен двум байтам. Передайте Length(), а не SizeOf(). Я могу отредактировать ответ, если это поможет вам понять. - person David Heffernan; 19.10.2014
comment
@David: я изменил SizeOf на Length. - person Ken White; 19.10.2014

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

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

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

Вызовите следующую функцию один раз при запуске:

procedure SetProcessErrorMode;
var
  CurrentMode: DWORD;
begin
  CurrentMode := SetErrorMode(0);
  SetErrorMode(CurrentMode or SEM_FAILCRITICALERRORS
    or SEM_NOOPENFILEERRORBOX);
end;

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

Рекомендуется, чтобы все приложения вызывали функцию SetErrorMode для всего процесса с параметром SEM_FAILCRITICALERRORS при запуске. Это необходимо для предотвращения зависания приложения в диалоговых окнах режима ошибок.

Я лично рекомендую также добавить SEM_NOOPENFILEERRORBOX.

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

person David Heffernan    schedule 18.10.2014
comment
Спасибо, сэр, этот код работает. Приведенный выше код также получен от вас. Вы помогали мне снова и снова. Очень ценю это. - person Bianca; 19.10.2014