[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1375
  • Last Modified:

Fix the answer to "Is a drive a USB drive?"

Hi Experts,

MiDaTi posed the question: Is a drive a USB drive?
(refer http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_23994641.html)

He posted his own solution (source code included) with no text as to which Delphi version it was intended for, required Delphi and additional 3rd-Party units and missing type definitions (e.g.  BusTypeUsb).

It's exactly what I am looking for but I doesn't compile in Delphi 5 which I use. I created a unit, added Windows and SysUtils but no joy. I can see it requires a 3rd-party unit for "Tnt_CreateFileW".  There are some Windows type definitions missing but I am not familiar with C syntax and conventions. D5 also doesn't have the WideFormat function.

Can anyone fix it so it complies without using 3rd-party units in Delphi 5? If that's  not possible then some other way?
I think the answer will be helpful to other users too.
Thanks!
{Posted self-solution by MiDaTi}
 
{ Returns TRUE if a drive is connected via USB or firewire, or is removable }
function NotInternalDrive(const aPath: WideString): Boolean;
 
const
  IOCTL_STORAGE_QUERY_PROPERTY = $2D1400;
 
type
  STORAGE_PROPERTY_QUERY = packed record
    PropertyId: DWORD;
    QueryType: DWORD;
    AdditionalParameters: array[0..3] of Byte;
  end;
 
  STORAGE_DEVICE_DESCRIPTOR = packed record
    Version: ULONG;
    Size: ULONG;
    DeviceType: Byte;
    DeviceTypeModifier: Byte;
    RemovableMedia: Boolean;
    CommandQueueing: Boolean;
    VendorIdOffset: ULONG;
    ProductIdOffset: ULONG;
    ProductRevisionOffset: ULONG;
    SerialNumberOffset: ULONG;
    STORAGE_BUS_TYPE: DWORD;
    RawPropertiesLength: ULONG;
    RawDeviceProperties: array[0..511] of Byte;
  end;
 
var
  Returned: Cardinal;
  Status: LongBool;
  PropQuery: STORAGE_PROPERTY_QUERY;
  DeviceDescriptor: STORAGE_DEVICE_DESCRIPTOR;
  FFileHandle: Cardinal;
  DriveStr: WideString;
begin
  Result:=FALSE;
  FFileHandle:=INVALID_HANDLE_VALUE;
  try
    // Open the drive
    DriveStr:=WideFormat('\\.\%s:', [AnsiString(aPath[1])]);
    FFileHandle:=Tnt_CreateFileW(PWideChar(DriveStr), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    if (FFileHandle = INVALID_HANDLE_VALUE) then Exit;
 
    // Get the drive information
    ZeroMemory(@PropQuery, SizeOf(PropQuery));
    ZeroMemory(@DeviceDescriptor, SizeOf(DeviceDescriptor));
    DeviceDescriptor.Size:=SizeOf(DeviceDescriptor);
    Status:=DeviceIoControl(FFileHandle, IOCTL_STORAGE_QUERY_PROPERTY,
      @PropQuery, SizeOf(PropQuery), @DeviceDescriptor, DeviceDescriptor.Size,
      Returned, nil);
    if not Status then Exit;
 
    // Connected via USB or firewire? Removable media?
    Result:=(DeviceDescriptor.STORAGE_BUS_TYPE in [BusTypeUsb, BusType1394]) or DeviceDescriptor.RemovableMedia;
  finally
    if (FFileHandle <> INVALID_HANDLE_VALUE) then CloseHandle(FFileHandle);
  end;
end;

Open in new window

0
WinRat
Asked:
WinRat
  • 2
1 Solution
 
Russell LibbySoftware Engineer, Advisory Commented:
Only need to include windows with this. Checks bus type for Usb.

Regards,
Russell

----

uses
  Windows;

const
  IOCTL_STORAGE_QUERY_PROPERTY  =  $002D1400;

//_STORAGE_PROPERTY_ID
const
  StorageDeviceProperty         =  $00000000;
  StorageAdapterProperty        =  $00000001;
  StorageDeviceIdProperty       =  $00000002;

// _STORAGE_QUERY_TYPE
const
  PropertyStandardQuery         =  $00000000;
  PropertyExistsQuery           =  $00000001;
  PropertyMaskQuery             =  $00000002;

// _STORAGE_BUS_TYPE
const
  BusTypeUnknown                =  $00000000;
  BusTypeScsi                   =  $00000001;
  BusTypeAtapi                  =  $00000002;
  BusTypeAta                    =  $00000003;
  BusType1394                   =  $00000004;
  BusTypeSsa                    =  $00000005;
  BusTypeFibre                  =  $00000006;
  BusTypeUsb                    =  $00000007;
  BusTypeRAID                   =  $00000008;

type
  STORAGE_PROPERTY_QUERY        =  packed record
    PropertyId:                 DWORD;
    QueryType:                  DWORD;
    AdditionalParameters:       Array [0..3] of Byte;
  end;
  PSTORAGE_PROPERTY_ID          =  ^STORAGE_PROPERTY_QUERY;

  STORAGE_DEVICE_DESCRIPTOR     =  packed record
    Version:                    ULONG;
    Size:                       ULONG;
    DeviceType:                 Byte;
    DeviceTypeModifier:         Byte;
    RemovableMedia:             Boolean;
    CommandQueueing:            Boolean;
    VendorIdOffset:             ULONG;
    ProductIdOffset:            ULONG;
    ProductRevisionOffset:      ULONG;
    SerialNumberOffset:         ULONG;
    STORAGE_BUS_TYPE:           DWORD;
    RawPropertiesLength:        ULONG;
    RawDeviceProperties:        Array [0..511] of Byte;
  end;
  PSTORAGE_DEVICE_DESCRIPTOR    =  ^STORAGE_DEVICE_DESCRIPTOR;

function IsUsbDrive(DriveLetter: Char): Boolean;

implementation

function IsUsbDrive(DriveLetter: Char): Boolean;
var  lpQuery:       STORAGE_PROPERTY_QUERY;
     lpDevDesc:     STORAGE_DEVICE_DESCRIPTOR;
     dwBytesReturn: DWORD;
     hDrive:        THandle;
begin

  // Set default result
  result:=False;

  // Attempt to open a handle to the drive
  hDrive:=CreateFile(PChar('\\.\' + DriveLetter + ':'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

  // Check handle
  if (hDrive <> INVALID_HANDLE_VALUE) then
  begin
     // Resource protection
     try
        // Clear the structs
        FillChar(lpQuery, SizeOf(lpQuery), 0);
        FillChar(lpDevDesc, SizeOf(lpDevDesc), 0);
        // Set query values
        lpQuery.PropertyId:=StorageDeviceProperty;
        lpQuery.QueryType:=PropertyStandardQuery;
        // Call device IO function
        if DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, @lpQuery, SizeOf(lpQuery), @lpDevDesc, SizeOf(lpDevDesc), dwBytesReturn, nil) then
           // Connected via USB?
           result:=(lpDevDesc.STORAGE_BUS_TYPE = BusTypeUsb)
        else
           // Failed to query
           result:=False;
     finally
        // Close the handle
        CloseHandle(hDrive);
     end;
  end;

end;
0
 
WinRatAuthor Commented:
Hi Russel!
Thanks for the solution. I just needed to add the "unit" & "Interface" identifiers to your code and it compiled first time.
There was a glitch which I sorted out with a little debugging. However I don't think it was a fault as such in your solution so I am giving you full points.
I wrote a simple test program to iterate through drives "A" to "Z", call your IsUsbDrive() function & display a list of USB drives.
The program hung for about 20 seconds every time I ran it then an Exception error message dialog from Windows popped up. I could select continue and then the USB drive list was displayed.
I eventually traced it to your function and found that it hung on the CreateFile call:
CreateFile(PChar('\\.\' + DriveLetter + ':'), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

With more debugging I found this only happened with my B drive, which is an old LS120 drive (accepts stiffy discs or 120 MB LS120 discs).

I compared your call to the code I had posted in my question and noticed a difference. You had used GENERIC_READ for the access mode. The other code used 0 which = "Specifies device query access to the object. An application can query device attributes without accessing the device". I modified your code to use 0 and the problem went away. Do you think this would be a better value to use anyway?

CreateFile(PChar('\\.\' + DriveLetter + ':'), 0, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

I don't know if the LS120 drive is actually the cause of the problem - but  I'm happy!  I had noticed recently that when I boot up - the drive gets accessed several times (sounds like the heads are moved) which never used to happen. So there might be a gltich with it.
Regards,
Mike
0
 
Russell LibbySoftware Engineer, Advisory Commented:
Mike, glad that you have it working. and yes, the 0 for access is probably a better value to use, though it should not have raised an exception. Most likely the driver/drive is not functioning properly.

Russell
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now