Shell Events 

Content / madShell /...
www.madshi.net

Sometimes you would like to be notified about when specific changes occur, e.g. when a new folder is created or when a file is renamed or when a share is removed. There must be such notification funtionality available somewhere, we can see that clearly in how the Windows Explorer refreshes itself automatically without pushing the CPU because of steadily polling.

Perhaps you know the API "FindFirstChangeNotification", but this API only tells us, *that* something happened. It doesn't tell us, *what* has happended in detail. It's not really useless, but the wish for something better keeps living. In winNT/2000 we can use "ReadDirectoryChangesW", but that API is not supported in win9x, furthermore it only notifies us about file/directory changes, not about share changes etc.

Recently I found infos about undocumented APIs, which do basically what I was wishing for. Furthermore they work in all 32bit Windows versions. They seem to be what the Explorer is using inside. However, they're not too reliable. It might happen, that you get a "seShareAdded" notification, although the share was removed in reality or such things. It also may happen from time to time, that an event is missed. So don't rely on it. But it works well for situations where it doesn't matter too much, when you miss an event, e.g. when refreshing an Explorer like TListView.

Installing a shell event notification routine is very easy. Just write a notification callback routine with the correct parameters, then pass it to the function "RegisterShellEvent". That's it.

type
  TShellEvent   = procedure (event: TShellEventType; const obj1, obj2: IShellObj; drive: char; value: cardinal);
  TShellEventOO = procedure (event: TShellEventType; const obj1, obj2: IShellObj; drive: char; value: cardinal) of object;

function RegisterShellEvent (eventProc    : TShellEvent;
                             root         : string           = '*';
                             watchSubtree : boolean          = true;
                             eventTypes   : TShellEventTypes = CShellEvents_All) : boolean; overload;
function RegisterShellEvent (eventProc    : TShellEventOO;
                             root         : string           = '*';
                             watchSubtree : boolean          = true;
                             eventTypes   : TShellEventTypes = CShellEvents_All) : boolean; overload;

Uninstalling a notification routine is as easy:

function UnregisterShellEvent (event: TShellEvent  ) : boolean; overload;
function UnregisterShellEvent (event: TShellEventOO) : boolean; overload;

There are a lot of different notification events, you can choose about which events you actually want to be informed. The enumeration type "TShellEventType" contains a list of all possible events:

type
  TShellEventType = (seItemCreated, seItemRenamed, seItemChanged, seItemDeleted,     // item
                     seDirCreated,  seDirRenamed,  seDirChanged,  seDirDeleted,      // dir
                     seAttributesChanged,                                            // item & dir
                     seFreespaceChanged,                                             // drive
                     seDriveAdded, seDriveAddedGui, seDriveRemoved,                  // drive
                     seMediaInserted, seMediaRemoved,                                // medium
                     seShareAdded, seShareRemoved, seServerDisconnect,               // network
                     seImageChanged, seAssociationChanged,                           // etc.
                     seThemeChanged, seOrderChanged, seMsiChanged, seMsiUninstalled  // extended
                    );
  TShellEventTypes = set of TShellEventType;

const
  CShellEvents_Disk  = [seItemCreated        .. seAttributesChanged  ];
  CShellEvents_Share = [seShareAdded         .. seShareRemoved       ];
  CShellEvents_All   = [low(TShellEventType) .. high(TShellEventType)];

Here comes a little example project, which logs file and directory changes into a TMemo component:

unit ShellEvents;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, madShell;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure ShellEvent(event: TShellEventType; const obj1, obj2: IShellObj; drive: char; value: cardinal);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ShellEvent(event: TShellEventType; const obj1, obj2: IShellObj; drive: char; value: cardinal);
begin
  case event of
    seItemCreated, seDirCreated : Memo1.Lines.Add('Add: ' + obj1.Description);
    seItemDeleted, seDirDeleted : Memo1.Lines.Add('Del: ' + obj1.Description);
    seItemRenamed, seDirRenamed : Memo1.Lines.Add('Ren: ' + obj1.Description + ' -> ' + obj2.Description);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RegisterShellEvent(ShellEvent);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnregisterShellEvent(ShellEvent);
end;

end.