Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Getting Started with VB.NET's Microsoft Outlook Standard Plugin Creator

0.00/5 (No votes)
20 Feb 2009 1  
Getting started with VB.NET's Microsoft Outlook standard plugin creator
Click to enlarge image

Introduction

This small article will send you on your way developing Microsoft Visual Basic .NET plugins for Microsoft Outlook 2003.

Background

Microsoft Outlook is, as we all know, an (or the) industry standard email client. Extending it by using VB.NET can be both profitable and actually fun (the final result of course, not the actual coding).

Well, Let's Get Started

First, create a clean "Outlook 2003 add-in" with the appropriate wizard. Call it anything you like.

Then, add this code to declare the objects that will be used. 

Private myCommandBarControll As CommandBar
Private btnGetAppointments As CommandBarControl
Private mnuPopup As CommandBarControl  

As you'll notice, the objects are declared without withevents and new, but we don't need those. Later, we will use DirectCast and AddHandler for making things work.

Now, at the ThisAddin_Startup event, add this code:

Private Sub ThisAddIn_Startup(ByVal sender As Object, _
	ByVal e As System.EventArgs) Handles Me.Startup

Dim btnclickhandler As CommandBarButton
Dim mnuPopuphandler As CommandBarPopup
'[.....]   

Again, without new.

To make things work, we have to declare a commandbar. So add this code right after the last piece:

Dim oApp As Outlook.Application = New Outlook.Application()
myCommandBarControll = oApp.ActiveExplorer.CommandBars.Add_
	("YourCommandbar", MsoBarPosition.msoBarTop, False, True)
myCommandBarControll.Visible = True

The only thing new here, is the Outlook.Application() object. We only inherit from now on. What happens here is that Outlook creates the commandbar YourCommandbar and makes it visible. Be sure though that you explicitly make the property Visible True.

It has no functionality nor buttons, so let's change that. Use this piece of code, still within ThisAddIn_Startup:

mnuPopup = myCommandBarControll.Controls.Add(MsoControlType.msoControlPopup)
mnuPopup.Caption = "Your pulldown menu"
mnuPopup.Visible = True

mnuPopuphandler = DirectCast(mnuPopup, CommandBarPopup) 

That will do the job creating a standard pulldown menu within your commandbar. Note the directcast and we aren't using any new statements.

This pulldown menu, however, needs a purpose. Just like everything else in life. So, let's add a button to the menu.

btnGetAppointments = mnuPopuphandler.Controls.Add(MsoControlType.msoControlButton)
btnGetAppointments.Caption = "Your caption of this button"
btnGetAppointments.Visible = True

btnclickhandler = DirectCast(btnGetAppointments, CommandBarButton)
btnclickhandler.Style = MsoButtonStyle.msoButtonIconAndCaption

btnclickhandler.TooltipText = "Your tooltip text"
AddHandler btnclickhandler.Click, AddressOf OnApptClick

Let's analyze the code. It has:

  • Visible explicitly made true.
  • A directcast to make the control object behave like a button.
  • Tooltip text.
  • A style declaration set to MsoButtonStyle.msoButtonIconAndCaption to make images work.
  • An Addhandler for making the button actually do anything.
  • Still no new's.

Let's add the function that makes the plugin actually do anything on the user's command. We will finally leave ThisAddIn_Startup and create a new sub called OnApptClick.

Private Sub OnApptClick()
        MsgBox("Hello Outlook world!")
End Sub  

Now, run your plugin. You are now fully qualified to make all kind of wierd plugins. Be creative!

If you want to use images in your buttons, keep on reading.

IPictureDisp

Outlook uses IPictureDisp for images. But that's no real problem if you add a module containing the following code:

'
' THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
' KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
' IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
'
Imports System
Imports System.Drawing
Imports System.Collections.Generic
Imports System.Runtime.InteropServices

