Re: Retrieving mapped drives from USB MSC device
- From: Robert Marquardt <marquardt@xxxxxxxxxxxxx>
- Date: Thu, 23 Jun 2005 08:01:07 +0200
Sasha wrote:
I need to get USB device ID from USB MSC device string descriptors. I can enumerate all USB hubs and ports. I can then find what devices are connected to which ports and get their serial number in string descriptors. Now I need to find what mapped drives does this particular USB Mass Storage device have. I would really appreciate any advise as I am literally stuck. What you propose is to walk through all removable drives and query their information with IOCTL_STORAGE_QUERY_PROPERTY. Unfortunately, this does not give me USB device serial number that I need.
Enumerating the USBVIEW way is a bit intrusive. I have seen spurious surprise removal dialogs sometimes.
Yo can extract the serial number from the "SymbolicName".
I have enclosed a Delphi (Pascal) source which shows the trick.
unit SafeRemovalMain;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms,
Dialogs, StdCtrls,
JwaDbt;
type
TSafeRemovalForm = class(TForm)
Description: TLabel;
DriveList: TListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure WMDeviceChange(var Msg: TWMDeviceChange); message WM_DEVICECHANGE;
procedure DriveListDblClick(Sender: TObject);
public
DriveMountPoints: TStringList;
procedure FillDriveList;
end;
var
SafeRemovalForm: TSafeRemovalForm;
implementation
uses
JwaWinBase, JwaWinType,
Cfg, CfgMgr32, SetupApi;
{$R *.dfm}
// encapsulate GetVolumeNameForVolumeMountPoint in a Delphi-style function
function GetVolumeNameForVolumeMountPointString(Name: string): string;
var
Volume: array [0..MAX_PATH] of Char;
begin
FillChar(Volume[0], SizeOf(Volume), 0);
GetVolumeNameForVolumeMountPoint(PChar(Name), @Volume[0], SizeOf(Volume));
Result := Volume;
end;
// fills the TStringList with the mount points of all removable drives
procedure FillInRemovableDriveMountPoints(MountPoints: TStrings);
const
MAX_DRIVES = 26;
var
I: Integer;
dwDriveMask: DWORD;
DriveName: string;
begin
MountPoints.Clear;
// get all mounted drive letters as bitmask
dwDriveMask := GetLogicalDrives;
DriveName := 'A:\';
// check all drive letters
for I := 0 to MAX_DRIVES - 1 do
// if drive letter exists
if (dwDriveMask and (1 shl I)) <> 0 then
begin
DriveName[1] := 'A';
Inc(DriveName[1], I);
// see if it is a removable drive
if GetDriveType(PChar(DriveName)) = DRIVE_REMOVABLE then
// store mount point string and corresponding drive letter in list
MountPoints.AddObject(GetVolumeNameForVolumeMountPointString(DriveName), TObject(DriveName[1]));
end;
end;
// Delphi style encapsulation for CM_Get_Device_ID
function GetDeviceID(Inst: DEVINST): string;
var
Buffer: PTSTR;
Size: ULONG;
begin
CM_Get_Device_ID_Size(Size, Inst, 0);
// Required! See DDK help for CM_Get_Device_ID
Inc(Size);
Buffer := AllocMem(Size * SizeOf(TCHAR));
CM_Get_Device_ID(Inst, Buffer, Size, 0);
Result := Buffer;
FreeMem(Buffer);
end;
// simple extraction of the bus name from DeviceID string
function ExtractBus(DeviceID: string): string;
begin
Result := Copy(DeviceID, 1, Pos('\', DeviceID) - 1);
end;
// get the "SymbolicName" registry entry of a device
// for an USB device this string contains VID, PID and SerialNumber string
function GetSymbolicName(Inst: DEVINST): string;
var
Len: DWORD;
Key: HKEY;
// a hopefully sufficiently large buffer
Buffer: array [0..4095] of Char;
begin
CM_Open_DevNode_Key(Inst, KEY_READ, 0,
REGDISPOSITION(RegDisposition_OpenExisting), Key, 0);
Buffer[0] := #0;
if Key <> INVALID_HANDLE_VALUE then
begin
Len := SizeOf(Buffer);
RegQueryValueEx(Key, 'SymbolicName', nil, nil, @Buffer[0], @Len);
RegCloseKey(Key);
end;
Result := Buffer;
end;
// extract a 4 digit hex number from SymbolicName
// example "\??\USB#Vid_08ec&Pid_0010#0918121014000B59#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"
function ExtractNum(const SymbolicName, Prefix: string): Integer;
var
S: string;
N: Integer;
begin
S := LowerCase(SymbolicName);
N := Pos(Prefix, S);
if N > 0 then
begin
S := '$' + Copy(SymbolicName, N + Length(Prefix), 4);
Result := StrToInt(S);
end
else
Result := 0;
end;
function ExtractVID(const SymbolicName: string): Integer;
begin
Result := ExtractNum(SymbolicName, 'vid_');
end;
function ExtractPID(const SymbolicName: string): Integer;
begin
Result := ExtractNum(SymbolicName, 'pid_');
end;
function ExtractSerialNumber(SymbolicName: string): string;
var
N: Integer;
begin
N := Pos('#', SymbolicName);
if N >= 0 then
begin
SymbolicName := Copy(SymbolicName, N + 1, Length(SymbolicName));
N := Pos('#', SymbolicName);
if N >= 0 then
begin
SymbolicName := Copy(SymbolicName, N + 1, Length(SymbolicName));
N := Pos('#', SymbolicName);
if N >= 0 then
Result := Copy(SymbolicName, 1, N - 1)
else
Result := '';
end;
end
else
Result := '';
end;
// find the "bus" DeviceID for a given mount point
function GetDriveInstanceID(MountPointName: string; var DeviceInst: DEVINST): Boolean;
const
GUID_DEVINTERFACE_VOLUME: TGUID = '{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}';
var
StorageGUID: TGUID;
PnPHandle: HDEVINFO;
DevData: TSPDevInfoData;
DeviceInterfaceData: TSPDeviceInterfaceData;
FunctionClassDeviceData: PSPDeviceInterfaceDetailData;
Success: LongBool;
Devn: Integer;
BytesReturned: DWORD;
Inst: DEVINST;
S, FileName, MountName, DevID: string;
begin
Result := False;
DeviceInst := 0;
// enumerate all volumes
StorageGUID := GUID_DEVINTERFACE_VOLUME;
PnPHandle := SetupDiGetClassDevs(@StorageGUID, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
if PnPHandle = Pointer(INVALID_HANDLE_VALUE) then
Exit;
Devn := 0;
repeat
DeviceInterfaceData.cbSize := SizeOf(TSPDeviceInterfaceData);
Success := SetupDiEnumDeviceInterfaces(PnPHandle, nil, StorageGUID, Devn, DeviceInterfaceData);
if Success then
begin
DevData.cbSize := SizeOf(DevData);
BytesReturned := 0;
SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData, nil, 0, BytesReturned, @DevData);
if (BytesReturned <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
begin
FunctionClassDeviceData := AllocMem(BytesReturned);
try
FunctionClassDeviceData.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
if SetupDiGetDeviceInterfaceDetail(PnPHandle, @DeviceInterfaceData,
FunctionClassDeviceData, BytesReturned, BytesReturned, @DevData) then
begin
FileName := PTSTR(@FunctionClassDeviceData.DevicePath[0]);
// get the grandparent DevNode which is the "bus" device
// like "USB". This is the DevNode for CM_Request_Device_Eject and
// several other useful operations
Inst := DevData.DevInst;
CM_Get_Parent(Inst, Inst, 0);
CM_Get_Parent(Inst, Inst, 0);
DevID := GetDeviceID(Inst);
// no need in this example to check for USB only
// if ExtractBus(DevID) = 'USB' then
begin
S := '\';
S := PTSTR(@FunctionClassDeviceData.DevicePath) + S;
MountName := GetVolumeNameForVolumeMountPointString(S);
if MountName = MountPointName then
begin
DeviceInst := Inst;
Result := True;
Exit;
end;
end;
end;
finally
FreeMem(FunctionClassDeviceData);
end;
end;
end;
Inc(Devn);
until not Success;
SetupDiDestroyDeviceInfoList(PnPHandle);
end;
//============================================================================
procedure TSafeRemovalForm.FormCreate(Sender: TObject);
begin
// never forget to load the dynamically linked APIs
LoadSetupApi;
LoadConfigManagerApi;
DriveMountPoints := TStringList.Create;
DriveMountPoints.Sorted := True;
// initialize drive list
FillDriveList;
end;
procedure TSafeRemovalForm.FormDestroy(Sender: TObject);
begin
DriveMountPoints.Free;
UnloadConfigManagerApi;
UnloadSetupApi;
end;
procedure TSafeRemovalForm.WMDeviceChange(var Msg: TWMDeviceChange);
begin
// watch for volumes being added or removed and update drive list
if (Msg.Msg = WM_DEVICECHANGE) and
(((Msg.Event = DBT_DEVICEARRIVAL) and
(PDevBroadcastHeader(Msg.dwData).dbcd_devicetype = DBT_DEVTYP_VOLUME)) or
(Msg.Event = DBT_DEVICEREMOVECOMPLETE)) then
FillDriveList;
end;
procedure TSafeRemovalForm.FillDriveList;
var
S: string;
I: Integer;
Inst: DEVINST;
SymbolicName: string;
begin
// update the list of drive mount points
FillInRemovableDriveMountPoints(DriveMountPoints);
// show the list of drive letters from the drive mount point list
DriveList.Items.BeginUpdate;
DriveList.Items.Clear;
S := 'A:';
for I := 0 to DriveMountPoints.Count - 1 do
begin
S[1] := Char(DriveMountPoints.Objects[I]);
GetDriveInstanceID(DriveMountPoints[I], Inst);
SymbolicName := GetSymbolicName(Inst);
DriveList.Items.AddObject(S + ' ' + ExtractSerialNumber(SymbolicName), TObject(Inst));
end;
DriveList.Items.EndUpdate;
end;
procedure TSafeRemovalForm.DriveListDblClick(Sender: TObject);
var
VetoType: PNP_VETO_TYPE;
// VetoBuffer: array [0..MAX_PATH-1] of TCHAR;
I: Integer;
begin
if DriveList.ItemIndex <> -1 then
// find the mount point name for the drive letter clicked
for I := 0 to DriveMountPoints.Count - 1 do
if Char(DriveMountPoints.Objects[I]) = DriveList.Items[DriveList.ItemIndex][1] then
begin
VetoType := 0;
// try to do a silent safe removal
// for drives not able to do a safe removal the function simply fails
// FillChar(VetoBuffer[0], SizeOf(VetoBuffer), 0);
// CM_Request_Device_Eject(DEVINST(DriveList.Items.Objects[I]),
// @VetoType, @VetoBuffer[0], Length(VetoBuffer), 0);
// do a safe removal with dialog
CM_Request_Device_Eject(DEVINST(DriveList.Items.Objects[DriveList.ItemIndex]),
@VetoType, nil, 0, 0);
end;
end;
end.
- References:
- Retrieving mapped drives from USB MSC device
- From: Sasha
- Re: Retrieving mapped drives from USB MSC device
- From: Don Burn
- Re: Retrieving mapped drives from USB MSC device
- From: Sasha
- Retrieving mapped drives from USB MSC device
- Prev by Date: Re: How to read Master File Table (MFT)
- Next by Date: Re: How to require a password to stop an NT service?
- Previous by thread: Re: Retrieving mapped drives from USB MSC device
- Next by thread: Re: Retrieving mapped drives from USB MSC device
- Index(es):
Relevant Pages
|