How can I detect when a removable disk drive is (dis)connected to the system? How to get the mount path (for Linux) and the drive letter (for windows)?
EDIT: Is there a way to detect the currently connected devices?
How can I detect when a removable disk drive is (dis)connected to the system? How to get the mount path (for Linux) and the drive letter (for windows)?
EDIT: Is there a way to detect the currently connected devices?
You can get the change notifications (for drive added / deleted) from the shell. You can use this Delphi code to use as documenattion:
unit UnitChangeNotify;
interface
uses
Windows, Messages, SysUtils, Classes, ShlObj, Types;
type
SHChangeNotifyEntry = record
pidlPath : PItemIDList;
bWatchSubtree : Boolean;
end;
TChangeEventType = (
cnAssocchanged,
cnAttributes,
cnCreate,
cnDelete,
cnDriveAdd,
cnDriveAddGui,
cnDriveRemoved,
cnMediaInserted,
cnMediaRemoved,
cnMkdir,
cnNetShare,
cnNetUnshare,
cnRenameFolder,
cnRenameItem,
cnRmdir,
cnServerDisconnect,
cnUpdateDir,
cnUpdateImage,
cnUpdateItem );
TChangeNotifyEvent = procedure( Sender: TObject; Event: TChangeEventType; const Paths: TStringDynArray ) of object;
TChangeNotify = class( TComponent )
private
FHWnd: HWND;
FRegID: THandle;
FEntry: SHChangeNotifyEntry;
FOnChange: TChangeNotifyEvent;
function GetActive (): Boolean;
procedure SetActive ( const Value: Boolean );
procedure WndProc ( var Msg: TMessage );
procedure DoEvent ( Event: TChangeEventType; const Paths: TStringDynArray ); virtual;
public
constructor Create ( Aowner: TComponent ); override;
destructor Destroy; override;
property Active: Boolean read GetActive write SetActive;
property OnChange: TChangeNotifyEvent read FOnChange write FOnChange;
end;
const
SHCNF_ACCEPT_INTERRUPTS = $0001;
SHCNF_ACCEPT_NON_INTERRUPTS = $0002;
SHCNF_NO_PROXY = $8000;
function SHChangeNotifyRegister( hWnd: HWND; fSources: Integer; wEventMask: DWORD; uMsg: UINT; cItems: integer; const Items: SHChangeNotifyEntry ): THandle; stdcall;
function SHChangeNotifyDeregister( hRegID: THandle ) : BOOL; stdcall;
function SHILCreateFromPath( Path: Pointer; PIDL: PItemIDList; var Attributes: ULONG ): HResult; stdcall;
implementation
const Shell32DLL = 'shell32.dll';
function SHChangeNotifyRegister; external Shell32DLL index 2;
function SHChangeNotifyDeregister; external Shell32DLL index 4;
function SHILCreateFromPath; external Shell32DLL index 28;
{ TChangeNotify }
constructor TChangeNotify.Create( Aowner: TComponent );
begin
inherited Create( AOwner );
end;
destructor TChangeNotify.Destroy;
begin
Active := False;
inherited Destroy;
end;
procedure TChangeNotify.DoEvent( Event: TChangeEventType; const Paths: TStringDynArray );
begin
if Assigned( FOnChange ) then FOnChange( Self, Event, Paths );
end;
function TChangeNotify.GetActive(): Boolean;
begin
Result := FHWnd <> 0;
end;
procedure TChangeNotify.SetActive( const Value: Boolean );
begin
if Value = GetActive() then Exit;
if Value then begin
FHWnd := AllocateHWnd( WndProc );
FEntry.pidlPath := nil;
FEntry.bWatchSubtree := True;
FRegID := SHChangeNotifyRegister( FHWnd, SHCNF_ACCEPT_INTERRUPTS or SHCNF_ACCEPT_NON_INTERRUPTS,
SHCNE_ALLEVENTS, WM_USER, 1, FEntry );
if FRegID = 0 then begin
DeallocateHWnd( FHWnd );
FHWnd := 0;
RaiseLastOSError();
end;
end else begin
SHChangeNotifyDeregister( FRegID );
FRegID := 0;
DeallocateHWnd( FHWnd );
FHWnd := 0;
end;
end;
procedure TChangeNotify.WndProc( var Msg: TMessage );
type
PPITEMIDLIST = ^PITEMIDLIST;
var
Event: TChangeEventType;
i: Integer;
Paths: TStringDynArray;
P: PPITEMIDLIST;
const
EventBits: array [ TChangeEventType ] of DWORD = (
SHCNE_ASSOCCHANGED,
SHCNE_ATTRIBUTES,
SHCNE_CREATE,
SHCNE_DELETE,
SHCNE_DRIVEADD,
SHCNE_DRIVEADDGUI,
SHCNE_DRIVEREMOVED,
SHCNE_MEDIAINSERTED,
SHCNE_MEDIAREMOVED,
SHCNE_MKDIR,
SHCNE_NETSHARE,
SHCNE_NETUNSHARE,
SHCNE_RENAMEFOLDER,
SHCNE_RENAMEITEM,
SHCNE_RMDIR,
SHCNE_SERVERDISCONNECT,
SHCNE_UPDATEDIR,
SHCNE_UPDATEIMAGE,
SHCNE_UPDATEITEM );
EventPIDLCount: array [ TChangeEventType ] of Integer = (
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
2,
1,
1,
1,
1,
1 );
begin
case Msg.Msg of
WM_USER: begin
// lParam = eventmask
// wParam = array of PIDLs with
for Event := Low( Event ) to High( Event ) do begin
if EventBits[ Event ] and msg.LParam = EventBits[ Event ] then begin
SetLength( Paths, EventPIDLCount[ Event ] );
P := PPITEMIDLIST( Msg.WParam );
for i := 0 to High( Paths ) do begin
SetLength( Paths[ i ], MAX_PATH );
if not SHGetPathFromIDList( P^, PChar( Paths[ i ] ) ) then Paths[ i ] := '' else Paths[ i ] := PChar( Paths[ i ] );
Inc( P );
end;
DoEvent( Event, Paths );
Break;
end;
end;
end;
end;
DefaultHandler( Msg );
end;
end.
For Windows, the API RegisterDeviceNotification will let you know when a USB device is added. The information about the volume is given in the DEV_BROADCAST_VOLUME structure. The dbcv_unitmask
gives the drive letter.