?
Solved

How to link hard drive numbers and drive letters

Posted on 2009-02-11
10
Medium Priority
?
2,231 Views
Last Modified: 2012-05-06
Hi Experts,
Can anyone supply me with working Delphi functions to link physical hard drive numbers and assigned drive letters?

Something like:
function GetDriveNumber(const DriveLetter: char): integer

procedure GetDriveLetters(const DriveNumber: integer; var DriveLetters: array of char)
(need some way of storing multiple  drive letters as a drive may be partitioned into several areas each with their own drive letter of course)

Thanks!!
0
Comment
Question by:WinRat
  • 5
  • 2
  • 2
  • +1
10 Comments
 
LVL 13

Assisted Solution

by:ThievingSix
ThievingSix earned 520 total points
ID: 23618015

function GetLD(Drive: Char): Cardinal;
var
  Buffer : String;
begin
  Buffer := Format('\\.\%s:',[Drive]);
  Result := CreateFile(PChar(Buffer),GENERIC_READ Or GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  If Result = INVALID_HANDLE_VALUE Then
    begin
    Result := CreateFile(PChar(Buffer),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  end;
end;
 
 
function GetPhysicalDiskNumber(Drive: Char): Byte;
var
  LD : DWORD;
  DiskExtents : PVolumeDiskExtents;
  DiskExtent : TDiskExtent;
  BytesReturned : Cardinal;
begin
  Result := 0;
  LD := GetLD(Drive);
  If LD = INVALID_HANDLE_VALUE Then Exit;
  Try
    DiskExtents := AllocMem(Max_Path);
    DeviceIOControl(LD,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,nil,0,DiskExtents,Max_Path,BytesReturned,nil);
    If DiskExtents^.NumberOfDiskExtents > 0 Then
      begin
      DiskExtent := DiskExtents^.Extents[0];
      Result := DiskExtent.DiskNumber;
    end;
  Finally
    CloseHandle(LD);
  end;
end;

Open in new window

0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 23618018
And you'll need these constants
  TDiskExtent = record
    DiskNumber: Cardinal;
    StartingOffset: Int64;
    ExtentLength: Int64;
  end;
  DISK_EXTENT = TDiskExtent;
  PDiskExtent = ^TDiskExtent;
  TVolumeDiskExtents = record
    NumberOfDiskExtents: Cardinal;
    Extents: array[0..0] of TDiskExtent;
  end;
  VOLUME_DISK_EXTENTS = TVolumeDiskExtents;
  PVolumeDiskExtents = ^TVolumeDiskExtents;
 
const
  FILE_DEVICE_DISK                     = $00000007;
  METHOD_BUFFERED                      = 0;
  FILE_ANY_ACCESS                      = 0;
  IOCTL_DISK_BASE                      = FILE_DEVICE_DISK;
  IOCTL_VOLUME_BASE                    = DWORD('V');
  IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = ((IOCTL_VOLUME_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or (0 shl 2) or METHOD_BUFFERED);

Open in new window

0
 

Author Comment

by:WinRat
ID: 23621005
@ThievingSix:

I created a test application (see Main Form code snippet) to test your supplied answers.

I added in the missing type and constant identifiers but it wouldn't compile on this line:
  DISK_EXTENT = TDiskExtent;  {compile error here: "(" expected but ";" found!}

Am I missing something obvious?
unit MainF;
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
 
type
  TMainForm = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  MainForm: TMainForm;
 
{--------------------- START: ThievingSix's constants ------------------------}
type
 TDiskExtent = record
    DiskNumber: Cardinal;
    StartingOffset: Int64;
    ExtentLength: Int64;
  end;
 
const
  DISK_EXTENT = TDiskExtent;  {compile error here: "(" expected but ";" found!}
  PDiskExtent = ^TDiskExtent;
 
type
  TVolumeDiskExtents = record
    NumberOfDiskExtents: Cardinal;
    Extents: array[0..0] of TDiskExtent;
  end;
 
const
  VOLUME_DISK_EXTENTS = TVolumeDiskExtents;
  PVolumeDiskExtents = ^TVolumeDiskExtents;
 
const
  FILE_DEVICE_DISK                     = $00000007;
  METHOD_BUFFERED                      = 0;
  FILE_ANY_ACCESS                      = 0;
  IOCTL_DISK_BASE                      = FILE_DEVICE_DISK;
  IOCTL_VOLUME_BASE                    = DWORD('V');
  IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = ((IOCTL_VOLUME_BASE shl 16) or
                      (FILE_ANY_ACCESS shl 14) or (0 shl 2) or METHOD_BUFFERED);
{---------------------  END: ThievingSix's constants -------------------------}
 
implementation
{$R *.DFM}
{----------------------------------------------------------------------------}
 
{--------------------- START: ThievingSix's functions ------------------------}
function GetLD(Drive: Char): Cardinal;
var
  Buffer : String;
begin
  Buffer := Format('\\.\%s:',[Drive]);
  Result := CreateFile(PChar(Buffer),GENERIC_READ Or GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  If Result = INVALID_HANDLE_VALUE Then
    begin
    Result := CreateFile(PChar(Buffer),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
  end;
end;
 
{-------------------------------------}
function GetPhysicalDiskNumber(Drive: Char): Byte;
var
  LD : DWORD;
  DiskExtents : PVolumeDiskExtents;
  DiskExtent : TDiskExtent;
  BytesReturned : Cardinal;
begin
  Result := 0;
  LD := GetLD(Drive);
  If LD = INVALID_HANDLE_VALUE Then Exit;
  Try
    DiskExtents := AllocMem(Max_Path);
    DeviceIOControl(LD,IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,nil,0,DiskExtents,Max_Path,BytesReturned,nil);
    If DiskExtents^.NumberOfDiskExtents > 0 Then
      begin
      DiskExtent := DiskExtents^.Extents[0];
      Result := DiskExtent.DiskNumber;
    end;
  Finally
    CloseHandle(LD);
  end;
end;
{---------------------- END: ThievingSix's functions -------------------------}
end.

Open in new window

0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:WinRat
ID: 23623267
Hi ThievingSix,

Regarding my previous post - disregard. It was silly. Your code never had any identifiers and I put some under CONST instead of TYPE. Fixed and it compiled fine.

I made a simple program to test your GetPhysicalDiskNumber() function. I just used GetdiskType so only fixed and removable drive letters were sent to your function.

MODS
1) As I wanted the return code to be -1 if there was an invalid file handle I modified the function, and also the return type, to be a LongInt instead of a Byte.

2) I found that the call to CreateFile in GetLD() hung 10-15 seconds for some hard drives (not all). As I had the same problem yesterday with other code I knew how to fix it: I changed the access flags to 0. Problem solved.
   CreateFile(PChar(Buffer),0,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0)

3) I suspect your code might have memory leak and ask if you could check it for possible culprits? During the day Delphi's recompile and run became slower and slower. First I thought I had a virus, but finally checked my mem allocation and found it to be 2Gb. Normally its 500 to 700 Mb. Physical mem is 1Gb so no wonder very slow.
I never before had a major memory leak while using Delphi. The only other program I was using today was FireFox 3.06, which does use a lot of RAM but never to that extent.

YOUR ANSWER
OK, I consider half my question to be answered very well.
But I also asked for a second function to accept a drive letter and which would return a list or string of all the drive letters that go with a drive number.
(I think that going along with that would also be to determine the total number of physical drives in the system (fixed and removable)
If you look at the bottom section of Disk Management the "Disk xx" entries do show what I need)

I hope you are up for completing the solution to earn all the points?
Thanks in advance!


0
 

Author Comment

by:WinRat
ID: 23633168
Hi Experts,
Increasing the points to attract more interest and to encourage a full answer to my question.

Moderators: So far no other experts appear interested. What do I do if only one answer is posted but it does not fully answer my question? Do I award the full points anyway? I'm still learning the ropes here!

0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 1080 total points
ID: 23660153

Example below that uses WMI to handle both the logical -> physical as well as the physical ->  logical mapping (the second being the more difficult). This was done using D5, so let me know if you run into problems if using a later version of the compiler.

Sample usage, then supporting code.

Russell

---

var  dwDrive:       Integer;
     dwIndex:       Integer;
     szDrives:      String;
begin

  // Get the physical disk that the logical drive C is located on
  if (WMIGetDriveNumber('c', dwDrive) = S_OK) then
  begin
     // Show the drive
     ShowMessage(Format('Drive C: is located on physical disk %d', [dwDrive]));
     // Now get all logical drives on disk
     if (WMIGetLogicalDrives(dwDrive, szDrives) = S_OK) then
     begin
        // Walk the drive letters that were found
        for dwIndex:=1 to Length(szDrives) do
        begin
           // Display
           ShowMessage(Format('Found drive %s: on physical disk %d', [szDrives[dwIndex], dwDrive]));
        end;
     end;
  end;

end;

---

unit WmiDisk;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  WmiDisk
//   Author      :  rllibby
//   Date        :  02.17.2009
//   Description :  Disk handling functions through WMI
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Compiler defines
////////////////////////////////////////////////////////////////////////////////

{$IFDEF VER80}
  {$DEFINE DELPHI_V1}
{$ELSE}
  {$IFDEF VER90}
     {$DEFINE DELPHI_V2}
  {$ELSE}
     {$IFDEF VER100}
        {$DEFINE DELPHI_V3}
     {$ELSE}
        {$IFDEF VER120}
           {$DEFINE DELPHI_V4}
        {$ELSE}
           {$IFDEF VER130}
              {$DEFINE DELPHI_V5}
           {$ELSE}
              {$DEFINE DELPHI_V6_UP}
           {$ENDIF}
        {$ENDIF}
     {$ENDIF}
  {$ENDIF}
{$ENDIF}

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes, ComObj, ActiveX {$IFDEF DELPHI_V6_UP}, Variants {$ENDIF};

////////////////////////////////////////////////////////////////////////////////
//   Interface types
////////////////////////////////////////////////////////////////////////////////
type
  IEnumerator       =  interface
     function       ForEach(out Obj: OleVariant): Boolean;
     function       ForEachObject(const IID: TGUID; out Obj): Boolean;
     function       Reset: Boolean;
     function       Skip(Count: LongWord): Boolean;
     function       Clone: IEnumerator;
  end;

////////////////////////////////////////////////////////////////////////////////
//   IEnumVariant wrapper
////////////////////////////////////////////////////////////////////////////////
type
  TEnumerator       =  class(TInterfacedObject, IEnumerator)
  private
     // Private declarations
     FEnumVariant:  IEnumVariant;
     procedure      GetEnumVariant(Dispatch: IDispatch);
  protected
     // Protected declarations
     function       ForEach(out Obj: OleVariant): Boolean;
     function       ForEachObject(const IID: TGUID; out Obj): Boolean;
     function       Reset: Boolean;
     function       Skip(Count: LongWord): Boolean;
     function       Clone: IEnumerator;
  public
     // Public declarations
     constructor    Create(Dispatch: IDispatch);
     constructor    CreateWrapper(EnumVariant: IEnumVariant);
     destructor     Destroy; override;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Constants
////////////////////////////////////////////////////////////////////////////////
const
  WMI_SERVICE       =  'winmgmts:{impersonationLevel=Impersonate}!//.';
  WMI_QRY_DRIVES    =  'SELECT DeviceID, Index FROM Win32_DiskDrive';
  WMI_QRY_DISKPARTS =  'ASSOCIATORS OF {Win32_DiskDrive.DeviceID=''%s''} WHERE AssocClass = Win32_DiskDriveToDiskPartition';
  WMI_QRY_DISKS     =  'ASSOCIATORS OF {Win32_DiskPartition.DeviceID=''%s''} WHERE AssocClass = Win32_LogicalDiskToPartition';

////////////////////////////////////////////////////////////////////////////////
//   Utility routines
////////////////////////////////////////////////////////////////////////////////
function   GetEnumerator(Dispatch: IDispatch): IEnumerator;
function   GetObject(ObjectName: String; out Obj: OleVariant): HResult;
function   SetCheckSucceed(Value: HResult; var Return: HResult): Boolean;

////////////////////////////////////////////////////////////////////////////////
//   WMI handling routines
////////////////////////////////////////////////////////////////////////////////
function   WMIGetDriveNumber(const DriveLetter: AnsiChar; out DriveNumber: Integer): HResult;
function   WMIGetLogicalDrives(const DriveNumber: Integer; out DriveLetters: String): HResult;

implementation

//// WMI handling routines /////////////////////////////////////////////////////
function WMIGetLogicalDrives(const DriveNumber: Integer; out DriveLetters: String): HResult;
var  enumDrives:    IEnumerator;
     enumDiskParts: IEnumerator;
     enumDisks:     IEnumerator;
     ovService:     OleVariant;
     ovDrive:       OleVariant;
     ovDiskPart:    OleVariant;
     ovDisk:        OleVariant;
     dwDrives:      Integer;
     szDisk:        String;
begin

  // Set count of drives found
  dwDrives:=0;

  // Resource protection
  try
     // Set worst case buffer for drive letters
     SetLength(DriveLetters, 26);
     // Get service
     if SetCheckSucceed(GetObject(WMI_SERVICE, ovService), result) then
     begin
        // Set default result
        result:=S_FALSE;
        // Resource protection
        try
           // Get physical disk drives
           enumDrives:=GetEnumerator(ovService.ExecQuery(WMI_QRY_DRIVES));
           // Resource protection
           try
              // Enumerate the drives
              while (result = S_FALSE) and enumDrives.ForEach(ovDrive) do
              begin
                 // Resource protection
                 try
                    // Check drive index against specified drive number
                    if (ovDrive.Index = DriveNumber) then
                    begin
                       // Query for the disk partitions
                       enumDiskParts:=GetEnumerator(ovService.ExecQuery(Format(WMI_QRY_DISKPARTS, [ovDrive.DeviceID])));
                       // Resource protection
                       try
                          // Enumerate the disk partitions
                          while enumDiskParts.ForEach(ovDiskPart) do
                          begin
                             // Resource protection
                             try
                                // Query for the logical disks
                                enumDisks:=GetEnumerator(ovService.ExecQuery(Format(WMI_QRY_DISKS, [ovDiskPart.DeviceID])));
                                // Resource protection
                                try
                                   // Enumerate the logical disks
                                   while enumDisks.ForEach(ovDisk) do
                                   begin
                                      // Update count of drives found
                                      Inc(dwDrives);
                                      // Resource protection
                                      try
                                         // Get the disk id
                                         szDisk:=ovDisk.DeviceID;
                                         // Add to result buffer
                                         DriveLetters[dwDrives]:=UpCase(szDisk[1]);
                                      finally
                                         // Release interface
                                         ovDisk:=Unassigned;
                                      end;
                                   end;
                                finally
                                   // Release interface
                                   enumDisks:=nil;
                                end;
                             finally
                                // Release interface
                                ovDiskPart:=Unassigned;
                             end;
                          end;
                       finally
                          // Release the interface
                          enumDisks:=nil;
                       end;
                       // Done processing
                       result:=S_OK;
                    end;
                 finally
                    // Release interface
                    ovDrive:=Unassigned;
                 end;
              end;
           finally
              // Release interface
              enumDrives:=nil;
           end;
        finally
           // Release interface
           ovService:=Unassigned;
        end;
     end;
  finally
     // Reset buffer with actual size
     SetLength(DriveLetters, dwDrives);
  end;

end;

function WMIGetDriveNumber(const DriveLetter: AnsiChar; out DriveNumber: Integer): HResult;
var  enumDrives:    IEnumerator;
     enumDiskParts: IEnumerator;
     enumDisks:     IEnumerator;
     ovService:     OleVariant;
     ovDrive:       OleVariant;
     ovDiskPart:    OleVariant;
     ovDisk:        OleVariant;
     szDisk:        String;
begin

  // Get service
  if SetCheckSucceed(GetObject(WMI_SERVICE, ovService), result) then
  begin
     // Set default result
     result:=S_FALSE;
     // Resource protection
     try
        // Get physical disk drives
        enumDrives:=GetEnumerator(ovService.ExecQuery(WMI_QRY_DRIVES));
        // Resource protection
        try
           // Enumerate the drives
           while (result = S_FALSE) and enumDrives.ForEach(ovDrive) do
           begin
              // Resource protection
              try
                 // Query for the disk partitions
                 enumDiskParts:=GetEnumerator(ovService.ExecQuery(Format(WMI_QRY_DISKPARTS, [ovDrive.DeviceID])));
                 // Resource protection
                 try
                    // Enumerate the disk partitions
                    while (result = S_FALSE) and enumDiskParts.ForEach(ovDiskPart) do
                    begin
                       // Resource protection
                       try
                          // Query for the logical disks
                          enumDisks:=GetEnumerator(ovService.ExecQuery(Format(WMI_QRY_DISKS, [ovDiskPart.DeviceID])));
                          // Resource protection
                          try
                             // Enumerate the logical disks
                             while (result = S_FALSE) and enumDisks.ForEach(ovDisk) do
                             begin
                                // Resource protection
                                try
                                   // Get the disk id
                                   szDisk:=ovDisk.DeviceID;
                                   // Check against the specififed drive letter
                                   if (Length(szDisk) > 0) and (UpCase(DriveLetter) = UpCase(szDisk[1])) then
                                   begin
                                      // Found the correct disk
                                      DriveNumber:=ovDrive.Index;
                                      // Set result
                                      result:=S_OK;
                                   end;
                                finally
                                   // Release interface
                                   ovDisk:=Unassigned;
                                end;
                             end;
                          finally
                             // Release interface
                             enumDisks:=nil;
                          end;
                       finally
                          // Release interface
                          ovDiskPart:=Unassigned;
                       end;
                    end;
                 finally
                    // Release the interface
                    enumDisks:=nil;
                 end;
              finally
                 // Release interface
                 ovDrive:=Unassigned;
              end;
           end;
        finally
           // Release interface
           enumDrives:=nil;
        end;
     finally
        // Release interface
        ovService:=Unassigned;
     end;
  end;

end;

//// TEnumerator ///////////////////////////////////////////////////////////////
procedure TEnumerator.GetEnumVariant(Dispatch: IDispatch);
var  pdParams:      TDispParams;
     ovEnum:        OleVariant;
begin

  // Check interface
  if Assigned(Dispatch) then
  begin
     // Clear disp params
     FillChar(pdParams, SizeOf(TDispParams), 0);
     // Get enumerator
     OleCheck(Dispatch.Invoke(DISPID_NEWENUM, GUID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET or DISPATCH_METHOD, pdParams, @ovEnum, nil, nil));
     // Resource protection
     try
        // Check returned interface
        if (TVariantArg(ovEnum).vt = VT_UNKNOWN) then
           // Query interface for the IEnumVariant
           OleCheck(IUnknown(ovEnum).QueryInterface(IEnumVariant, FEnumVariant))
        else
           // Throw error
           OleError(E_NOINTERFACE);
     finally
        // Clear interface
        ovEnum:=Unassigned;
     end;
  end
  else
     // Throw error
     OleError(E_NOINTERFACE);

end;

function TEnumerator.ForEach(out Obj: OleVariant): Boolean;
var  dwFetch:       Cardinal;
begin

  // Get the next item
  result:=(FEnumVariant.Next(1, Obj, dwFetch) = S_OK) and (dwFetch = 1);

end;

function TEnumerator.ForEachObject(const IID: TGUID; out Obj): Boolean;
var  ovItem:        OleVariant;
begin

  // Get next item as OleVariant
  if ForEach(ovItem) then
  begin
     // Resource protection
     try
        // Check interface for IUknown
        if (TVariantArg(ovItem).vt = VT_UNKNOWN) then
           // Query interface for the desired interface
           result:=(IUnknown(ovItem).QueryInterface(IID, Obj) = S_OK)
        // Check interface for IDispatch
        else if (TVariantArg(ovItem).vt = VT_DISPATCH) then
           // Query interface for the desired interface
           result:=(IDispatch(ovItem).QueryInterface(IID, Obj) = S_OK)
        else
        begin
           // Pacify the compiler
           result:=False;
           // Throw error
           OleError(E_NOINTERFACE);
        end;
     finally
        // Clear obtained item
        ovItem:=Unassigned;
     end;
  end
  else
     // Failed to get item
     result:=False;

end;

function TEnumerator.Reset: Boolean;
begin

  // Reset enumerator
  result:=(FEnumVariant.Reset = S_OK);

end;

function TEnumerator.Skip(Count: LongWord): Boolean;
begin

  // Skip items in enumerator
  result:=(FEnumVariant.Skip(Count) = S_OK);

end;

function TEnumerator.Clone: IEnumerator;
var  pvEnum:        IEnumVariant;
begin

  // Clone
  if (FEnumVariant.Clone(pvEnum) = S_OK) then
  begin
     // Resource protection
     try
        // Return wrapper
        result:=TEnumerator.CreateWrapper(pvEnum);
     finally
        // Release interface
        pvEnum:=nil;
     end
  end
  else
     // Return nil
     result:=nil;

end;

constructor TEnumerator.CreateWrapper(EnumVariant: IEnumVariant);
begin

  // Perform inherited
  inherited Create;

  // Check interface pointer
  if Assigned(EnumVariant) then
     // Bind to the passed interface
     FEnumVariant:=EnumVariant
  else
     // Throw error
     OleError(E_NOINTERFACE);

end;

constructor TEnumerator.Create(Dispatch: IDispatch);
begin

  // Perform inherited
  inherited Create;

  // Get enumerator interface
  GetEnumVariant(Dispatch);

end;

destructor TEnumerator.Destroy;
begin

  // Resource protection
  try
     // Release the interface
     FEnumVariant:=nil;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// Utility routines //////////////////////////////////////////////////////////
function GetEnumerator(Dispatch: IDispatch): IEnumerator;
begin

  // Create class and return interface
  result:=TEnumerator.Create(Dispatch);

end;

function GetObject(ObjectName: String; out Obj: OleVariant): HResult;
var  lpwszMoniker:  PWideChar;
     pvUnknown:     IUnknown;
     pvMoniker:     IMoniker;
     pvContext:     IBindCtx;
     cbEaten:       LongInt;
     clsid:         TGUID;
begin

  // Clear outbound param
  Obj:=Unassigned;

  // Check object name
  if SetCheckSucceed(CLSIDFromProgID(PWideChar(WideString(ObjectName)), clsid), result) then
  begin
     // Check running object table
     if SetCheckSucceed(GetActiveObject(clsid, nil, pvUnknown), result) then
     begin
        // Resource protection
        try
           // QI for IDispatch
           if SetCheckSucceed(pvUnknown.QueryInterface(IDispatch, TVariantArg(Obj).dispVal), result) then
           begin
              // Set the vt type
              TVariantArg(Obj).vt:=VT_DISPATCH;
           end;
        finally
           // Clear interface
           pvUnknown:=nil;
        end;
     end;
  end
  // Try to access the object using a moniker
  else if SetCheckSucceed(CreateBindCtx(0, pvContext), result) then
  begin
     // Resousce protection
     try
        // Allocate bstr for moniker
        lpwszMoniker:=StringToOleStr(ObjectName);
        // Resource protection
        try
           // Parse the display name
           if SetCheckSucceed(MkParseDisplayName(pvContext, lpwszMoniker, cbEaten, pvMoniker), result) then
           begin
              // Resource protection
              try
                 // Attempt to bind the moniker
                 if SetCheckSucceed(BindMoniker(pvMoniker, 0, IUnknown, pvUnknown), result) then
                 begin
                    // Resource protection
                    try
                       // QI for IDispatch
                       if SetCheckSucceed(pvUnknown.QueryInterface(IDispatch, TVariantArg(Obj).dispVal), result) then
                       begin
                          // Set the vt type
                          TVariantArg(Obj).vt:=VT_DISPATCH;
                       end;
                    finally
                       // Clear interface
                       pvUnknown:=nil;
                    end;
                 end;
              finally
                 // Release interface
                 pvMoniker:=nil;
              end;
           end;
        finally
           // Free string memory
           SysFreeString(lpwszMoniker);
        end;
     finally
        // Release interface
        pvContext:=nil;
     end;
  end;

end;

function SetCheckSucceed(Value: HResult; var Return: HResult): Boolean;
begin

  // Resource protection
  try
     // Perform return set
     Return:=Value;
  finally
     // Check for success
     result:=(Value = S_OK);
  end;

end;

end.


0
 

Author Comment

by:WinRat
ID: 23661971
HI Rusell,

Helping me again! Thanks!

I made a nice little test program to iterate everything and put it in a ListBox.
(screened for only fixed and removable drives)
Both functions seems to work fine, thought the WMI calls seem a little slow.
However only takes 2-5 seconds for each function so can live with that.

Is there a better way to iterate through all the physical drives i.e. is there a way to know how many physical drives there are before I start?
See the code section to see how I am doing it.

Oddly enough neither function returns a value for my USB ZIP drive (remember those)?
Again not serious - I was just testing everything I could connect!

Will allocate points as soon as I get your comments.
Probably 2/3 to you and 1/3 to ThievingSix. He did give me half the solution even if he didn't answer my further questions esp about a possible memory leak. I hope that sounds fair to all?
I will have to research how to allocate split points.

Mike


    for DrvNum := 0 to 9 do {change to 15 - should be high enough to catch all}
    begin
      R := WMIGetLogicalDrives(DrvNum,DriveLetters);
      if R = S_OK then                     {S_OK defined in Windows}
      begin
        MultiColListbox2.Items.Add(IntToStr(DrvNum)+';'+ DriveLetters);       
      end
    end;

Open in new window

0
 
LVL 26

Assisted Solution

by:Russell Libby
Russell Libby earned 1080 total points
ID: 23663053
To get the physical count:

function WMIGetPhysicalDriveCount(out Count: Integer): HResult;
var  ovService:     OleVariant;
     ovDrives:      OleVariant;
     ovDrive:       OleVariant;
begin

  // Set count of drives found
  Count:=0;

  // Get service
  if SetCheckSucceed(GetObject(WMI_SERVICE, ovService), result) then
  begin
     // Resource protection
     try
        // Get physical disk drives
        ovDrives:=ovService.ExecQuery(WMI_QRY_DRIVES);
        // Resource protection
        try
           // Return the count
           result:=ovDrives.Count;
        finally
           // Release interface
           ovDrives:=Unassigned;
        end;
     finally
        // Release interface
        ovService:=Unassigned;
     end;
  end;

end;

Note, the WMI routines I gave you won't return CD/DVD/Zip/Network drives. Only those that have a device ID of '\\.\PHYSICALDRIVEX' will be listed. (Usb drives do show up). And yeah, the WMI routines are a little slower, but they do operate correctly.

Point split is fine.

Russell
0
 

Author Closing Comment

by:WinRat
ID: 31545758
Thanks Russell for another "copy & paste" full solution that worked first time!
0
 

Expert Comment

by:Peter_Panino
ID: 35424382
Hi Russell, I corrected a bug (which occurs in Delphi XE, probably an Unicode issue) in your WmiDisk unit:

This line produces a compiler error in Delphi XE:

if (Length(szDisk) > 0) and (UpCase(DriveLetter) = UpCase(szDisk[1])) then

Open in new window


Error message:
[DCC Error] WmiDisk.pas(256): E2015 Operator cannot be used with this type of operand

Solution: UpCase(szDisk[1]) needs to be typecasted:

if (Length(szDisk) > 0) and (UpCase(DriveLetter) = AnsiChar(UpCase(szDisk[1]))) then

Open in new window


Now it works in Delphi XE!
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
As many of you are aware about Scanpst.exe utility which is owned by Microsoft itself to repair inaccessible or damaged PST files, but the question is do you really think Scanpst.exe is capable to repair all sorts of PST related corruption issues?
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question