How To: Set a custom icon for an Outlook folder in the Navigation Pane
Dear reader, today I want to show you one of the new features Outlook 2010 brought to programmers. Now Outlook allows setting custom icons the Navigation pane folders. The Outlook Object Model provides a special method for this task – SetCustomIcon. So, let’s start our journey into the Outlook programming world ;-)
Setting custom icons in Outlook – a bit of theory
The SetCustomIcon method is available at the MAPIFolder class level. This class also provides the opposite method for getting a custom icon – GetCustomIcon. However, today we will focus our attention on the setting a custom icon. The SetCustomIcon method accepts an instance of the IPictureDisp interface and doesn’t return anything. The VBA help reference (as well as the Outlook Object Model reference) provides a code to convert instances of .Net based classes to the IPictureDisp interface. Also you can find the sample solution (available for download at the end of article) which contains the entire code needed to get the job done.
Allowed types of images are icons and bitmaps. The size can be 32×32, 24×24 and 16×16. An appropriate size will be used depending on the DPI (Dots Per Inch) mode set on the PC. Outlook can scale up icons if a small image was set and Outlook is running in high DPI mode.
Not all Outlook folders accept custom icons
Not all folders allow you to set a custom icon. For example, I have tried to set a custom icon for the Inbox folder and immediately got the “Custom folder icon is not allowed…” exception:
A custom icon cannot be added to the following groups of folders in Outlook:
- Default folders (see the OlDefaultFolders enumeration)
- Special folders (see the OlSpecialFolders enumeration)
- Exchange public folders
- Exchange Root folders
- Hidden folders
As you might have noticed there are no search folders on the list, which means you can set custom icons for Search folders. The same technique is used for solutions module folders.
Only in-process callers are allowed
The Outlook Object Model doesn’t allow setting a custom icon for a folder from another application (using the OLE Automation technology). Only the in-process code can be used for such a task. An exception will be thrown in the SetCustomIcon method in this case.
Set an icon each time Outlook is started
I would like to draw your attention to the fact that Outlook doesn’t store the icon set during a previous session. So, if you programmatically set an icon and then close Outlook, there will be no icon when you open it anew. That is why you need to set an icon on each Outlook startup. For example, I created a folder and set a custom icon for it. Here is how it looks in my Outlook 2010:
Then I close Outlook and when I reopen it anew (without the code which was used for creating a folder and setting an icon), no icon is there:
The sample code in full details
Of course, I have prepared a sample code which shows how to set a custom icon for an Outlook folder. There are two main methods:
- The SetUpFolder method – is used for creating a new folder if no such folder was created earlier. If a folder with the same name already exists we just need to find it. The method accepts an instance of the Outlook Application.
- The SetCustomIcon method – is used for setting a custom icon for an Outlook folder (the name of our custom folder is “Smiling Folder”). It accepts an instance of the MAPIFolder class for which an icon should be set.
Note that if you try to add a folder and such a folder already exists, you will get an exception saying the folder cannot be created:
So, we need to find a folder first and only then, if there is folder with the same name, create it.
Below you can find a snippet of code developed in C# and VB.NET for VSTO and Add-in Express based add-ins. You can also download a complete solution which contains all the code including auxiliary code needed for converting images into the IPictureDisp interface. Note that I have included an image to the resources of each add-in project, so you won’t need to include it in the setup package manually. The image will be embedded into the add-in assembly automatically.
- C# and Add-in Express:
using System.Drawing; // private void SetUpFolder(Outlook._Application OutlookApp) { Outlook.NameSpace ns = null; Outlook.MAPIFolder folderInbox = null; Outlook.MAPIFolder parent = null; Outlook.Folders rootFolders = null; Outlook.MAPIFolder smileFolder = null; try { ns = OutlookApp.GetNamespace("MAPI"); folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox); parent = folderInbox.Parent as Outlook.MAPIFolder; rootFolders = parent.Folders; for (int i = 1; rootFolders.Count >= i; i++) { smileFolder = rootFolders[i]; if (smileFolder.Name == "Smiling Folder") { break; } else { Marshal.ReleaseComObject(smileFolder); smileFolder = null; } } if (smileFolder == null) { smileFolder = rootFolders.Add("Smiling Folder"); } SetCustomIcon(smileFolder); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message, "An exception was thrown in the code..."); } finally { if (smileFolder != null) Marshal.ReleaseComObject(smileFolder); if (rootFolders != null) Marshal.ReleaseComObject(rootFolders); if (folderInbox != null) Marshal.ReleaseComObject(folderInbox); if (ns != null) Marshal.ReleaseComObject(ns); } } private void SetCustomIcon(Outlook.MAPIFolder folder) { Icon icon = null; try { icon = ADX_CSharp.Properties.Resources.SampleIcon1; stdole.StdPicture iconPictureDisp = PictureDispConverter.ToIPictureDisp(icon) as stdole.StdPicture; folder.SetCustomIcon(iconPictureDisp); } finally { icon.Dispose(); } }
- VB.NET and Add-in Express:
Imports System.Drawing ' Private Sub SetUpFolder(OutlookApp As Outlook._Application) Dim ns As Outlook.NameSpace = Nothing Dim folderInbox As Outlook.MAPIFolder = Nothing Dim parent As Outlook.MAPIFolder = Nothing Dim rootFolders As Outlook.Folders = Nothing Dim smileFolder As Outlook.MAPIFolder = Nothing Try ns = OutlookApp.GetNamespace("MAPI") folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) parent = CType(folderInbox.Parent, Outlook.MAPIFolder) rootFolders = parent.Folders For i As Integer = 1 To rootFolders.Count smileFolder = rootFolders(i) If smileFolder.Name = "Smiling Folder" Then Exit For Else Marshal.ReleaseComObject(smileFolder) smileFolder = Nothing End If Next If IsNothing(smileFolder) Then smileFolder = rootFolders.Add("Smiling Folder") End If SetCustomIcon(smileFolder) Catch ex As Exception System.Windows.Forms.MessageBox.Show(ex.Message, _ "An exception was thrown in the code...") Finally If Not IsNothing(smileFolder) Then Marshal.ReleaseComObject(smileFolder) If Not IsNothing(rootFolders) Then Marshal.ReleaseComObject(rootFolders) If Not IsNothing(folderInbox) Then Marshal.ReleaseComObject(folderInbox) If Not IsNothing(ns) Then Marshal.ReleaseComObject(ns) End Try End Sub Private Sub SetCustomIcon(folder As Outlook.MAPIFolder) Dim icon As Icon = Nothing Try icon = ADX_VBNET.My.Resources.SampleIcon1 Dim iconPictureDisp As stdole.StdPicture = _ CType(PictureDispConverter.ToIPictureDisp(icon), stdole.StdPicture) folder.SetCustomIcon(iconPictureDisp) Finally icon.Dispose() End Try End Sub
- C# and VSTO:
using System.Runtime.InteropServices; using System.Drawing; // private void SetUpFolder(Outlook.Application Application) { Outlook.NameSpace ns = null; Outlook.MAPIFolder folderInbox = null; Outlook.MAPIFolder parent = null; Outlook.Folders rootFolders = null; Outlook.MAPIFolder smileFolder = null; try { ns = Application.GetNamespace("MAPI"); folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox); parent = folderInbox.Parent as Outlook.MAPIFolder; rootFolders = parent.Folders; for (int i = 1; rootFolders.Count >= i; i++) { smileFolder = rootFolders[i]; if (smileFolder.Name == "Smiling Folder") { break; } else { Marshal.ReleaseComObject(smileFolder); smileFolder = null; } } if (smileFolder == null) { smileFolder = rootFolders.Add("Smiling Folder"); } SetCustomIcon(smileFolder); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message, "An exception was thrown in the code..."); } finally { if (smileFolder != null) Marshal.ReleaseComObject(smileFolder); if (rootFolders != null) Marshal.ReleaseComObject(rootFolders); if (folderInbox != null) Marshal.ReleaseComObject(folderInbox); if (ns != null) Marshal.ReleaseComObject(ns); } } private void SetCustomIcon(Outlook.MAPIFolder folder) { Icon icon = null; try { icon = VSTO_CSharp.Properties.Resources.SampleIcon1; stdole.StdPicture iconPictureDisp = PictureDispConverter.ToIPictureDisp(icon) as stdole.StdPicture; folder.SetCustomIcon(iconPictureDisp); } finally { icon.Dispose(); } }
- VB.NET and VSTO:
Imports System.Drawing Imports System.Runtime.InteropServices ' Private Sub SetUpFolder(Application As Outlook.Application) Dim ns As Outlook.NameSpace = Nothing Dim folderInbox As Outlook.MAPIFolder = Nothing Dim parent As Outlook.MAPIFolder = Nothing Dim rootFolders As Outlook.Folders = Nothing Dim smileFolder As Outlook.MAPIFolder = Nothing Try ns = Application.GetNamespace("MAPI") folderInbox = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) parent = CType(folderInbox.Parent, Outlook.MAPIFolder) rootFolders = parent.Folders For i As Integer = 1 To rootFolders.Count smileFolder = rootFolders(i) If smileFolder.Name = "Smiling Folder" Then Exit For Else Marshal.ReleaseComObject(smileFolder) smileFolder = Nothing End If Next If IsNothing(smileFolder) Then smileFolder = rootFolders.Add("Smiling Folder") End If SetCustomIcon(smileFolder) Catch ex As Exception System.Windows.Forms.MessageBox.Show(ex.Message, "An exception was thrown in the code...") Finally If Not IsNothing(smileFolder) Then Marshal.ReleaseComObject(smileFolder) If Not IsNothing(rootFolders) Then Marshal.ReleaseComObject(rootFolders) If Not IsNothing(folderInbox) Then Marshal.ReleaseComObject(folderInbox) If Not IsNothing(ns) Then Marshal.ReleaseComObject(ns) End Try End Sub Private Sub SetCustomIcon(folder As Outlook.MAPIFolder) Dim icon As Icon = Nothing Try icon = My.Resources.SampleIcon1 Dim iconPictureDisp As stdole.StdPicture = CType( _ PictureDispConverter.ToIPictureDisp(icon), stdole.StdPicture) folder.SetCustomIcon(iconPictureDisp) Finally icon.Dispose() End Try End Sub
See you on our forums and in the e-mail support!
Available downloads:
These sample COM Add-ins were developed using Add-in Express for Office and .net:
C# Outlook add-ins: VSTO and Add-in Express based
VB.NET Outlook add-ins: VSTO and Add-in Express based
14 Comments
Hi,
I want to add image of email,like if email is in Inbox it has image,and if image is sent mail then it have different image. Like that i put a custom button on ribbon of outlook on clicking of the button i need to set a image to that particular email.
Kindly help me in this.
Thanks in advance.
Hi Naresh,
There is no trivial way to change an email item’s icon on the fly. You can try to create and publish an Outlook custom form containing custom icons and then change the email’s MessageClass property so that it uses the icons from that published form.
Hi, i’m having some trouble with attaching icons on outlook startup.
I’m doing the following:
1.- Load my custom .pst store into outlook
2.- Get a Store object reference to that loaded .pst
3.- Save the store.GetRootFolder() reference in a class private attibute.
I call this methods with a reference to the store root folder:
public static void AttachAllIconsRecursively(Folder root)
{
//attach icon
FoldersUtil.AttachFolderIcon(root, SystemIcons.Warning);
//Recursive Call to subfolders
Folders children = root.Folders;
for (int i = 1; i <= children.Count; i++)
{
Folder item = children[i] as Folder;
AttachAllIconsRecursively(item);
}
if (children != null)
{
Marshal.ReleaseComObject(children);
children = null;
}
}
public static void AttachFolderIcon(Folder folder, Icon icon)
{
try
{
stdole.StdPicture iconPictureDisp = PictureDispConverter.ToIPictureDisp(icon) as stdole.StdPicture;
folder.SetCustomIcon(iconPictureDisp);
}
catch (System.Exception e)
{
//…manage error
}
}
It worked just fine until now.
I'm getting this exception
"El icono de carpeta personalizada no se permite para una carpeta que no se muestra en la interfaz de usuario de Outlook"
in english it would be something like "The custom folder icon it's not allowed for a non-visible folder in the outlook user interface", i'm guessing outlook does not load it's UI before this method call, and the folder's tree in the pst it's not being shown at that time.
Can you please tell me if there is any solution to this ?
Thanks in advance.
Silvio.
Hi Silvio,
First of all, thank you for reading my posts on our technical blog!
Please take a look at the SetCustomIcon description in MSDN. It states:
You can only call SetCustomIcon from code that runs in-process as Outlook. An IPictureDisp object cannot be marshaled across process boundaries. If you attempt to call SetCustomIcon from out-of-process code, an exception will occur.
Is this the case? Do you develop a COM add-in or VBA macro?
Hi Eugene,
Thanks to you for writing them! Them are really useful beccause microsoft does not provide real examples like this.
I believe it runs in the same thread as the outlook plugin, i’m calling it from thisaddin_startup method and also from a RunWorkerCompleted event of a backgroundworker, i believe this event is handled by the .RunWorkerAsync() caller thread.
I’m developing a plugin with ms visual studio 2010 and vsto 4.0, a outlook plugin project, i’m working with microsoft.interop… so i guess it’s a com add-in wrapped into C#. Is there any post about the com object referencing and how to use marshal.releasecomobject right?
Thanks for your answer!
Silvio
Hi Silvio,
Please take a look at the When to release COM objects in Office add-ins developed in .NET on our technical blog for more information about this.
If you develop a COM add-in (VSTO based one), why isn’t there any Outlook window visible at the moment you’re trying to set icons? How do you run Outlook?
Hi,
Thanks, i will read it.
I click start on VS 2010, outlook is loaded with a fresh build from VS. There is a outlook window, but it’s the splash with the text “Loading plugins…”.
When i said that it was no window, i meant that the main outlook window (The Explorer) with all the folder tree’s is not loaded yet.
Any mistakes in this way of running the plugin?
Thanks for the answer!
Silvio
Hi Silvio,
Thank you for the explanation.
What event handler do you use for setting icons in Outlook?
It looks like you need to use the Startup event of the Application class. Am I right?
Yes, you are rigth, i’m using the startup event to call the method posted above.
Does that method fires before outlook explorer UI is loaded?
Greetings.
Silvio
Hi Silvio,
No, it doesn’t.
Sorry, but I can’t diagnose the issue off the top of my head. I would recommend you contact our support team by email or forum. Please understand that the cause of the issue could be in your code.
Also please make sure that you have installed all the latest service packs for Outlook, OS and .Net.
Thanks,
a last question, the problem seems to be that when i load the store with
Session.AddStoreEx(path, Microsoft.Office.Interop.Outlook.OlStoreType.olStoreUnicode);
the store shows in the UI, but closed ( i mean, the tree is all collapsed ) . any way to programatically expand the tree?.
I will contact the support team for further questions, can you please email me the address where i have to send the email’s.
Thanks again.
Silvio
Hello Silvio,
Please use the CurrentFolder property of the Explorer class to select the folder programmatically.
Hi Eugene,
i have download your example (ADX_VB.NET) to run im my pc but appear the error below :
System.AccessViolationException was unhandled by user code
HResult=-2147467261
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=Microsoft.Office.Interop.Outlook
StackTrace:
at Microsoft.Office.Interop.Outlook.MAPIFolder.SetCustomIcon(StdPicture Picture)
at ADX_VBNET.AddinModule.SetCustomIcon(MAPIFolder folder) in H:\VB PROJECT\KODE VB.NET\adx-net-sample-projects\adx_outlook_set-custom-icon_vb\ADX_VB.NET\AddinModule.vb:line 175
at ADX_VBNET.AddinModule.SetUpFolder(_Application OutlookApp) in H:\VB PROJECT\KODE VB.NET\adx-net-sample-projects\adx_outlook_set-custom-icon_vb\ADX_VB.NET\AddinModule.vb:line 159
at ADX_VBNET.AddinModule.AdxCommandBarButton1_Click(Object sender) in H:\VB PROJECT\KODE VB.NET\adx-net-sample-projects\adx_outlook_set-custom-icon_vb\ADX_VB.NET\AddinModule.vb:line 186
at AddinExpress.MSO.ADXCommandBarButton.OnButtonClick(ICommandBarButton button, Boolean& handled)
InnerException:
I use Visual Studio 2012 with ms office 2007.
Can you help me please?
Best regards
Dritan
Hi Dritan,
Outlook 2007 does not support custom icons. This feature was added to the Outlook 2010 Object Model.