Introduction
When you build an application that shows files or directories of the local computer, you must know if the files already exist. You can add refresh functions but there is another way to know in real time when something has changed by using the SHChangeNotifyRegister
API.
For a complete overview of shell change notifications, James Holderness made a very useful "Shell Notifications" page.
The structures and enumerations are not listed here, but are accessible in the sources.
SHChangeNotifyRegister and SHChangeNotifyUnregister declarations
The API calls are hidden: SHChangeNotifyRegister
uses an entry point value of 2 and SHChangeNotifyUnregister
uses 4.
using System.Runtime.InteropServices;
[DllImport("shell32.dll", EntryPoint="#2", CharSet=CharSet.Auto)]
private static extern uint SHChangeNotifyRegister(
IntPtr hWnd,
SHCNF fSources,
SHCNE fEvents,
uint wMsg,
int cEntries,
ref SHChangeNotifyEntry pFsne);
[DllImport("shell32.dll", EntryPoint="#4", CharSet=CharSet.Auto)]
[return:MarshalAs(UnmanagedType.Bool)]
private static extern Boolean SHChangeNotifyUnregister(
ulong hNotify);
Register a Handle
When a modification occurs, the shell sends a notification to each registered handle. You have to specify the folder root by the pidl
retrieved by the SHGetSpecialFolderLocation
API.
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
private static extern uint SHGetSpecialFolderLocation(
IntPtr hWnd,
CSIDL nFolder,
out IntPtr Pidl);
public ulong RegisterChangeNotify(IntPtr hWnd, CSIDL FolderID,
bool Recursively)
{
if(notifyid != 0) return(0);
SHChangeNotifyEntry changeentry = new SHChangeNotifyEntry();
changeentry.pIdl = GetPidlFromFolderID(hWnd, FolderID);
changeentry.Recursively = Recursively;
notifyid = SHChangeNotifyRegister(
hWnd,
SHCNF.SHCNF_TYPE | SHCNF.SHCNF_IDLIST,
SHCNE.SHCNE_ALLEVENTS | SHCNE.SHCNE_INTERRUPT,
WM_SHNOTIFY,
1,
ref changeentry);
return(notifyid);
}
public Boolean UnregisterChangeNotify()
{
if(notifyid == 0) return(false);
if(SHChangeNotifyUnregister(notifyid))
{
notifyid = 0;
return(true);
}
return(false);
}
public static IntPtr GetPidlFromFolderID(IntPtr hWnd, CSIDL Id)
{
IntPtr pIdl = IntPtr.Zero;
SHGetFolderLocationReturnValues res =
(SHGetFolderLocationReturnValues)
SHGetSpecialFolderLocation( hWnd,
Id,
out pIdl);
return(pIdl);
}
Notification Reception
When you receive a notification, you want to know the type of the operation, the concerned item, and the new value of the item. This information is stored in the SHNOTIFYSTRUCT
which the WParam
points to. You can then add the new notification to an ArrayList
that contains each notification.
public System.Collections.ArrayList NotificationsReceived =
new System.Collections.ArrayList();
public bool NotificationReceipt(IntPtr wParam, IntPtr lParam)
{
SHNOTIFYSTRUCT shNotify = (SHNOTIFYSTRUCT) Marshal.PtrToStructure(
wParam,
typeof(SHNOTIFYSTRUCT));
NotifyInfos info = new NotifyInfos((SHCNE)(int) lParam);
if(info.Notification == SHCNE.SHCNE_FREESPACE ||
info.Notification == SHCNE.SHCNE_UPDATEIMAGE)
return(false);
info.Item1 = GetPathFromPidl(shNotify.dwItem1);
info.Item2 = GetPathFromPidl(shNotify.dwItem2);
if(NotificationsReceived.Contains(info))
return(false);
NotificationsReceived.Add(info);
return(true);
}
If the notification was not in the NotificationsReceived
list, the NotificationReceipt
method adds it to the list and returns true
.
Now, a new case must be added in the WndProc
of the Form, so that the notifications can be handled:
The code below handles the notification by checking if it has been added to the list. if it isn't, NotificationReceipt
returns true
and the code calls NewOperation
passing in the NotifyInfos
struct
corresponding to the latest event.
private ShellNotifications Notifications = new ShellNotifications();
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case (int) ShellNotifications.WM_SHNOTIFY:
bool bAdded = Notifications.NotificationReceipt(
m.WParam, m.LParam);
if( bAdded )
{
NotifyInfos nis = (NotifyInfos)
Notifications.NotificationsReceived[
Notifications.NotificationsReceived.Count - 1];
NewOperation(nis);
}
break;
}
base.WndProc(ref m);
}
private void NewOperation(NotifyInfos infos)
{
listView1.Items.Add(
new ListViewItem(
new string[] {
infos.Notification.ToString(),
infos.Item1,
infos.Item2}));
}
Revision History
- October 17, 2002 - Initial post