Hi,
Post by Chuck ChoppI should qualify that request just a bit.... I've done a # of Google
searches, and I've found the one article on GeoCities that discusses
SHChangeNotifyRegister() in a little more detail, including the
dcoumentation of a data structure that contains a PIDL. So, that
Caution, this system works different on different OS's. Test it on
all target versions of Windows. Also these are "funny" half baked
PIDL's. Don't use them for anything but identifiying the object. Very
little of the Details (report mode) are stored in them and if you want
to use them to display details it will fail. The PIDLs also don't
belong to you, make copies with IMalloc and let them pass on.
Post by Chuck ChoppWhatn I'm not finding is a good clean example of the use of this
function in C/C++. The closest I've gotten is some VB code, so
perhaps I'll have to digest that and see if I can convert it into
Can you read Delphi? This may or not be helpful to you.
const
{$EXTERNALSYM SHCNF_ACCEPT_INTERRUPTS}
SHCNF_ACCEPT_INTERRUPTS = $0001;
{$EXTERNALSYM SHCNF_ACCEPT_NON_INTERRUPTS}
SHCNF_ACCEPT_NON_INTERRUPTS = $0002;
{$EXTERNALSYM SHCNF_NO_PROXY}
SHCNF_NO_PROXY = $8000;
type
// Structures for the undocumented ChangeNotify handler
TNotifyRegister = packed record
ItemIDList: PItemIDList;
bWatchSubTree: Bool;
end;
PDWordItemID = ^TDWordItemID;
TDWordItemID = packed record
cb: Word; { Size of Structure }
dwItem1: DWORD;
dwItem2: DWORD;
end;
PShellNotifyRec = ^TShellNotifyRec;
TShellNotifyRec = packed record
PIDL1, // Most ne_xxxx Notifications
PIDL2: PItemIDList;
end;
procedure TVirtualShellChangeThread.RegisterChangeNotify;
var
NR: TNotifyRegister;
Flags, Msg: LongWord;
begin
if (ChangeNotifyHandle = 0) and (NotifyWindowHandle > 0) then
begin
{ Watch everything }
NR.ItemIDList := nil;
NR.bWatchSubTree := True;
Flags := SHCNF_ACCEPT_INTERRUPTS or SHCNF_ACCEPT_NON_INTERRUPTS;
Msg := WM_CHANGENOTIFY;
if (Win32Platform = VER_PLATFORM_WIN32_NT) and
Assigned(SHChangeNotificationLock) and
Assigned(SHChangeNotificationUnlock)
then begin
Flags := Flags or SHCNF_NO_PROXY;
Msg := WM_CHANGENOTIFY_NT
end;
ChangeNotifyHandle := SHChangeNotifyRegister(NotifyWindowHandle,
Flags, SHCNE_ALLEVENTS, Msg, 1, NR);
end;
end;
procedure TVirtualShellChangeThread.UnRegisterChangeNotify;
begin
// Unregister the SHChangeNotify
if ChangeNotifyHandle <> 0 then
SHChangeNotifyDeRegister(ChangeNotifyHandle);
ChangeNotifyHandle := 0
end;
// Window Procedure
function TVirtualShellChangeThread.NotifyWndProc(Wnd: HWND; Msg: UINT;
wParam, lParam: LPARAM): LRESULT;
// Takes the information from the ShellEvent and packs it into a
thread which collects them
// then packs them by combining duplicates.
procedure AddEventToList(Event: LongWord; SNR: PShellNotifyRec;
W1, W2: DWORD);
begin
case Event of
SHCNE_ASSOCCHANGED:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneAssoccChanged, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_ATTRIBUTES:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneAttributes, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_CREATE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneCreate, SNR.PIDL1, SNR.PIDL2, W1, W2, ThreadPIDLMgr);
SHCNE_DELETE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneDelete, SNR.PIDL1, SNR.PIDL2, W1, W2, ThreadPIDLMgr);
SHCNE_DRIVEADD:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneDriveAdd, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_DRIVEADDGUI:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneDriveAddGUI, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_DRIVEREMOVED:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneDriveRemoved, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_FREESPACE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneFreeSpace, nil, nil, PDWordItemID(SNR.PIDL1).dwItem1,
PDWordItemID(SNR.PIDL1).dwItem2, ThreadPIDLMgr);
SHCNE_MEDIAINSERTED:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneMediaInserted, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_MEDIAREMOVED:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneMediaRemoved, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_MKDIR:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneMkDir, SNR.PIDL1, SNR.PIDL2, W1, W2, ThreadPIDLMgr);
SHCNE_NETSHARE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneNetShare, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_NETUNSHARE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneNetUnShare, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_RENAMEFOLDER:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneRenameFolder, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_RENAMEITEM:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneRenameItem, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_RMDIR:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneRmDir, SNR.PIDL1, SNR.PIDL2, W1, W2, ThreadPIDLMgr);
SHCNE_SERVERDISCONNECT:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneServerDisconnect, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_UPDATEDIR:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneUpdateDir, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
SHCNE_UPDATEIMAGE:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneUpdateImage, nil, nil,
PDWordItemID(SNR.PIDL1).dwItem1,
PDWordItemID(SNR.PIDL1).dwItem2, ThreadPIDLMgr);
SHCNE_UPDATEITEM:
ChangeNotifier.ChangeDispatchThread.AddEvent(
vsneUpdateItem, SNR.PIDL1, SNR.PIDL2, W1, W2,
ThreadPIDLMgr);
end;
end;
var
Message: TMessage;
Event: DWORD;
Handle: THandle;
SNR: PShellNotifyRec;
Rec: TShellNotifyRec;
i, j: integer;
PIDL: PItemIDList;
OwnP1, OwnP2: Boolean;
begin
Message.Msg := Msg;
Message.lParam := lParam;
Message.wParam := wParam;
Message.Result := 0;
case Msg of
WM_NCCREATE: Message.Result := 1;
{ Message sent by shell }
WM_CHANGENOTIFY_NT:
begin
Handle := SHChangeNotificationLock(WParam, LParam, SNR, Event);
if Handle <> 0 then
try
AddEventToList(Event, SNR, 0, 0);
finally
SHChangeNotificationUnLock(Handle);
end
end;
WM_CHANGENOTIFY:
begin
AddEventToList(lParam, PShellNotifyRec(wParam), 0, 0);
if MapVirtualFolders then
begin
// Due to bug? in 9x we need to generate PIDLs for the My
Documents under
// the desktop if we get one for the physical folder
if Assigned(MyDocsPIDL) then
begin
OwnP1 := False;
OwnP2 := False;
i := ThreadPIDLMgr.IDCount(MyDocsPIDL);
Rec := PShellNotifyRec(wParam)^;
if ILIsParent(MyDocsPIDL, PShellNotifyRec(wParam).PIDL1,
False) then
begin
PIDL := PShellNotifyRec(wParam).PIDL1;
for j := 0 to i - 1 do
PIDL := ThreadPIDLMgr.NextID(PIDL);
Rec.PIDL1 := ThreadPIDLMgr.AppendPIDL(MyDocsDeskPIDL,
PIDL);
OwnP1 := True;
end;
if ILIsParent(MyDocsPIDL, PShellNotifyRec(wParam).PIDL2,
False) then
begin
PIDL := PShellNotifyRec(wParam).PIDL2;
for j := 0 to i - 1 do
PIDL := ThreadPIDLMgr.NextID(PIDL);
Rec.PIDL2 := ThreadPIDLMgr.AppendPIDL(MyDocsDeskPIDL,
PIDL);
OwnP2 := True;
end;
if OwnP1 or OwnP2 then
begin
AddEventToList(lParam, @Rec, 0, 0);
if OwnP1 then
ThreadPIDLMgr.FreePIDL(Rec.PIDL1);
if OwnP2 then
ThreadPIDLMgr.FreePIDL(Rec.PIDL2);
end
end
end;
end;
else
Message.Result := DefWindowProc(NotifyWindowHandle, Msg, wParam,
lParam);
end;
Result := Message.Result;
end;
Jim
--
www.mustangpeak.net