Public Module PictureDispConverter

    'IPictureDisp guid
    Public iPictureDispGuid As Guid = GetType(stdole.IPictureDisp).GUID

    'Converts an Icon into a IPictureDisp
    Public Function ToIPictureDisp(ByVal ico As Icon) As stdole.IPictureDisp

        Dim pictIcon As New PICTDESC.Icon(ico)
        Return PictureDispConverter.OleCreatePictureIndirect_
				(pictIcon, iPictureDispGuid, True)
    End Function

    'Converts an image into a IPictureDisp
    Public Function ToIPictureDisp(ByVal picture As Image) As stdole.IPictureDisp

        Dim bm As Bitmap
        If TypeOf picture Is Bitmap Then
            bm = picture
        Else
            bm = New Bitmap(picture)
        End If
        Dim pictBit As New PICTDESC.Bitmap(bm)
        Return PictureDispConverter.OleCreatePictureIndirect_
				(pictBit, iPictureDispGuid, True)
    End Function

    <DllImport("OleAut32.dll", EntryPoint:="OleCreatePictureIndirect", _
				ExactSpelling:=True, PreserveSig:=False)> _
    Private Function OleCreatePictureIndirect(<MarshalAs(UnmanagedType.AsAny)> _
	ByVal picdesc As Object, ByRef iid As Guid, ByVal fOwn As Boolean) _
	As stdole.IPictureDisp
    End Function

    Private ReadOnly hCollector As New HandleCollector("Icon handles", 1000)

    'PICTDESC is a union in native, so we'll just
    'define different ones for the different types
    'the "unused" fields are there to make it the right
    'size, since the struct in native is as big as the biggest
    'union.
    Private Class PICTDESC

        'Picture Types
        Public Const PICTYPE_UNINITIALIZED As Short = -1
        Public Const PICTYPE_NONE As Short = 0
        Public Const PICTYPE_BITMAP As Short = 1
        Public Const PICTYPE_METAFILE As Short = 2
        Public Const PICTYPE_ICON As Short = 3
        Public Const PICTYPE_ENHMETAFILE As Short = 4

        <StructLayout(LayoutKind.Sequential)> _
        Public Class Icon

            Friend cbSizeOfStruct As Integer = Marshal.SizeOf(GetType(PICTDESC.Icon))
            Friend picType As Integer = PICTDESC.PICTYPE_ICON
            Friend hicon As IntPtr = IntPtr.Zero
            Friend unused1 As Integer = 0
            Friend unused2 As Integer = 0

            Friend Sub New(ByVal icon As System.Drawing.Icon)
                Me.hicon = icon.ToBitmap().GetHicon()
            End Sub

        End Class

        <StructLayout(LayoutKind.Sequential)> _
        Public Class Bitmap

            Friend cbSizeOfStruct As Integer = Marshal.SizeOf(GetType(PICTDESC.Bitmap))
            Friend picType As Integer = PICTDESC.PICTYPE_BITMAP
            Friend hbitmap As IntPtr = IntPtr.Zero
            Friend hpal As IntPtr = IntPtr.Zero
            Friend unused As Integer = 0

            Friend Sub New(ByVal bitmap As System.Drawing.Bitmap)
                Me.hbitmap = bitmap.GetHbitmap()
            End Sub
        End Class

    End Class

End Module

Many thanks to http://blogs.msdn.com/rgregg/archive/2006/11/27/converting-bitmaps-to-ipicturedisp.aspx.

Now, make the conversion of the image type actually work. The bold line is added:

'[....]
        btnclickhandler = DirectCast(btnGetAppointments, CommandBarButton)
        btnclickhandler.Style = MsoButtonStyle.msoButtonIconAndCaption

        btnclickhandler.Picture = ToIPictureDisp(My.Resources.comments_add)

        btnclickhandler.TooltipText = "Your tooltip text"
        AddHandler btnclickhandler.Click, AddressOf OnApptClick
'[....] 

You can of course replace My.Resources.comments_add with any icon or bitmap you have.

Points of Interest

The annoying thing about developing for Microsoft Outlook is that you will have to use COM (there is no way to escape it) and debugging can be a pain in the neck. You will have to know what you're doing when you're coding.

History

  • 18th February, 2009: Initial post
  • 20th February, 2009: Added screenshot and source files

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here