unit shellevents;
// a component to set and receive shell events. Written by Glenn9999 @ tek-tips.com
interface
uses shellapi, shlobj, activex, windows, classes, messages, sysutils,
controls, forms;
// all constants are related to shell api calls added in this unit.
const
SCHNE_RENAMEITEM = $01;
SCHNE_CREATE = $02;
SCHNE_DELETE = $04;
SCHNE_MKDIR = $08;
SCHNE_RMDIR = $10;
SCHNE_MEDIAINSERTED = $20;
SCHNE_MEDIAREMOVED = $40;
SCHNE_DRIVEREMOVED = $80;
SCHNE_DRIVEADD = $100;
SCHNE_NETSHARE = $200;
SCHNE_NETUNSHARE = $400;
SCHNE_ATTRIBUTES = $800;
SCHNE_UPDATEDIR = $1000;
SCHNE_UPDATEITEM = $2000;
SCHNE_SERVERDISCONNECT = $4000;
SCHNE_UPDATEIMAGE = $8000;
SCHNE_DRIVEADDGUI = $10000;
SCHNE_RENAMEFOLDER = $20000;
SCHNE_FREESPACE = $40000;
SCHNE_EXTENDED_EVENT = $04000000;
SHCNE_ASSOCCHANGED = $08000000;
SCHNE_DISKEVENTS = $0002381F;
SCHNE_GLOBALEVENTS = $0C0581E0;
SCHNE_ALLEVENTS = $7FFFFFFF;
SCHNE_INTERRUPT = $80000000;
SHCNRF_INTERRUPTLEVEL = $0001;
SHCNRF_SHELLLEVEL = $0002;
SHCNRF_RECURSIVEINTERRUPT = $1000;
SHCNRF_NEWDELIVERY = $8000;
type
// declarations related to shell api calls added to this unit.
TSHNotifyStruct = packed record
dw1: PItemIDList;
dw2: PItemIDList;
end;
PSHNotifyStruct = ^TSHNotifyStruct;
TSHChangeNotifyEntry = record
pidl: PItemIdList;
fRecursive: BOOL;
end;
// declarations related to the shell events component here.
TShellEvent = (seRenameItem, seCreate, seDelete, seMkDir, seRmDir,
seMediaInserted, seMediaRemoved, seDriveRemoved, seDriveAdd,
seNetShare, seNetUnShare, seAttributes, seUpdateDir, seUpdateItem,
seServerDisconnect, seUpdateImage, seDriveAddGUI, seRenameFolder,
seFreeSpace, seExtendedEvent, seAssocChanged);
TShellEventSet = set of TShellEvent;
TShellNotifyEvent = procedure (Sender: TObject; LEvent: TShellEvent;
pidl1: PItemIDList; pidl2:PItemIDList) of object;
TShellNotifyHandler = class(TComponent)
private
FWndProc: TWndMethod;
FShellMsg: DWord;
FOnShellNotify: TShellNotifyEvent;
FEvents: TShellEventSet;
FRecursive: Boolean;
function IsTwoParmEvent(LEvent: Longint): boolean;
function IsItemNotificationEvent(lEvent: Longint): boolean;
function EventSetToDWord(FEvents: TShellEventSet): DWord;
function DWordToShellEvent(FEvent: DWord): TShellEvent;
public
Constructor Create(AOwner: TComponent); override;
Destructor Destroy; override;
function ShellEventString(inevent: TShellEvent): String;
function RegisterPIDL(FHWnd: HWnd; pidl: PitemIDList):THandle;
function Deregister(MonitorHandle: THandle): boolean;
procedure WindowProc(Var msg: TMessage);
published
property Events: TShellEventSet read FEvents write FEvents;
property Recursive: Boolean read FRecursive write FRecursive;
property ShellMsg: DWord read FShellMsg write FShellMsg;
property OnShellNotify: TShellNotifyEvent read FOnShellNotify write FOnShellNotify;
end;
function SHChangeNotifyRegister(OwnerHwnd: HWnd; fSources: integer;
fEvents: Dword; wmsg: UINT; cEntries: Integer;
var pshcne: TSHChangeNotifyEntry): HResult; stdcall;
function SHChangeNotifyDeRegister(ulID: DWord): BOOL; stdcall;
function SHChangeNotification_Lock(hChangeNotification: THandle; dwProcessID: DWord;
out pppidl: PSHNotifyStruct; out plEvent: Longint): THandle; stdcall;
function SHChangeNotification_Unlock(hLock: THandle): Boolean; stdcall;
procedure Register;
implementation
function SHChangeNotifyRegister; external shell32 name 'SHChangeNotifyRegister';
function SHChangeNotifyDeRegister; external shell32 name 'SHChangeNotifyDeregister';
function SHChangeNotification_Lock;
external shell32 name 'SHChangeNotification_Lock';
function SHChangeNotification_Unlock;
external shell32 name 'SHChangeNotification_Unlock';
function TShellNotifyHandler.IsTwoParmEvent(LEvent: Longint): boolean;
// takes an event type and returns whether two parms are expected or not
var
flagval: Longint;
begin
// SCHNE_ASSOCCHANGED is listed in this function and the one below, which is it?
flagval := (lEvent and ({SHCNE_ASSOCCHANGED or }SHCNE_RENAMEFOLDER
or SHCNE_RENAMEITEM));
Result := (flagval > 0);
end;
function TShellNotifyHandler.IsItemNotificationEvent(lEvent: Longint): boolean;
// takes event type and returns whether event has no parms.
var
flagval: Longint;
begin
flagval := (lEvent and (SCHNE_UPDATEIMAGE or SHCNE_ASSOCCHANGED
or SHCNE_EXTENDED_EVENT or SHCNE_FREESPACE
or SHCNE_DRIVEADDGUI or SHCNE_SERVERDISCONNECT));
Result := (flagval > 0);
end;
procedure TShellNotifyHandler.WindowProc(Var msg: TMessage);
// event processing proc for the form.
var
hNotifyLock: THandle;
lEvent: Longint;
pgpidl: PSHNotifyStruct;
begin
if Msg.Msg = FShellMsg then
begin
hNotifyLock := SHChangeNotification_Lock(THandle(Msg.WParam),DWord(Msg.LParam),
pgpidl, lEvent);
if (hNotifyLock <> 0) then
begin
if Assigned(FOnShellNotify) then
begin
if IsItemNotificationEvent(Levent) then
FOnShellNotify(Self, DWordToShellEvent(LEvent), nil, nil)
else
if IsTwoParmEvent(Levent) then
FOnShellNotify(Self, DWordToShellEvent(LEvent), pgpidl^.dw1, pgpidl^.dw2)
else
FOnShellNotify(Self, DWordToShellEvent(LEvent), pgpidl^.dw1, nil);
end;
SHChangeNotification_Unlock(hNotifyLock);
end;
end
else
FWndProc(Msg);
end;
Constructor TShellNotifyHandler.Create(AOwner: TComponent);
begin
FWndProc := TForm(AOwner).WindowProc;
TForm(AOwner).WindowProc := WindowProc;
inherited create(aowner);
end;
Destructor TShellNotifyHandler.Destroy;
begin
Inherited;
end;
function TShellNotifyHandler.ShellEventString(inevent: TShellEvent): String;
// takes TShellEvent type and returns string representation of the value
begin
case inevent of
seRenameItem: Result := 'seRenameItem';
seCreate: Result := 'seCreate';
seDelete: Result := 'seDelete';
seMkDir: Result := 'seMkDir';
seRmDir: Result := 'seRmDir';
seMediaInserted: Result := 'seMediaInserted';
seMediaRemoved: Result := 'seMediaRemoved';
seDriveRemoved: Result := 'seDriveRemoved';
seDriveAdd: Result := 'seDriveAdd';
seNetShare: Result := 'seNetShare';
seNetUnshare: Result := 'seNetUnshare';
seAttributes: Result := 'seAttributes';
seUpdateDir: Result := 'seUpdateDir';
seUpdateItem: Result := 'seUpdateItem';
seServerDisconnect: Result := 'seServerDisconnect';
seUpdateImage: Result := 'seUpdateImage';
seDriveAddGUI: Result := 'seDriveAddGUI';
seRenameFolder: Result := 'seRenameFolder';
seFreeSpace: Result := 'seFreeSpace';
seExtendedEvent: Result := 'seExtendedEvent';
seAssocChanged: Result := 'seAssocChanged';
else
Result := 'UnknownEvent';
end;
end;
function TShellNotifyHandler.DWordToShellEvent(FEvent: DWord): TShellEvent;
// puts a single SHChangeNotifyRegister event to TShellEvent;
begin
case FEvent of
SCHNE_RENAMEITEM: Result := seRenameItem;
SCHNE_CREATE: Result := seCreate;
SCHNE_DELETE: Result := seDelete;
SCHNE_MKDIR: Result := seMkDir;
SCHNE_RMDIR: Result := seRmDir;
SCHNE_MEDIAINSERTED: Result := seMediaInserted;
SCHNE_MEDIAREMOVED: Result := seMediaRemoved;
SCHNE_DRIVEREMOVED: Result := seDriveRemoved;
SCHNE_DRIVEADD: Result := seDriveAdd;
SCHNE_NETSHARE: Result := seNetShare;
SCHNE_NETUNSHARE: Result := seNetUnShare;
SCHNE_ATTRIBUTES: Result := seAttributes;
SCHNE_UPDATEDIR: Result := seUpdateDir;
SCHNE_UPDATEITEM: Result := seUpdateItem;
SCHNE_SERVERDISCONNECT: Result := seServerDisconnect;
SCHNE_UPDATEIMAGE: Result := seUpdateImage;
SCHNE_DRIVEADDGUI: Result := seDriveAddGUI;
SCHNE_RENAMEFOLDER: Result := seRenameFolder;
SCHNE_FREESPACE: Result := seFreeSpace;
SCHNE_EXTENDED_EVENT: Result := seExtendedEvent;
SHCNE_ASSOCCHANGED: Result := seAssocChanged;
end;
end;
function TShellNotifyHandler.EventSetToDWord(FEvents: TShellEventSet): DWord;
begin
// convert FEvents to something SHChangeNotifyRegister understands
Result := 0;
if seRenameItem in FEvents then
Result := Result or SCHNE_RENAMEITEM;
if seCreate in FEvents then
Result := Result or SCHNE_CREATE;
if seDelete in FEvents then
Result := Result or SCHNE_DELETE;
if seMkDir in FEvents then
Result := Result or SCHNE_MKDIR;
if seRmDir in FEvents then
Result := Result or SCHNE_RMDIR;
if seMediaInserted in FEvents then
Result := Result or SCHNE_MEDIAINSERTED;
if seMediaRemoved in FEvents then
Result := Result or SCHNE_MEDIAREMOVED;
if seDriveRemoved in FEvents then
Result := Result or SCHNE_DRIVEREMOVED;
if SeDriveAdd in FEvents then
Result := Result or SCHNE_DRIVEADD;
if seNetShare in FEvents then
Result := Result or SCHNE_NETSHARE;
if seNetUnShare in FEvents then
Result := Result or SCHNE_NETUNSHARE;
if seAttributes in FEvents then
Result := Result or SCHNE_ATTRIBUTES;
if seUpdateDir in FEvents then
Result := Result or SCHNE_UPDATEDIR;
if SeUpdateItem in FEvents then
Result := Result or SCHNE_UPDATEITEM;
if SeServerDisconnect in FEvents then
Result := Result or SCHNE_SERVERDISCONNECT;
if SeUpdateImage in FEvents then
Result := Result or SCHNE_UPDATEIMAGE;
if SeDriveAddGUI in FEvents then
Result := Result or SCHNE_DRIVEADDGUI;
if SeRenameFolder in FEvents then
Result := Result or SCHNE_RENAMEFOLDER;
if SEFreeSpace in FEvents then
Result := Result or SCHNE_FREESPACE;
if seExtendedEvent in FEvents then
Result := Result or SCHNE_EXTENDED_EVENT;
if seAssocChanged in FEvents then
Result := Result or SHCNE_ASSOCCHANGED;
end;
function TShellNotifyHandler.RegisterPIDL(FHWnd: HWnd; pidl: PitemIDList):THandle;
// this is used to register a shell event.
var
stPIDL: TSHChangeNotifyEntry;
begin
stPIDL.pidl := pidl;
stPIDL.fRecursive := FRecursive;
Result := SHChangeNotifyRegister(FHWnd,
SHCNRF_INTERRUPTLEVEL or SHCNRF_SHELLLEVEL
or SHCNRF_RECURSIVEINTERRUPT or SHCNRF_NEWDELIVERY,
EventSetToDWord(FEvents),
FShellMsg, 1, stPIDL);
end;
function TShellNotifyHandler.Deregister(MonitorHandle: THandle): boolean;
// this is used to unregister a shell event.
begin
Result := SHChangeNotifyDeregister(MonitorHandle);
end;
procedure Register;
begin
RegisterComponents('Samples', [TShellNotifyHandler]);
end;
end.