An Outlook Geek looks at 40

Meanderings and brain dumps from the world of an Outlook tech support perspective

An Outlook Geek looks at 40

  • Using the Outlook Object Model to add helper features - The LastName Problem

    Back in the days before email viruses changed the game, the Microsoft Office applications were amazing extensibility platforms. All the way back to wordbasic and the excel macro language, and then certainly with VBA in the core office apps.  Now finally after years of maturity with the security features and A/V precauations in place, I thought it would be fun to show an example of how you can still solve little problems and add helper features with this powerful area.

    The Problem

    Finding an email address by last name is a bit of a challenge in Outlook.  Suppose your last name is Jones and you have 5 brothers stored in various address books in your Outlook configuration.  You also are using Exchange so you have a Global Address List (GAL) that has 25 different "Jones" in the list.

    You create a new email and type in "Jone" hoping the Outlook will autocomplete from the last time you mailed one of your brothers.  No such luck as the autocomplete feature does not trigger on last name.

    Next you tab away or hit the check names button, and you get the ANR  (Ambiguous Name Resolution) dialog.  Unfortanately, since there are many Jones entries in the GAL, and the GAL is the first address list in order of searching, you only see the names from that last.  None of your brothers are visible.

    At this point you can try to use the Find feature, or manually change the address list context, but you get the point.  It is too hard to find the recipient by last name.

    The Solution

    Outlook provides enough extensibility through its object model to get to all the entries in the address list.  We just need to cook up some UI to accept a search term, present the results, and allow you to insert the found address into a message.

    I have attached two example solutions to this post.  One is a VBA sample that you would import into Outlook itself and use.  This would allow you to provide a button on the inspector ribbon to launch the macro, and make the extended address lookup seem more "integrated" into outlook.  I also included a standalone VB.NET project that shows a sample of how to have a separate EXE "always running" that you would use to do the search and mail creation.

    Both the samples, do the bare minimum to show a proof of concept of the ideas here, which are to

    1. Use the object model to access the address lists that are available.
    2. Loop through "more than one" of them trying to find the search term.

    The "search" here is a basic string comparision against the Display Name.  That happens to work for a last name search becuase the last name is typically part of the display name.  For a real solution, the search code would be smarter and allow for more agresssive or different matching.

    Also note, that you have to configure outlook to allow use of the object model

    Sample Screens

    VBA Sample:

    VB.Net Sample:

    Basic Code Review

    The code in these samples essentially does this:

    • Loop through each address list (address book) that is configured in Outlook.
    • Loop through each entry (one address) within the address list
    • Check to see if the search term matches, and if so, add it to the list.

    For Each onelist In mysession.AddressLists

    ' Only process entries from Contact Folder based address books (and the GAL if really want to)
    ' Change abfake to abGAL to include GAL in the list but that would be really slow

    If onelist.AddressListType = abfake Or onelist.AddressListType = abOutlook Then

        '   Give a little status on the progress
        lblstatus.Caption = "Searching " + onelist.Name
        DoEvents
       
        ' Setup vars for item progress
        itotal = onelist.AddressEntries.Count
        i = 1
       
        ' Loop through every entry in the address list
        For Each oneentry In onelist.AddressEntries
       
            lblstatus.Caption = "Checking entry " + Str(i) + " of " + Str(itotal) + " from " + onelist.Name
            DoEvents
           
            ' This is the current sum total of the "find".  It just matches the passed in string to the
            ' name property from the address entry.  This could be made as complicated as you want by adding
            ' logic to test agaist other fields, etc
           
            r = InStr(1, oneentry.Name, strstarttext, vbTextCompare)
           
            ' r not equal to 0 means you found a match, so add it to the list
           
            If r <> 0 Then
                With Me.listresults
                    .ColumnCount = 3
                    .ColumnWidths = "150;150;150"
                    .AddItem
                    .List(listresults.ListCount - 1, 0) = oneentry.Name
                    .List(listresults.ListCount - 1, 1) = oneentry.Address
                    .List(listresults.ListCount - 1, 2) = onelist.Name
                End With
            End If
           
            i = i + 1
        Next
       
    End If
       
    Next

     

     

  • Internet Headers and Outlook

    In the world of SMTP mail there is the notion of "Internet Headers".  This data which is considered a separate part of the email message contains information on "how the mail got here".  Here is a mock up of a sample "Internet Header":

    Received: from someserver.somedomain.com (999.99.99.99) by
     myserver.mail.com (999.99.99.99) with Someone's SMTP Server (TLS) id
     1.2.3.4; Tue, 30 Oct 2012 10:29:16 +0000
    Received: from pereviousserver.somedomain.com (999.99.99.99) by
     anotherserver.mail.com (999.99.99.99) with DifferentSMTP Server
     id 5.6.7.8; Tue, 30 Oct 2012 10:28:49 +0000
    Received: from moreprevious.somedomain.com (999.99.99.99) by
     yetanother.mail.com (999.99.99.99) with Thirdparty SMTP
     Server id 9.10.11.12 via my transport; Tue, 30 Oct 2012 10:28:48 +0000
    Received: from originalserver.somedomain.com ([999.99.99.99)    by
     senderserver.mai.com (-); Tue, 30 Oct 2012 03:16:17 -0700

    X-MailingID: 9999999::9999999::999999::299999::999999::999999

    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: inline
    Content-Type: text/html; charset="UTF-8"
    Reply-To: <replytome@exampledomain.com>
    MIME-Version: 1.0
    Message-ID: <123456789@exampledomain.com>
    Subject: The subject of the message
    Date: Tue, 30 Oct 2012 03:18:51 -0700
    To: <recipient@exampledomain.com>
    From: Sender@exampledomain.com
    Return-Path: bounce@exampledomain.com

     

    "Internet Headers" are a specific part of SMTP messages. On the other hand, Outlook is a MAPI client, and does not store messages in their native SMTP format.  Any message that is read in Outlook has been converted to MAPI, and the data is stored in a set of MAPI properties.  In almost all current mail server scenarios, if internet headers are available at the time that the message is converted to MAPI, they are converted and stored in a special MAPI property named PR_TRANSPORT_MESSAGE_HEADERS.

    In the outlook UI, you can view the headers using the options dialog for an opened message:

     

    Unfortunately, viewing the internet headers is the extent of the User Interface feature set.  There is no built in way to view the headers in the same pane as the message, and there is no feature set to print the headers along with the message.

    The outlook object model provides a feature called the "propertyaccessor" that allows for an extensible method to retreive MAPI properties.  This feature can be used therefore to retrieve the internet headers and then present them in a way that adds additional features to the UI.

    To demonstrate this concept, consider a scenario where we want this feature set:

    1. The ability to print the internet headers and the message they came from in a "set" together.

    2. A button on the ribbon of the original message to initiate this task.

     

    To achieve this goal, we can take these steps:

    1. Create an outlook macro that retrieves the headers, creates a new item to display and print them, and then prints the original item as well.

    2. Create a toolbar button to start the macro

    3. Ensure that macros are allowed to execute in Outlook.

     

    Step 1: Create the macro that does the work

    1. Starting from Outlook, press Alt-F11 to bring up the VBA editing window.

    2. In the project window, expand the Project to 'Microsoft Outlook Objects', then double-click the 'ThisOutlookSession" to have a place to add the macro

    3. Add the macro code to this area

    Sub BetterHeaders()
    Dim oneitem As Object           ' This is the currently selected outlook item
    Dim newitem As Object           ' This will be the new postitem we create to hold the headers
    Dim onepa As PropertyAccessor   ' This is part of outlook macros that lets you retrieve mapi props
    Dim onefield As String          ' This holds the headers that we get back from MAPI

    ' Initialize to blank
    onefield = ""

    On Error GoTo reportit:

    Set oneitem = Application.ActiveInspector.CurrentItem       'Setup the currently opened item as our base item
    Set newitem = Application.CreateItem(olPostItem)            'Create the new post item

    ' Set the subject for the new post item
    newitem.Subject = "Message and Headers for: " & oneitem.Subject

    ' Add the original item to the new post item as an attachment
    Set oneattach = newitem.Attachments.Add(oneitem, Outlook.OlAttachmentType.olEmbeddeditem)

    ' Retrieve the message headers and store them in the onefield string variable
    ' http://schemas.microsoft.com/mapi/proptag/0x007D001E is the code for PR_MESSAGE_TRANSPORT_HEADERS

    Set onepa = oneitem.PropertyAccessor
    onefield = onepa.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")

    ' If we got headers, then build up the data for the body of the new post item
    If onefield <> "" Then
        hdata = "Message Headers For:" & vbCrLf
        hdata = hdata & "Subject: " & oneitem.Subject & vbCrLf
        hdata = hdata & "Received: " & oneitem.ReceivedTime & vbCrLf
        hdata = hdata & "========================" & vbCrLf
        hdata = hdata & vbCrLf
       
        newitem.Body = hdata & vbCrLf & onefield            ' Set the body of the new postitem to this data
        newitem.Display                                     ' Display the new post on the screen
           
    Else
    ' There were no message headers for this item, so report that fact
        MsgBox "There are no transport headers available for this item.", vbInformation, "Transport Headers"
    End If

    ' Tell outlook to print the new post item and the original item. By specifically printing both items from the macro
    ' we dont have to worry about 'include attachments' being turned on in the print options.

    rtg = MsgBox("Ready to print the headers and original message?", vbYesNoCancel, "Transport Headers")

    If rtg = 6 Then
        newitem.PrintOut
        oneitem.PrintOut
    End If

    ' Clean up memory used by the macros
    Set oneitem = Nothing
    Set newitem = Nothing

    Exit Sub

    reportit:
    Select Case Err.Number
        Case 91:
                MsgBox "Cannot find a 'current' item. Make sure the item is open and active.", vbExclamation, "Transport Headers"
        Case Else:
                MsgBox "Error retreiving transport headers: " & Err.Number & " - " & Err.Description, vbCritical, "Transport Headers"
    End Select

    End Sub

     

    4. Chose File-Save from the VBA screen to save the macro.

     

    Step 2: Make a ribbon button to launch the macro

    5. Open an existing mail item.  Do not open a new item. The goal is to have a ribbon button on the toolbar of EXISTING items that would have some internet headers.

    6. Right click in the ribbon and choose customize.

    7. At the ribbon customization screen, create a new Toolbar called 'Mytools' or something similar.  Then create a section named 'My Commands'.

    8. In the left hand commands window, change the scope to Macros.  Then drag the 'BetterHeaders' macro over to the 'My Commands' section of the ribbon.

    9. Use the rename button to change the name to something meaningful like 'Track and Print Message Headers', and choose an appropriate icon.

    10.  Press 'OK' to close the customization screen. The end result is that when you open any existing email, you should see this on your ribbon:

    Pressing this button should run the macro which

    a. Creates a new postitem in your inbox

    b. Tries to retrieve the internet message headers from the original message and store them in the body of the new post item.

    c. Adds the subject and received time so that you can visually "see" that these headers go with the original item.

    d. Attaches the original item to the new post.

    e. Asks if you are ready to print both the new post item and the original together

    f. After confirmation, sends both the new post item and the original to the printer.

    g. You can now optionally SAVE the new post item to create a permanent item that shows you have done this work.

     

    Step 3 - Make sure Macros are allowed to Run in Outlook

    If your trust settings do not allow for macros to run in Outlook, then when you press the toolbar button, nothing will happen.  To adjust the macro settings and test the sample, navigate to the trust center.  In Outlook 2010 that is through File-Options-Trust Center (on the left) - Trust Center Settings (button) - Macro Settings (on the left).  Set the macro settings to "Notifications for all macros" or if you understand the risk and are comfortable with the risk, change the setting to 'Enable all macros'.

    Summary and Extra Information

    The macro can be edited and adjusted to a variety of needs.  For example, adding the original item as an attachment is just a convenient way to "link" the header item to the original.  If the goal is only to print them out together once, you do not need to add the original as an attachment, and you could skip that step just by commenting out the one line in the macro that adds the attachment.  Likewise if you want to add more information in the body of the new postitem that adds context to the work, just add that into the macro where the body text is built.

     

    There are a couple of points to make about using and manipulating the PR_TRANSPORT_MESSAGE_HEADERS property.  As explained earlier, it was data copied to the MAPI property during conversion from the SMTP message.  It can be edited and changed without affecting the outlook message in any way.  So just having a method to get and display/print the headers does *NOT* mean they were not manipulated or changed since the message was received.  Secondly, it is important to understand that when Outlook is used with Microsoft Exchange Server, the Exchange Server converts the internet headers to PR_TRANSPORT_MESSAGE_HEADERS, not the outlook client.  In scenarios where you are not using Microsoft Exchange, the outlook client DOES convert the data.  In this scearnio, where the outlook client does the conversion, there is a registry value that will tell outlook to convert the ENTIRE MESSAGE, and not just the headers.  So instead of just having a small bit of header information, the PR_TRANSPORT_MESSAGE_HEADERS will contain the entire original SMTP message including encoded attachments and different body parts, etc.  This setting is described in detail in http://www.outlook-tips.net/beginner-user/view-internet-headers/.  Be advised that if that registry option is enabled, your PR_TRANSPORT_MESSAGE_HEADERS data will potentially be large and likely break the macro.

    This code is provided as an example of how to retrieve and manipulate the PR_TRANSPORT_MESSAGE_HEADERS data.  There is no support for it beyond just comments and self-help on this blog and it is intended as a sample to show the possibilities of using outlook macros to solve special property needs.

     

     

     

     

     

     

     

     

  • Methods to send mail through Outlook

    Back in 2008, I wrote a little VB6 test application to test sending a sample message using the three methods:

    1. SimpleMapi
    2. CDO 1.21
    3. The Outlook Object Model

    At the request of some co-workers, I am going to post that sample here.  It is not supported and is intended to be just an example of how you might write your own test application.  Actually if you were going to write one today, it would probably make sense to use vb.net and some newer code, but this can still serve as a sample. I can make no guarantees that it works in all situations, so use it at your own risk.

    Cheers,
    Scott

  • The HOWTO and Sample Template for Custom Outlook Calendar Templates

    Hopefully I am attaching the files for this topic along with this post. 

    There should be a zip file with:

    1) The word document that descibes the techniques in detail step by step

    2) A SAMPLE word template that implements one specific kind of custom calendar template

    3) A readme.txt to remind everyone these files are just provided as samples. 

  • Custom Calendar Printouts from Outlook

    One feature in Outlook that divides and unites people is the calendar printing feature set.  It seems that if you ask 1000 people what they need from a calendar printout, you will get 999 different answers.  Some people rely on word wrapping a certain way, others want more flexibility in paper sizes, others want more/better formatting of the appointment text, etc etc.  We actually have a companion product, the Calendar Printing Assistant for Outlook or CPAO for short, which produces even more printout options.  Yet, even with the CPAO there are just some things you can't get in your printouts.  This blog entry is a reminder of an age old way to get "exactly" what you want if you have some intermediate scripting skills and a little patience.

    I will be posting the detailed step by step on how to use this method, but for now, here is a summary.

    1. We use Word for Windows as the "formatting engine".  Word has tables and styles and colors and shading and practically any kind of formatting you can imagine.  So if you want your appts to be in a crazy font, or a different color, or animated with ants crawling around the letters, then Word provides that.  Not only do we have a gigantic set of formatting choices, but we have features that make it easy to LAYOUT the calendar.  Tables can be inserted to look like months or weeks.  Bookmarks can be used to mark places to insert our data, and autotext entries can make boilerplate information a snap.

    2. We use macros from Word to automate Outlook and get the calendar data.  The outlook object model provides a rich set of methods to get the data we want.  We can restrict by dates, include or not include recurring data, get the tasks data as well, etc.

     

    3. Figure out how to best put things together.  To run macros, you have to apply some level of trust to things these days.  That means either signing the word macros, or lowering the macro secruity or something that will let the code run and get the work done.

     

    So that will be out topic.  Hopefully I will have the first part ready in a day or so...

  • Create a minidump from VB.Net

    I am a programming hack, but i do need to prototype tools for support.  This month, I am working on a little notification tray utility that will monitor the processes that have mspst32.dll loaded.  This is becuase if those processes don't exit properly, it will cause outlook to not trust your datafile, and the next time you launch you have to sit through a lengthy data file check.  So to help monitor and diagnose those problems, I am writing a tool.  As part of that tool, I wanted to be able to create a memory dump of a process that would not close.  I looked forever for a sample of using MiniDumpWriteDump API in VB.Net without success.  With the help of my incredible dev support guru Matt Stehle (http://blogs.msdn.com/mstehle/), was able to convert a C# example and get it working in Vb.Net.  Here is the snippet

     

    'Imports

    Imports System.Diagnostics
    Imports System.IO 

    ' The declaration and enum for the dump types:

     Private Declare Function MiniDumpWriteDump Lib "dbghelp.dll" (ByVal hProcess As IntPtr, ByVal ProcessId As Int32, ByVal hFile As IntPtr, ByVal DumpType As mydumptypes, ByVal ExceptionParam As IntPtr, ByVal UserStreamParam As IntPtr, ByVal CallackParam As IntPtr) As Boolean

    Enum mydumptypes
            normal = 1
            full = 2
    End Enum

    ' pseduocode to get a process and create a filename. 

    dumpprocess = Process.GetProcessById(<PID of the process>)
    onefilename = Path.GetTempFileName.ToString
    onefile = New FileStream(onefilename, FileMode.Create)

    'Call the API
    result = MiniDumpWriteDump(dumpprocess.Handle, dumpprocess.Id, onefile.SafeFileHandle.DangerousGetHandle, mydumptypes.normal, 0, 0, 0)

    There is some way to add GetLastError support to the API declaration so thats on my list to add.  The API returns true or false, so if there was an error you have to retrieve it with Getlasterror in some way.  Also note you need the "debug" priviledge to be able to write dumps.  If you are a local machine admin, you get that priviledge for free.

     

  • A word about MAPI named properties ...

    One of the early roadblocks to understanding MAPI and especially Outlook's integration with it, is coming to grips with Mapi's named property feature.  If you use a tool like Steve Griffin's excellent MFCMAPI.EXE, you can see named props in a table view type of UI.

     A large subset of mapi tags are static and well defined.  PR_SUBJECT is always proptag 0x0037 for example.

    Properties in the 0x8000 range however are not static.  They are reserve for named properties.  So an application can tell mapi "I want a named property called myprop, and the store will dynamically "map" this named prop to one of the tags in the 0x8000 range.

    In a choice that might make sense in some internal way, but always adds confusion to the mapi beginner, the outlook team decided to "name" their named props using a numbering scheme that coincentally started around 0x8000.  Therefore, the outlook named properties look visually JUST like regular property tags.  What this means is that you sometimes see documentation that says "look at this prop 0x8### that outlook uses for such and such.  So if you assume that the 0x8### is the actual tag, you look at the wrong property.  You must find out what the 0x8### maps to for your particular store.   MFCMAPI.EXE shows the named prop names out in about the 4th or 5th column.  You can sort by that column, find the 0x8### you are intersted in, and read the data from that row.

    For example, you can see in Steve's post about the new timezone props for Outlook 2007 (http://blogs.msdn.com/stephen_griffin/archive/2006/12/06/outlook-2007-timezone-structures.aspx) that a couple of new "outlook" props are used for time zone information.

    dispidApptTZDefStartDisplay  0x825E 
    dispidApptTZDefEndDisplay    0x825F  

    Lets say you want to know whether an item in outlook has been stamped with the new time zone props.  If you use MFCMAPI.EXE and look immediately for a property tag of 0x825e you will probably be dissapointed.  What you must do is scroll over to the right to see the named props column.  Sort by the column, go down to where 0x825E would be, and if its there, you have the new props.

     

     

  • Sharing Messages in Outlook 2007

    Im going to preference all these post by the words "I THINK:"

    This means that I believe all this stuff to be technically accurate, but I don't gurantee it.  I could be wrong, or have missed something in my testing.  More than anything I want it to come across as a brain dump. So read at your own risk ...

    I THINK:

    Down in the architecture of Outlook, different message types are represented by "objects" that kind of encapsulate everything that is associated with them.  Everyone is familiar with task items and contact items, and appointment items for example.

    One of the newer item types is the Sharing Item.  You will see Sharing Items when you sent or reply to a sharing invitation in Outlook 2007.  Sharing items have a message class of IPM.Sharing.

    Sharing items at the mapi level contain special properties (named properties in a special propset) that tell outlook about the sharing characteristics (the URL to the sharing provider, a GUID to identify it, etc).  So these 10 or 12 custom properties have to be present on a sharing item to "make it work".

    Now an interesting scenario happens if you have an environment where a non MAPI method is used to generate or reply to a sharing item.  The non mapi sender would either have to be smart enough to construct a TNEF blob (which encapsulates mapi properties) and store that in the RFC822 message, or it would have do use something other than mapi properties.

    The good news is that Outlook 2007 will actually look for and use X-Header information for all these needed properties.  So if a message arrives that does NOT have the needed mapi properties, outlook will look in the PR_TRANSPORT_MESSAGE_HEADERS prop and parse out any X-header information that is there.  If the Sharing props are present, they will be used, and the sharing message will render and behave like you expect.  If the mapi props are not there, and the X-header props are not there, Outlook will yield a cryptic "Cannot open this item" dialog, and ask you if that dialog was helpful  (I'll leave it to the reader to decide whether the dialog was helpful).

    One common scenario where you might see this is when using MOSS 2007 Alerts.  These alerts are sharing items that tell you that something has changed on one of your sharepoint lists.  They are sent via SMTP from the server.  Unfortunately, if you receive these alerts through Exchange server 2003 and then sync them down to cached mode of Outlook 2007, the sharing message properties do not make it to outlook.  The result is that when you try to open the sharing item, you get the error.

    This is a known problem and we are currently investigating a fix that will allow the props to survive the trip, and allow the sharing item to work.  I'll post again when/if that fix becomes available.

     

  • Where to begin ...

    Ok, lets see ...

    I work for Microsoft supporting Outlook and get to spend a lot of time working on interesting problems.  My goal for this blog is to share some of what I learn about those problems and some Outlook "stuff" in general.  I also just turned 40 which seems impossible until I look at my children and realize how old I really am.

     I'm also a frustrated guitar player, and really passionate about songwriting and music.  So that will creep into my posting I'm sure from time to time.  The only other thing that will probably be noticable is that I am an awful speller and too impatient to correct the mistakes.  So no doubt many post will look sloppy and seem unintelligle becuase i typed can when i mean can't or something.

    So without further fanfare, on to the first techincal entry ...