views:

776

answers:

2

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?

+1  A: 

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.
Ritsaert Hornstra
+2  A: 

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.

Mark Wilkins
can you suggest an example?
Levo
http://msdn.microsoft.com/en-us/library/aa363215(VS.85).aspx
Julien L.
Thanks, I'll try that.
Levo
An important case that you need to test is the first attachment of a particular USB device. This will take a while as the driver manager needs to create device nodes. (not Windows specific)
MSalters