Click here to Skip to main content
15,867,986 members
Articles / Programming Languages / C#

Shell Notifications in C#

Rate me:
Please Sign up or sign in to vote.
4.94/5 (15 votes)
16 Oct 2002Ms-PL1 min read 165.6K   3.4K   62   28
Shows how to receive shell notifications for specified directories

Shell Notifications

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.

C#
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.

C#
[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.

C#
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);
    
    // These notifications are Not supported
    if(info.Notification == SHCNE.SHCNE_FREESPACE ||
        info.Notification == SHCNE.SHCNE_UPDATEIMAGE)
        return(false);
    
    info.Item1 = GetPathFromPidl(shNotify.dwItem1);
    info.Item2 = GetPathFromPidl(shNotify.dwItem2);
    
    // Was this notification in the received notifications ?
    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.

C#
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)
{
    //
    // You can add your code here.
    // The line below is an example : 
    // it adds the operation informations in a listview.
    //
    listView1.Items.Add(
        new ListViewItem(
            new string[] {
                infos.Notification.ToString(),
                infos.Item1,
                infos.Item2}));
}

Revision History

  • October 17, 2002 - Initial post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Web Developer
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSHChangeNotifyRegister Does not notifies when USB is ejected in Non Admin Mode Pin
harsha_techi16-Mar-15 4:39
harsha_techi16-Mar-15 4:39 
GeneralMy vote of 5 Pin
malac113-Jul-10 9:26
malac113-Jul-10 9:26 
QuestionCompact framework compatibility?? Pin
nuttynibbles20-Feb-10 7:29
nuttynibbles20-Feb-10 7:29 
GeneralSHChangeNotifyUnregister Pin
bzucko5-Mar-07 2:52
bzucko5-Mar-07 2:52 
Generalthis only works from explorer Pin
Billiebub12-Feb-07 1:16
Billiebub12-Feb-07 1:16 
GeneralGetDisplayNameFromPidl Pin
Lesuran9-Nov-04 3:44
Lesuran9-Nov-04 3:44 
GeneralRe: GetDisplayNameFromPidl Pin
felix at home24-Sep-06 23:07
felix at home24-Sep-06 23:07 
GeneralGet path name directly instead pidls Pin
Freigeist21-Sep-04 6:34
Freigeist21-Sep-04 6:34 
Questioncan not log all changes Pin
zbshen19-May-04 7:43
zbshen19-May-04 7:43 
i delete 13 files, but only show 9 SHCNE_DELETE messages. But if i delete 5 files, it looks work well. Seemed some deletions had not notified the application if there are too many deletions(>10). Why?
AnswerRe: can not log all changes Pin
SimmRein10-Sep-09 16:58
SimmRein10-Sep-09 16:58 
QuestionHow to watch for viewing & executing actions Pin
zuken212-Apr-04 13:54
zuken212-Apr-04 13:54 
GeneralCan't download although i logged in - that really bugs me! Pin
AnonymousAnNoyedAndRageous25-Jan-03 16:26
sussAnonymousAnNoyedAndRageous25-Jan-03 16:26 
GeneralFileSystemWatcher has platform issues Pin
James Cadd17-Oct-02 7:41
James Cadd17-Oct-02 7:41 
GeneralRe: FileSystemWatcher has platform issues Pin
Jesper17-Oct-02 22:29
Jesper17-Oct-02 22:29 
GeneralRe: FileSystemWatcher has platform issues Pin
James Cadd18-Oct-02 1:46
James Cadd18-Oct-02 1:46 
GeneralRe: FileSystemWatcher has platform issues Pin
Jesper18-Oct-02 1:56
Jesper18-Oct-02 1:56 
GeneralRe: FileSystemWatcher has platform issues Pin
malac113-Jul-10 9:27
malac113-Jul-10 9:27 
GeneralSystem.IO.FileSystemWatcher Pin
Jesper17-Oct-02 2:51
Jesper17-Oct-02 2:51 
GeneralRe: System.IO.FileSystemWatcher Pin
Paul Watson17-Oct-02 3:34
sitebuilderPaul Watson17-Oct-02 3:34 
GeneralRe: System.IO.FileSystemWatcher Pin
Daniel Turini17-Oct-02 3:40
Daniel Turini17-Oct-02 3:40 
GeneralRe: System.IO.FileSystemWatcher Pin
Paul Watson17-Oct-02 3:46
sitebuilderPaul Watson17-Oct-02 3:46 
GeneralRe: System.IO.FileSystemWatcher Pin
Daniel Turini17-Oct-02 3:56
Daniel Turini17-Oct-02 3:56 
GeneralRe: System.IO.FileSystemWatcher Pin
Jesper17-Oct-02 4:05
Jesper17-Oct-02 4:05 
GeneralRe: System.IO.FileSystemWatcher Pin
Paul Watson17-Oct-02 4:28
sitebuilderPaul Watson17-Oct-02 4:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.