Question

How to link hard drive numbers and drive letters

Asked by: WinRat

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!!

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2009-02-11 at 12:18:41ID24134897
Tags

Delphi

,

hard drives

,

drive numbers

,

drive letters

Topic

Delphi Programming

Participating Experts
3
Points
400
Comments
10

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Testing Novell mapping to driveletter
    I would like to have a Delphi procedure for testing the existence of a drivemapping under NT 4.0 Workstation SP 3, running the Novell Intranetware Client, on a Novell 4.11 server when the NT 4.0 Workstation does a NDS logon. Something with the functionality of this procedure...
  2. how can i find Partition/disk letter ?
    ex : i want know harddisk drive letter(ex : C:,D:) or its partition's drive letters using its windows device name like "\\.\PHYSICALDRIVE1". And i don't know how partitions are naming by windows. is it like "\\.\PHYSICALDRIVE1\PARTITION1" ?

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: ThievingSixPosted on 2009-02-11 at 16:00:04ID: 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;
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:

Select allOpen in new window

 

by: ThievingSixPosted on 2009-02-11 at 16:00:31ID: 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);
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:

Select allOpen in new window

 

by: WinRatPosted on 2009-02-12 at 03:40:25ID: 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.

                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:

Select allOpen in new window

 

by: WinRatPosted on 2009-02-12 at 08:01:06ID: 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!


 

by: WinRatPosted on 2009-02-13 at 06:55:58ID: 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!

 

by: rllibbyPosted on 2009-02-17 at 06:58:29ID: 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.


 

by: WinRatPosted on 2009-02-17 at 09:25:26ID: 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;

                                              
1:
2:
3:
4:
5:
6:
7:
8:

Select allOpen in new window

 

by: rllibbyPosted on 2009-02-17 at 11:15:03ID: 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

 

by: WinRatPosted on 2009-02-17 at 12:34:23ID: 31545758

Thanks Russell for another "copy & paste" full solution that worked first time!

 

by: Peter_PaninoPosted on 2011-04-19 at 06:28:09ID: 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
                                              
1:

Select allOpen 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
                                              
1:

Select allOpen in new window



Now it works in Delphi XE!

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...