Click here to Skip to main content
Click here to Skip to main content

Shell Notifications in C#

By , 16 Oct 2002
 
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.

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);
    
    // 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.

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 posting

License

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

About the Author

Thomas Caudal
Web Developer
France France
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralCan't download although i logged in - that really bugs me!sussAnonymousAnNoyedAndRageous25 Jan '03 - 16:26 
Can't download although i logged in - that really bugs me!
 
I even changed the browser (from Mozilla to IE), double and double checked my password and my cooky settings
 
This really is annoying. I know i can write a mail to the admin, but this will not show others, that it may be not their fault, if the can't log in.
 
good luck
 
although i am a little at rage now - this is a really code project and i am glad about all the articles and the people who keep this site running.
 
cya Confused | :confused: Mad | :mad: Frown | :( img src="/script/Forums/Images/smiley_mad.gif" align="top" alt="Mad | :mad:" /> Confused | :confused:
 
and by the way As i was trying to send this message with my user account i received the message: this account is already taken <-- I KNOW THIS. IT WAS TAKEN BY ME. SonOfA
GeneralFileSystemWatcher has platform issuesmemberJames Cadd17 Oct '02 - 7:41 
this code is the only solution for win98 (FileSystemWatcher is 2K/NT/XP only), so its actually quite useful. many thanks to the author for the code and for providing a link to a valuable resource.
GeneralRe: FileSystemWatcher has platform issuesmemberJesper17 Oct '02 - 22:29 
Does anyone know why FileSystemWatcher doesn't work on win9x? Is it because of dependencies on other API's than the one in the article?
 
It seems a bit weird.
GeneralRe: FileSystemWatcher has platform issuesmemberJames Cadd18 Oct '02 - 1:46 
check out cutting edge in msdn mag, october 2002. FileSystemWatcher is a wrapper for the ReadDirectoryChangesW API which is only available in NT4 and later.
GeneralRe: FileSystemWatcher has platform issuesmemberJesper18 Oct '02 - 1:56 
Thanks!Big Grin | :-D
GeneralRe: FileSystemWatcher has platform issuesmembermalac113 Jul '10 - 9:27 
FileSystemWatcher doesn't work on Linux servers... this solution works.
GeneralRe: System.IO.FileSystemWatchermemberHeath Stewart17 Oct '02 - 8:49 
If you want to know how MS's code works, use ildasm.exe to read the IL instructions. It's their own version of assembler, basically, and isn't too hard to follow once you learn it.
 
"Well, I wouldn't say I've been missing it, Bob." - Peter Gibbons
GeneralSystem.IO.FileSystemWatchermemberJesper17 Oct '02 - 2:51 
There is actually a class in the framework for this: System.IO.FileSystemWatcher
GeneralRe: System.IO.FileSystemWatchereditorPaul Watson17 Oct '02 - 3:34 
Isn't that great though? The author has shown us how it is done and we can now choose, choice is great.
 
Paul Watson
Bluegrass
Cape Town, South Africa

GeneralRe: System.IO.FileSystemWatchermemberDaniel Turini17 Oct '02 - 3:40 
Reinventing the wheel is only good if you create a better wheel.
 
My latest articles:
Desktop Bob - Instant CP notifications
XOR tricks for RAID data protection
GeneralRe: System.IO.FileSystemWatchereditorPaul Watson17 Oct '02 - 3:46 
Daniel Turini wrote:
Reinventing the wheel is only good if you create a better wheel.
 
Not true. What if you have no clue how that wheel was created and you want to know? MS are not too chummy with their .NET Framework class code yet.
 
So an article like this shows how it can be done. I had no idea about these shell notification thingamabobs but now I do and I also know how to use them etc.
 

Also maybe this articles code is better for a different purpose. Maybe the FileWatcher class is so feture packed it can be overkill for just a simple task, in that case this article code would be helpful.
 

It is a different wheel with it's innards showing Big Grin | :-D
 
Paul Watson
Bluegrass
Cape Town, South Africa

GeneralRe: System.IO.FileSystemWatchermemberDaniel Turini17 Oct '02 - 3:56 
Well, what I really don't agree is rating this article 1! Mad | :mad:
Some lamer did it, and it's a well-written article.
 
My latest articles:
Desktop Bob - Instant CP notifications
XOR tricks for RAID data protection
GeneralRe: System.IO.FileSystemWatchermemberJesper17 Oct '02 - 4:05 
I think the article does a good job of explaining the inner workings of these notification, and I think it is useful if you are programming i C++.
 
But, I also think that part of programming for .NET is learning about the framework. It is after all meant to make life simpler, not more complicated Smile | :)
 
If you are of the oppinion that we shouldn't use the framework, since MS might have made mistakes, then obviously you might as well do your programming without it (and C# et al.)
 
NB: I'm just starting to use .NET myself, and I know the feeling of doing something cool Cool | :cool: , just to find that MS already did it for you. I just wanted to enlighten my fellow rookies.
GeneralRe: System.IO.FileSystemWatchereditorPaul Watson17 Oct '02 - 4:28 
Jesper wrote:
But, I also think that part of programming for .NET is learning about the framework. It is after all meant to make life simpler, not more complicated
 
If you are of the oppinion that we shouldn't use the framework, since MS might have made mistakes, then obviously you might as well do your programming without it (and C# et al.)

 
Oh no not at all, the framework classes are pretty much up there as the main reason why I am so into .NET. They are brilliant and the sheer ease of "extending" it is also a big plus for me.
 

But still it is nice to figure out how to do things, like shell notifications, without having to rely on some encapsulated FileWatcher class. I just like to know Smile | :)
 

Jesper wrote:
and I know the feeling of doing something cool , just to find that MS already did it for you
 
Sun did C# before MS did, it was called Java but that does not make C# any less cool or worthy. We need options and alternatives, not because I think the .NET Framework classes are crap (they aren't IMO) but just for choice sake.
 
And frankly you learnt a lot more re-inventing the wheel than just using a pre-built wheel.
 
Paul Watson
Bluegrass
Cape Town, South Africa

GeneralRe: System.IO.FileSystemWatchersussAnonymous18 Oct '02 - 7:21 
I agree...
 
I just wish "Select Case" statements were much easier to follow in IL when the Cases aren't numbered 0, 1, ...
 
It takes me a while to figure out the program flow in the "WndProc" functions in some of the classes in Systems.Windows.Forms.
GeneralRe: System.IO.FileSystemWatchersussAnonymous19 Oct '02 - 4:58 
Why don´t you use Anakrino, or look into Rotor then?
 
// F
GeneralRe: System.IO.FileSystemWatchermembersweavo28 Nov '06 - 8:54 
Can System.IO.FileSystemWatcher tell me when a USB stick is inserted/removed?

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 17 Oct 2002
Article Copyright 2002 by Thomas Caudal
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid