ShellBoost

Information Bar

The Windows Explorer can display notification banners under certain conditions. They are displayed in the upper area of the shell view, like this for example in the case of a failing network:

Information Bar - Picture 75

As you can see in this screenshot, a banner can also contain a menu the end-user can open and use.

Starting with version 1.2.2.0, ShellBoost offers exclusive support for this feature.

To display such a banner, you must override methods of the ShellFolder class. This is demonstrated in the Registry Folder sample. So, if you just want to display a message, override the GetInformationBar method, like this, for example:

// this is called by ShellBoost
protected override InformationBar GetInformationBar()
{
    // in this sample, we test the user has enough rights
    if (Hive != RegistryHive.LocalMachine || DiagnosticsInformation.GetTokenElevationType() == TokenElevationType.Full)
        return null;
 
    var bar = new InformationBar();
 
    // the guid identifies the banner, it must not be the empty guid.
    // Use the same guid for one given banner.
    bar.Guid = _adminMessageGuid;
 
    // set the message that will be displayed to the end-user.
    // it must not be null nor empty not whitespaces only
    bar.Message = "You must be running with full privileges to change anything here";
    return bar;
}

This is what’s displayed when the Shell Folder is open:

Information Bar - Picture 74

In this screenshot, we also added a menu (this is optional) like this:

protected override void CreateInformationBarMenu(InformationBar bar, ShellMenu appendMenu)
{
    var item = new ShellMenuItem(appendMenu, "What is my UAC level?");
    appendMenu.Items.Add(item);
}

And the menu handling is done like this:

protected override async void HandleInformationBarMenu(InformationBar bar, IntPtr hwndOwner, int id)
{
    if (id == 1) // first item has id 1, etc.
    {
        await WindowsUtilities.DoModelessAsync(() =>
        {
            System.Windows.Forms.MessageBox.Show(new Win32Window(hwndOwner), "UAC level is " + DiagnosticsInformation.GetTokenElevationType(), "Registry Folder");
        });
        return;
    }
}

Each menu item added to the banner menu is automatically given an id that correspond to its insertion index.

The result is shown here:

Information Bar - Picture 73

Note the MessageBox here is a modeless Window for your app (the ShellBoost Shell Folder server) but a modal one for the Explorer Window.

Starting with ShellBoost version 1.8.1.0, you can also force the display of the Information Bar at any time, using the Roundtrip Shell programming. It’s based on the native IOleCommandTarget interface that the native proxy has support for.

Once you have a Shell View on a ShellBoost Namespace Extension, you can call Exec with special input values, as demonstrated here:

public override IEnumerable<ShellItem> EnumItems(SHCONTF options)
{
       foreach (var item in _items)
       {
             if (SomeCondition(...))
             {
                    // clone the context as we'll change threads
                    var ctx = ShellContext.Current.Clone();
                    Task.Run(async () =>
                    {
                           // this runs in another thread
                           var view = await ctx.GetShellBoostViewAsync(this);
                           if (view != null)
                           {
                                  // get the ShellFolder instance that's running in-process with the host (Explorer, etc.)
                                  // note this is different from using view.Folder
                                  var sf = view.CoreShellFolder;
                                  if (sf != null)
                                  {
                                        // build a dictionary
                                        var dic = new Dictionary<string, object>();
                                        
                                        // ask the namespace extension to show the infobar
                                        dic["Command"] = "InfoBarInform";
                                        
                                        // this is the bar identifier, use your own identifier
                                        var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
                                        dic["BarId"] = guid;
                                        
                                        // this is the bar message
                                        dic["BarMessage"] = "There was a problem during items gathering!";
 
                                        // call the namespace extension
                                        sf.ExecCommand(IntPtr.Zero, Guid.Empty, 0, dic);
                                  }
                           }
                    });
             }
             
             // etc.
             yield return item.Value;
       }
}

The dictionary must contain the “Command” key that only understand the “InfoBarInform" and "InfoBarCancel" commands. Both commands need the “BarId” key that must contain a valid Guid that represents the bar identifier. The “InfoBarInform" command also needs the “BarMessage” key that must contain the text that will be displayed by the information bar.

These bars can have context menus, just like other information bars. For example, the code below adds a menu item to the bar shown in the code above:

protected override void CreateInformationBarMenu(InformationBar bar, ShellMenu appendMenu)
{
       // use the same id than before to be able to filter on the type of bar
       var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
       if (bar.Guid == guid)
       {
             var item = new ShellMenuItem(appendMenu, "Click here to report that...");
             appendMenu.Items.Add(item);
       }
}

If you need to cancel an information bar that has been shown, you can use a code like this:

var dic = new Dictionary<string, object>();
 
// ask the namespace extension to cancel the infobar
dic["Command"] = "InfoBarCancel";
 
// this is the bar identifier, use the same identifier as the one used to show
// of course, there’s no need to pass a message this time
var guid = new Guid("b5c4a837-5540-4ba0-83b5-f76a4601ad92");
dic["BarId"] = guid;
 
// call the namespace extension
sf.ExecCommand(IntPtr.Zero, Guid.Empty, 0, dic);