Лучший способ всегда обнаружить съемное устройство - PullRequest
6 голосов
/ 22 декабря 2009

В моем предыдущем вопросе «Как найти уникальный серийный номер флеш-устройства?» Я закончил тем, что спросил способ получить букву диска. Эта проблема решена.

Тем не менее, мой первоначальный вопрос не получил ответа. Я хотел, чтобы можно было различать съемные устройства (USB-накопители, SD-карты, (внешние жесткие диски?) И т. Д.) И всегда иметь возможность снова их распознавать при повторном подключении. Это также должно быть возможно на любом другом компьютере. К счастью, меня не волнует форматирование дисков (если / когда они в моей программе считаются новыми дисками), поэтому я могу использовать идентификаторы разделов и томов как часть моего распознавания? Я спрашиваю это, поскольку PNPDeviceID является НЕ уникальным. Я обнаружил, что это зависит от аппаратного обеспечения его чтения, см. Ниже изображения:

alt text

alt text

Итак, я ищу способ обнаружения и распознавания любого съемного устройства на любом компьютере с помощью следующего: Win32_DiskDrive , Win32_DiskPartition , Win32_LogicalDisk, Я благодарю RRUZ за оригинальный код:

program GetWMI_USBConnectedInfo;

{$APPTYPE CONSOLE}

uses
  Windows,
  Classes,
  ActiveX,
  Variants,
  SysUtils,
  WbemScripting_TLB in '..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';

procedure  GetUSBDiskDriveInfo;
var
  WMIServices  : ISWbemServices;
  Root,a,b     : ISWbemObjectSet;
  Item,Item2   : Variant;
  i,ii,iii,iiii: Integer;
  start,stop,freq:Int64;
begin
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(start);

  WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  Root := WMIServices.ExecQuery('Select * From Win32_DiskDrive','WQL', 0, nil);
  for i := 0 to Root.Count - 1 do
  begin
    Item := Root.ItemIndex(i);
    for ii := VarArrayLowBound(Item.Capabilities, 1) to VarArrayHighBound(Item.Capabilities, 1) do if (Item.Capabilities[ii] = 7) then begin
      Writeln('Caption      '+VarToStr(Item.Caption));
      Writeln('Name         '+VarToStr(Item.Name));
      Writeln('DeviceID     '+VarToStr(Item.DeviceID));
      Writeln('Partitions   '+VarToStr(Item.Partitions));
      Writeln('PNPDeviceID  '+VarToStr(Item.PNPDeviceID));
      Writeln('SerialNumber '+VarToStr(Item.SerialNumber));
      Writeln('Signature    '+VarToStr(Item.Signature));

      a := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskDrive.DeviceID=''' + VarToStr(Item.DeviceID) + '''} WHERE AssocClass = Win32_DiskDriveToDiskPartition','WQL', 0, nil);
      for iiii := 0 to a.Count - 1 do begin
        b := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID=''' + VarToStr(Variant(a.ItemIndex(iiii)).DeviceID) + '''} WHERE AssocClass = Win32_LogicalDiskToPartition','WQL', 0, nil);
        for iii := 0 to b.Count - 1 do begin
          Item2 := b.ItemIndex(iii);
          Writeln('Drive = ' + Item2.Caption);
        end;
      end;
      Writeln;
      Writeln;
    end;
  end;
  QueryPerformanceCounter(stop);
  if (freq > 0) then
    Writeln('Time took: ' + FloatToStr((stop-start) / freq))
  else
    Writeln('Unable to measure time!');
end;

begin
  try
    CoInitialize(nil);
    GetUSBDiskDriveInfo;
    Readln;
    CoUninitialize;
  except
    on E:Exception do
    Begin
        CoUninitialize;
        Writeln(E.Classname, ': ', E.Message);
        Readln;
    End;
  end;
end.

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

Окончательное редактирование
Я прочитал, что разработчики могут безопасно использовать ID раздела / тома для распознавания. Могу ли я рассчитывать на это?

Решение:
Итак, поскольку чтение «уникальных» идентификаторов не является жизнеспособным решением, есть два способа обойти это:

  1. Сохраните скрытый файл на диске с уникальным идентификатором, который может распознать программа (сравните с локальной базой данных).
  2. Сохраните все, что связано с диском , на диске в виде скрытого файла настроек. Я выбрал этот подход, так как сама программа не имеет никаких настроек. Все настройки на раздел. Это также делает настройки / программы переносимыми.

Ответы [ 2 ]

2 голосов
/ 23 декабря 2009

USB-устройства должны иметь уникальный идентификатор. Я успешно использовал это в прошлом в приложении .NET, и оно должно одинаково хорошо работать в приложении Delphi.

Вы должны иметь возможность импортировать COM-объекты WMI в Delphi с помощью редактора библиотеки типов, чтобы вы могли использовать раннее связывание вместо вариантов.

Если вам нужен реальный пример, мне нужно найти код C #. Напишите мне личное сообщение или письмо, если вам это нужно.

Просто взглянул, как вы делаете это в .NET: вы используете Генератор классов со строгим управлением (Mgmtclassgen.exe) , который недоступен в собственном мире.

Я не совсем уверен насчет устройств не-USB. Вы ожидаете, что идентификаторы PNP будут одинаковыми для устройств, но вы утверждаете, что это не так, однако я не вижу одинаковые идентификаторы PNP для разных устройств в вашем примере (если, конечно, я могу упустить что-то очевидное здесь).

- Йерун

USB-устройства имеют уникальный идентификатор.

Найдены несколько частей кода C #, в котором перечислены все USB-устройства:

    public static List<DiskDrive> GetUsbDiskDrives()
    {
        DiskDrive.DiskDriveCollection diskDrives = DiskDrive.GetInstances("InterfaceType = 'USB'");
        return DiskDriveList(diskDrives);
    }

    public static List<DiskDrive> DiskDriveList(DiskDrive.DiskDriveCollection diskDrives)
    {
        List<DiskDrive> result = new List<DiskDrive>();
        foreach (DiskDrive diskDrive in diskDrives)
        {
            result.Add(diskDrive);
        }
        return result;
    }

    public static string Serial(DiskDrive diskDrive)
    {
        // pick the last portion of diskDrive.PNPDeviceID:
        string[] splitted = diskDrive.PNPDeviceID.Split('\\'); // note this becomes one backslash
        string result = splitted[splitted.Length - 1];
        return result;
    }

Вышеуказанные части используют пространство имен .NET System.Collections.Generic, чтобы вы могли иметь общий список.

DiskDrive находится в пространстве имен Win32.WMI в файле C #, созданном с помощью этой команды:

MgmtClassGen.exe Win32_DiskDrive /oWin32.WMI
2 голосов
/ 23 декабря 2009

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

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

РЕДАКТИРОВАТЬ Я не уверен, что вы можете абсолютно обойти это для всех устройств в целом. Аппаратные средства от каждого поставщика различны, и спецификации подходят для интерпретации, поэтому серийный номер для некоторых устройств является нулевым, а для некоторых - нет. Ваша единственная надежда состоит в том, чтобы либо самостоятельно поставлять оборудование, либо требовать конкретного оборудования, которое ведет себя предсказуемо.

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

...