WinRat
asked on
How to link hard drive numbers and drive letters
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!!
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!!
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
@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?
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.
ASKER
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_SHAR E_READ,nil ,OPEN_EXIS TING,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!
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 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!
ASKER
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!
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!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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;
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks Russell for another "copy & paste" full solution that worked first time!
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:
Solution: UpCase(szDisk[1]) needs to be typecasted:
Now it works in Delphi XE!
This line produces a compiler error in Delphi XE:
if (Length(szDisk) > 0) and (UpCase(DriveLetter) = UpCase(szDisk[1])) then
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
Now it works in Delphi XE!
Open in new window