Mega Code Archive

 
Categories / Delphi / Examples
 

Getting notifications from the shell

How to hook to the shell internal event triggering If you have ever needed to know when the shell issues an event here is a wrapper around SHChangeNotifyRegister wich allows you to know the kind of event and the file(s) affected, this component is in stage 1 of developement however it offers the whole functionality. The component itself is composed of enhancements made to TSHChangeNotify component by Elliott Shevin (shevine@aol.com) By the way here is a list of things you should be aware when using this component for instance restoring an item from the recycle bin to the original location turn on a rename event, while you get a notification over an event you cannot stop it you just get notified of it. Deleting an Item causes 3 events: OnFileDelete (When the dialog, or the lack of, is displayed normally this is before the deletion of the file) an OnFolderUpdate (after the element is removed and the folder refreshes) and surprise yet another OnFileDelete (after the file has completely been deleted) this behavior is not of the component but of the api it relies on. Also according to some documentation I read the apis used on this article are entirely undocumented, perhaps it can be possible to stop the event however due to lack of documentation, it is unknown to me. <-------------- Begin UNIT code ----------------------------> {$IFNDEF VER80} {$IFNDEF VER90} {$IFNDEF VER93} {$DEFINE Delphi3orHigher} {$ENDIF} {$ENDIF} {$ENDIF} unit ShellNotify; interface uses Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs, {$IFNDEF Delphi3orHigher} OLE2, {$ELSE} ActiveX, ComObj, {$ENDIF} ShlObj; type NOTIFYREGISTER = record pidlPath : PItemIDList; bWatchSubtree : boolean; end; PNOTIFYREGISTER = ^NOTIFYREGISTER; const SNM_SHELLNOTIFICATION = WM_USER +1; SHCNF_ACCEPT_INTERRUPTS = $0001; SHCNF_ACCEPT_NON_INTERRUPTS = $0002; SHCNF_NO_PROXY = $8000; type TNotificationEvent = (neAssociationChange, neAttributesChange, neFileChange, neFileCreate, neFileDelete, neFileRename, neDriveAdd, neDriveRemove, neShellDriveAdd, neDriveSpaceChange, neMediaInsert, neMediaRemove, neFolderCreate, neFolderDelete, neFolderRename, neFolderUpdate, neNetShare, neNetUnShare, neServerDisconnect, neImageListChange); TNotificationEvents = set of TNotificationEvent; TShellNotificationEvent1 = procedure(Sender: TObject; Path: String)of Object; TShellNotificationEvent2 = procedure(Sender: TObject; path1, path2: String) of Object; TShellNotification = class(TComponent) private fWatchEvents: TNotificationEvents; fPath: String; fActive, fWatch: Boolean; prevPath1, prevPath2: String; PrevEvent: Integer; Handle, NotifyHandle: HWND; fOnAssociationChange: TNotifyEvent; fOnAttribChange: TShellNotificationEvent2; FOnCreate: TShellNotificationEvent1; FOnDelete: TShellNotificationEvent1; FOnDriveAdd: TShellNotificationEvent1; FOnDriveAddGui: TShellNotificationEvent1; FOnDriveRemove: TShellNotificationEvent1; FOnMediaInsert: TShellNotificationEvent1; FOnMediaRemove: TShellNotificationEvent1; FOnDirCreate: TShellNotificationEvent1; FOnNetShare: TShellNotificationEvent1; FOnNetUnShare: TShellNotificationEvent1; FOnRenameFolder: TShellNotificationEvent2; FOnItemRename: TShellNotificationEvent2; FOnFolderRemove: TShellNotificationEvent1; FOnServerDisconnect: TShellNotificationEvent1; FOnFolderUpdate: TShellNotificationEvent1; function PathFromPidl(Pidl: PItemIDList): String; procedure SetWatchEvents(const Value: TNotificationEvents); function GetActive: Boolean; procedure SetActive(const Value: Boolean); procedure SetPath(const Value: String); procedure SetWatch(const Value: Boolean); protected procedure ShellNotifyRegister; procedure ShellNotifyUnregister; procedure WndProc(var Message: TMessage); procedure DoAssociationChange; dynamic; procedure DoAttributesChange(Path1, Path2: String); dynamic; procedure DoCreateFile(Path: String); dynamic; procedure DoDeleteFile(Path: String); dynamic; procedure DoDriveAdd(Path:String); dynamic; procedure DoDriveAddGui(Path: String); dynamic; procedure DoDriveRemove(Path: String); dynamic; procedure DoMediaInsert(Path: String); dynamic; procedure DoMediaRemove(Path: String); dynamic; procedure DoDirCreate(Path: String); dynamic; procedure DoNetShare(Path: String); dynamic; procedure DoNetUnShare(Path: String); dynamic; procedure DoRenameFolder(Path1, Path2: String); dynamic; procedure DoRenameItem(Path1, Path2: String); dynamic; procedure DoFolderRemove(Path: String); dynamic; procedure DoServerDisconnect(Path: String); dynamic; procedure DoDirUpdate(Path: String); dynamic; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property Path: String read fPath write SetPath; property Active: Boolean read GetActive write SetActive; property WatchSubTree: Boolean read fWatch write SetWatch; property WatchEvents: TNotificationEvents read fWatchEvents write SetWatchEvents; property OnAssociationChange: TNotifyEvent read fOnAssociationChange write FOnAssociationChange; property OnAttributesChange: TShellNotificationEvent2 read fOnAttribChange write fOnAttribChange; property OnFileCreate: TShellNotificationEvent1 read FOnCreate write FOnCreate; property OnFolderRename: TShellNotificationEvent2 read FOnRenameFolder write FOnRenameFolder; property OnFolderUpdate: TShellNotificationEvent1 read FOnFolderUpdate write FOnFolderUpdate; property OnFileDelete: TShellNotificationEvent1 read FOnDelete write FOnDelete; property OnDriveAdd: TShellNotificationEvent1 read FOnDriveAdd write FOnDriveAdd; property OnFolderRemove: TShellNotificationEvent1 read FOnFolderRemove write FOnFolderRemove; property OnItemRename: TShellNotificationEvent2 read FOnItemRename write FOnItemRename; property OnDriveAddGui: TShellNotificationEvent1 read FOnDriveAddGui write FOnDriveAddGui; property OnDriveRemove: TShellNotificationEvent1 read FOnDriveRemove write FOnDriveRemove; property OnMediaInserted: TShellNotificationEvent1 read FOnMediaInsert write FOnMediaInsert; property OnMediaRemove: TShellNotificationEvent1 read FOnMediaRemove write FOnMediaRemove; property OnDirCreate: TShellNotificationEvent1 read FOnDirCreate write FOnDirCreate; property OnNetShare: TShellNotificationEvent1 read FOnNetShare write FOnNetShare; property OnNetUnShare: TShellNotificationEvent1 read FOnNetUnShare write FOnNetUnShare; property OnServerDisconnect: TShellNotificationEvent1 read FOnServerDisconnect write FOnServerDisconnect; end; function SHChangeNotifyRegister( hWnd: HWND; dwFlags: integer; wEventMask : cardinal; uMsg: UINT; cItems : integer; lpItems : PNOTIFYREGISTER) : HWND; stdcall; function SHChangeNotifyDeregister(hWnd: HWND) : boolean; stdcall; function SHILCreateFromPath(Path: Pointer; PIDL: PItemIDList; var Attributes: ULONG):HResult; stdcall; procedure Register; implementation const Shell32DLL = 'shell32.dll'; function SHChangeNotifyRegister; external Shell32DLL index 2; function SHChangeNotifyDeregister; external Shell32DLL index 4; function SHILCreateFromPath; external Shell32DLL index 28; { TShellNotification } constructor TShellNotification.Create(AOwner: TComponent); begin inherited Create( AOwner ); if not (csDesigning in ComponentState) then Handle := AllocateHWnd(WndProc); PrevEvent := 0; fWatchEvents := [neAssociationChange, neAttributesChange, neFileChange, neFileCreate, neFileDelete, neFileRename, neDriveAdd, neDriveRemove, neShellDriveAdd, neDriveSpaceChange, neMediaInsert, neMediaRemove, neFolderCreate, neFolderDelete, neFolderRename, neFolderUpdate, neNetShare, neNetUnShare, neServerDisconnect, neImageListChange]; end; destructor TShellNotification.Destroy; begin if not (csDesigning in ComponentState) then Active := False; if Handle <> 0 then DeallocateHWnd( Handle ); inherited Destroy; end; procedure TShellNotification.DoAssociationChange; begin if Assigned( fOnAssociationChange ) and (neAssociationChange in fWatchEvents) then fOnAssociationChange( Self ); end; procedure TShellNotification.DoAttributesChange; begin if Assigned( fOnAttribChange ) then fOnAttribChange( Self, Path1, Path2 ); end; procedure TShellNotification.DoCreateFile(Path: String); begin if Assigned( fOnCreate ) then FOnCreate(Self, Path) end; procedure TShellNotification.DoDeleteFile(Path: String); begin if Assigned( FOnDelete ) then FOnDelete(Self, Path); end; procedure TShellNotification.DoDirCreate(Path: String); begin if Assigned( FOnDirCreate ) then FOnDirCreate( Self, Path ); end; procedure TShellNotification.DoDirUpdate(Path: String); begin if Assigned( FOnFolderUpdate ) then FOnFolderUpdate(Self, Path); end; procedure TShellNotification.DoDriveAdd(Path: String); begin if Assigned( FOnDriveAdd ) then FOnDriveAdd(Self, Path); end; procedure TShellNotification.DoDriveAddGui(Path: String); begin if Assigned( FOnDriveAddGui ) then FOnDriveAdd(Self, Path); end; procedure TShellNotification.DoDriveRemove(Path: String); begin if Assigned( FOnDriveRemove ) then FOnDriveRemove(Self, Path); end; procedure TShellNotification.DoFolderRemove(Path: String); begin if Assigned(FOnFolderRemove) then FOnFolderRemove( Self, Path ); end; procedure TShellNotification.DoMediaInsert(Path: String); begin if Assigned( FOnMediaInsert ) then FOnMediaInsert(Self, Path); end; procedure TShellNotification.DoMediaRemove(Path: String); begin if Assigned(FOnMediaRemove) then FOnMediaRemove(Self, Path); end; procedure TShellNotification.DoNetShare(Path: String); begin if Assigned(FOnNetShare) then FOnNetShare(Self, Path); end; procedure TShellNotification.DoNetUnShare(Path: String); begin if Assigned(FOnNetUnShare) then FOnNetUnShare(Self, Path); end; procedure TShellNotification.DoRenameFolder(Path1, Path2: String); begin if Assigned( FOnRenameFolder ) then FOnRenameFolder(Self, Path1, Path2); end; procedure TShellNotification.DoRenameItem(Path1, Path2: String); begin if Assigned( FOnItemRename ) then FonItemRename(Self, Path1, Path2); end; procedure TShellNotification.DoServerDisconnect(Path: String); begin if Assigned( FOnServerDisconnect ) then FOnServerDisconnect(Self, Path); end; function TShellNotification.GetActive: Boolean; begin Result := (NotifyHandle <> 0) and (fActive); end; function TShellNotification.PathFromPidl(Pidl: PItemIDList): String; begin SetLength(Result, Max_Path); if not SHGetPathFromIDList(Pidl, PChar(Result)) then Result := ''; if pos(#0, Result) > 0 then SetLength(Result, pos(#0, Result)); end; procedure TShellNotification.SetActive(const Value: Boolean); begin if (Value <> fActive) then begin fActive := Value; if fActive then ShellNotifyRegister else ShellNotifyUnregister; end; end; procedure TShellNotification.SetPath(const Value: String); begin if fPath <> Value then begin fPath := Value; ShellNotifyRegister; end; end; procedure TShellNotification.SetWatch(const Value: Boolean); begin if fWatch <> Value then begin fWatch := Value; ShellNotifyRegister; end; end; procedure TShellNotification.SetWatchEvents( const Value: TNotificationEvents); begin if fWatchEvents <> Value then begin fWatchEvents := Value; ShellNotifyRegister; end; end; procedure TShellNotification.ShellNotifyRegister; var Option: TNotificationEvent; NotifyRecord: NOTIFYREGISTER; Flags: DWORD; Pidl: PItemIDList; Attributes: ULONG; const NotifyFlags: array[TNotificationEvent] of DWORD = ( SHCNE_ASSOCCHANGED, SHCNE_ATTRIBUTES, SHCNE_UPDATEITEM, SHCNE_CREATE, SHCNE_DELETE, SHCNE_RENAMEITEM, SHCNE_DRIVEADD, SHCNE_DRIVEREMOVED, SHCNE_DRIVEADDGUI, SHCNE_FREESPACE, SHCNE_MEDIAINSERTED, SHCNE_MEDIAREMOVED, SHCNE_MKDIR, SHCNE_RMDIR, SHCNE_RENAMEFOLDER, SHCNE_UPDATEDIR, SHCNE_NETSHARE, SHCNE_NETUNSHARE, SHCNE_SERVERDISCONNECT, SHCNE_UPDATEIMAGE); begin ShellNotifyUnregister; if not (csDesigning in ComponentState) and not (csLoading in ComponentState) then begin SHILCreatefromPath( PChar(fPath), Addr(Pidl), Attributes); NotifyRecord.pidlPath := Pidl; NotifyRecord.bWatchSubtree := fWatch; Flags := 0; for Option := Low(Option) to High(Option) do if (Option in FWatchEvents) then Flags := Flags or NotifyFlags[Option]; NotifyHandle := SHChangeNotifyRegister(Handle, SHCNF_ACCEPT_INTERRUPTS or SHCNF_ACCEPT_NON_INTERRUPTS, Flags, SNM_SHELLNOTIFICATION, 1, @NotifyRecord); end; end; procedure TShellNotification.ShellNotifyUnregister; begin if NotifyHandle <> 0 then SHChangeNotifyDeregister(NotifyHandle); end; procedure TShellNotification.WndProc(var Message: TMessage); type TPIDLLIST = record pidlist : array[1..2] of PITEMIDLIST; end; PIDARRAY = ^TPIDLLIST; var Path1 : string; Path2 : string; ptr : PIDARRAY; repeated : boolean; event : longint; begin case Message.Msg of SNM_SHELLNOTIFICATION: begin event := Message.LParam and ($7FFFFFFF); Ptr := PIDARRAY(Message.WParam); Path1 := PathFromPidl( Ptr^.pidlist[1] ); Path2 := PathFromPidl( Ptr^.pidList[2] ); repeated := (PrevEvent = event) and (uppercase(prevpath1) = uppercase(Path1)) and (uppercase(prevpath2) = uppercase(Path2)); if Repeated then exit; PrevEvent := Message.Msg; prevPath1 := Path1; prevPath2 := Path2; case event of SHCNE_ASSOCCHANGED : DoAssociationChange; SHCNE_ATTRIBUTES : DoAttributesChange( Path1, Path2); SHCNE_CREATE : DoCreateFile(Path1); SHCNE_DELETE : DoDeleteFile(Path1); SHCNE_DRIVEADD : DoDriveAdd(Path1); SHCNE_DRIVEADDGUI : DoDriveAddGui(path1); SHCNE_DRIVEREMOVED : DoDriveRemove(Path1); SHCNE_MEDIAINSERTED : DoMediaInsert(Path1); SHCNE_MEDIAREMOVED : DoMediaRemove(Path1); SHCNE_MKDIR : DoDirCreate(Path1); SHCNE_NETSHARE : DoNetShare(Path1); SHCNE_NETUNSHARE : DoNetUnShare(Path1); SHCNE_RENAMEFOLDER : DoRenameFolder(Path1, Path2); SHCNE_RENAMEITEM : DoRenameItem(Path1, Path2); SHCNE_RMDIR : DoFolderRemove(Path1); SHCNE_SERVERDISCONNECT : DoServerDisconnect(Path); SHCNE_UPDATEDIR : DoDirUpdate(Path); SHCNE_UPDATEIMAGE : ; SHCNE_UPDATEITEM : ; end;//Case event of end;//SNM_SHELLNOTIFICATION end; //case end; procedure Register; begin RegisterComponents('Shell', [TShellNotification]); end; end.