Delphi - convert physical path (device File handle

2019-04-11 10:48发布

问题:

How can I convert a path like

\Device\HarddiskVolume3\Windows

into its corresponding virtual path? (like c:\Windows in this case)

回答1:

Personally I prefer the native way:

function GetHDDDevicesWithDOSPath:TStringlist;
var
  i: integer;
  root: string;
  device: string;
  buffer: string;
begin
  setlength(buffer, 1000);
  result:=TStringlist.create;
  for i := Ord('c') to Ord('z') do
  begin
    root := Char(i) + ':';
    if (QueryDosDevice(PChar(root), pchar(buffer), 1000) <> 0) then
    begin
      device := pchar(buffer);
      result.add(format('%s = %s\',[device, root ]));
    end;
  end;
end;

NB: This code sample is taken from: http://www.delphipraxis.net/165249-auflistung-devices.html

This will return a map between logical drive and path. In my case:

\Device\HarddiskVolume2 = c:\
\Device\HarddiskVolume3 = d:\
\Device\IsoCdRom0 = e:\
\Device\CdRom0 = f:\
\Device\hgfs\;Z:0000000000084af9\vmware-host\Shared Folders = z:\

You have to replace "\device\harddisk" part in your path with coresponding drive letter.

Please, note drive letters are user dependent. Some useful links:

  • Defining an MS-DOS Device Name
  • Naming Files, Paths, and Namespaces
  • coverting physical drive to logical drive how to?


回答2:

One of approaches would be using WMI, for example http://www.magsys.co.uk/delphi/magwmi.asp - it also has a demo that you can use to take code samples from.

You can find a number of WMI Explorers and explorer the configuartiona classes and construct queries. To name few free ones:

  • Microsoft WMI CIM Studio is bad at making queries, but can be nice to view classes
  • WMI Explorer from www.ks-soft.net turned to be the one i used the most

You would also need to google for WMI Query Language examples and read specifications: while WMI language resembles SQL queries it has a bit different syntax and a set of hard to predict non-uniform restrictions.


Personally i used it in the different direction: i needed to make a list of volumes (drive letters), grouped by physical disk or network server.

I ended with (quick and quite ugly, but sufficient for one-time work at program initialization) unit like below. You can do stream-lining on it, and you'd definitely would have to reverse requests and functions.

unit WMI_Helper;

interface

function WMINetDiskName(const disk: string { 'C:' - w/o slash } ): string;
function WMIPhysDiskName(const disk: string { 'C:' - w/o slash } ): string;
function WMIGetVolumeName(const disk: string { 'C:' - w/o slash } ): string;

implementation

uses magwmi, SysUtils, StrUtils, Windows, IOUtils;

function WMIGetProp(const query, param, resultProp: string): string;
begin
  if MagWmiGetOneQ(StringReplace(query, '%s', param, []), resultProp, Result) <= 0
  then
    Result := '';
  Result := Trim(Result);
end;

function WMINetDiskName(const disk: string { 'C:' - w/o slash } ): string;
const
  req = 'select ProviderName from Win32_MappedLogicalDisk where DeviceID = "%s"';
  prop = 'ProviderName';
var
  i: integer;
begin
  Result := WMIGetProp(req, disk, prop);

  If not TPath.IsUNCPath(Result) then
    exit('');

  i := PosEx('\', TPath.GetPathRoot(Result), 3);
  if i <= 0 then
    exit('');

  SetLength(Result, i - 1);
end;

function WMIPhysDiskName(const disk: string { 'C:' - w/o slash } ): string;
const
  resultProp = 'DeviceID';
  reqPart = 'ASSOCIATORS OF {Win32_LogicalDisk.DeviceID="%s"} WHERE ResultClass=Win32_DiskPartition';
  reqDisk = 'ASSOCIATORS OF {Win32_DiskPartition.DeviceID="%s"} WHERE ResultClass=Win32_DiskDrive';
begin
  Result := WMIGetProp(reqPart, disk, resultProp);
  if Result > '' then
    Result := WMIGetProp(reqDisk, Result, resultProp);
end;

function WMIGetVolumeName(const disk: string { 'C:' - w/o slash } ): string;
const
  prop = 'VolumeName';
  reqNet = 'select VolumeName from Win32_MappedLogicalDisk where DeviceID = "%s"';
  reqPhy = 'select VolumeName from Win32_LogicalDisk where DeviceID = "%s"';

begin
  Result := WMIGetProp(IfThen(GetDriveType(PChar(disk)) = DRIVE_REMOTE, reqNet,
    reqPhy), disk, prop);
end;

end.

I did not tried letter-less volumes (such as c:\ and c:\Windows would be one partition, and c:\Windows\Temp would reside on quite another disk), but if you need to account for such configuration, i believe you can make a proper query in WMI Explorer and add it to your program as well.