And you'll need these constants
Main Topics
Browse All TopicsHi 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.
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.
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.
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.
Access the answers to your technology questions today.
30-day free trial. Register in 60 seconds.
Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Try it out and discover for yourself.
30-day free trial. Register in 60 seconds.
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.
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
3
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!
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!
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(dwDri
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:
destructor Destroy; override;
end;
//////////////////////////
// Constants
//////////////////////////
const
WMI_SERVICE = 'winmgmts:{impersonationLe
WMI_QRY_DRIVES = 'SELECT DeviceID, Index FROM Win32_DiskDrive';
WMI_QRY_DISKPARTS = 'ASSOCIATORS OF {Win32_DiskDrive.DeviceID=
WMI_QRY_DISKS = 'ASSOCIATORS OF {Win32_DiskPartition.Devic
//////////////////////////
// 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(
begin
// Set default result
result:=S_FALSE;
// Resource protection
try
// Get physical disk drives
enumDrives:=GetEnumerator(
// Resource protection
try
// Enumerate the drives
while (result = S_FALSE) and enumDrives.ForEach(ovDrive
begin
// Resource protection
try
// Check drive index against specified drive number
if (ovDrive.Index = DriveNumber) then
begin
// Query for the disk partitions
enumDiskParts:=GetEnumerat
// Resource protection
try
// Enumerate the disk partitions
while enumDiskParts.ForEach(ovDi
begin
// Resource protection
try
// Query for the logical disks
enumDisks:=GetEnumerator(o
// 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]:=Up
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(
begin
// Set default result
result:=S_FALSE;
// Resource protection
try
// Get physical disk drives
enumDrives:=GetEnumerator(
// Resource protection
try
// Enumerate the drives
while (result = S_FALSE) and enumDrives.ForEach(ovDrive
begin
// Resource protection
try
// Query for the disk partitions
enumDiskParts:=GetEnumerat
// Resource protection
try
// Enumerate the disk partitions
while (result = S_FALSE) and enumDiskParts.ForEach(ovDi
begin
// Resource protection
try
// Query for the logical disks
enumDisks:=GetEnumerator(o
// 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
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(D
// Resource protection
try
// Check returned interface
if (TVariantArg(ovEnum).vt = VT_UNKNOWN) then
// Query interface for the IEnumVariant
OleCheck(IUnknown(ovEnum).
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
end;
function TEnumerator.ForEachObject(
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).
// Check interface for IDispatch
else if (TVariantArg(ovItem).vt = VT_DISPATCH) then
// Query interface for the desired interface
result:=(IDispatch(ovItem)
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.Rese
end;
function TEnumerator.Skip(Count: LongWord): Boolean;
begin
// Skip items in enumerator
result:=(FEnumVariant.Skip
end;
function TEnumerator.Clone: IEnumerator;
var pvEnum: IEnumVariant;
begin
// Clone
if (FEnumVariant.Clone(pvEnum
begin
// Resource protection
try
// Return wrapper
result:=TEnumerator.Create
finally
// Release interface
pvEnum:=nil;
end
end
else
// Return nil
result:=nil;
end;
constructor TEnumerator.CreateWrapper(
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(Dispatc
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
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(CLSIDFromP
begin
// Check running object table
if SetCheckSucceed(GetActiveO
begin
// Resource protection
try
// QI for IDispatch
if SetCheckSucceed(pvUnknown.
begin
// Set the vt type
TVariantArg(Obj).vt:=VT_DI
end;
finally
// Clear interface
pvUnknown:=nil;
end;
end;
end
// Try to access the object using a moniker
else if SetCheckSucceed(CreateBind
begin
// Resousce protection
try
// Allocate bstr for moniker
lpwszMoniker:=StringToOleS
// Resource protection
try
// Parse the display name
if SetCheckSucceed(MkParseDis
begin
// Resource protection
try
// Attempt to bind the moniker
if SetCheckSucceed(BindMonike
begin
// Resource protection
try
// QI for IDispatch
if SetCheckSucceed(pvUnknown.
begin
// Set the vt type
TVariantArg(Obj).vt:=VT_DI
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.
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
To get the physical count:
function WMIGetPhysicalDriveCount(o
var ovService: OleVariant;
ovDrives: OleVariant;
ovDrive: OleVariant;
begin
// Set count of drives found
Count:=0;
// Get service
if SetCheckSucceed(GetObject(
begin
// Resource protection
try
// Get physical disk drives
ovDrives:=ovService.ExecQu
// 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
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:
Error message:
Now it works in Delphi XE!
Business Accounts
Answer for Membership
by: ThievingSixPosted on 2009-02-11 at 16:00:04ID: 23618015
Select allOpen in new window