Add header to outgoing mail RRS feed

  • Question

  • Hello,

    I would like to create an outlook add-in that simply adds a custom bit of header information to every outgoing mail message.  I have searched all day for information on this topic, and I would just like somebody to point me in the direction I need to go.  I don't want to use CDO or Redemption (costs $200).  I can easily use the propertyaccessor in Outlook 2007 to read the header, but you cannot set this property.  Also, I need to support Outlook 2003.  I also have a class I modified from http://www.outlookcode.com/codedetail.aspx?id=1112.  It uses mapi32 directly with interop, and I can get the header information easily, but it does nothing when I try to set it.  Even if it comes down to me having to write a com component in c++, where is a good resource to get started with that.  Is there a MAPI SDK anymore?

    Any help would be appreciated.

    Tuesday, May 1, 2007 10:00 PM


  • Jarrod, can I mark this as answered or were there outstanding issues?



    Wednesday, May 23, 2007 9:36 PM

All replies

  • Are you using the right namespace? This works fine here:


     oPA.SetProperty("http://schemas.microsoft.com/mapi/string/" & _
                     "{00020386-0000-0000-C000-000000000046}/" & Prop, Val)


    where oPA is the PropertyAccessor, Prop is the string for the x-header name and Val is the string value for that header. Make sure you do something to "dirty" the item after you set the X-header, such as setting the Subject value, and then save it.


    Of course, if you want to support Outlook 2003, then PropertyAccessor is irrelevant. Helmut's MAPI32 technique ought to work. Yes, there is still a MAPI SDK and MAPI discussion groups; see http://www.outlookcode.com/d/mapi.htm

    Wednesday, May 2, 2007 1:56 PM
  • Sue, you appear to the the authority on outlook, thanks for your help.

    I finally got the PropertyAccessor to work as per your post.  I was using the PropertyTag format and when I used the string format as you suggested it worked.  But as you mentioned, I would like to support 2003 so this is irrelevant.  I would like to get Helmut's technique to work.  Here is the method I have been using, taken from Helmut's post.  What do I need to do to get this to work?  I see he is using the propertytags, so how can I modify this to set the header?  Thanks again.

    Code Snippet

    private static void SetMessageProperty(object mapiObject, uint propertyTag, string text)
                // Pointer to IUnknown Interface
                IntPtr IUnknown = NULL;

                // Pointer to IMessage Interface
                IntPtr IMessage = NULL;

                // Pointer to IMAPIProp Interface
                IntPtr IMAPIProp = NULL;

                // Structure that will hold the Property Value
                SPropValue propValue;

                // A pointer that points to the SPropValue structure
                IntPtr ptrPropValue = NULL;

                // if we have no MAPIObject everything is senseless...
                if (mapiObject == null) return;

                    // We can pass NULL here as parameter, so we do it.

                    // retrive the IUnknon Interface from our MAPIObject comming from Outlook.
                    IUnknown = Marshal.GetIUnknownForObject(mapiObject);

                    // since HrGetOneProp needs a IMessage Interface, we must query our IUnknown interface for the IMessage interface.
                    // create a Guid that we pass to retreive the IMessage Interface.
                    Guid guidIMessage = new Guid(IID_IMessage);

                    // try to retrieve the IMessage interface, if we don't get it, everything else is sensless.
                    if (Marshal.QueryInterface(IUnknown, ref guidIMessage, out IMessage) != S_OK) return;

                    // create a Guid that we pass to retreive the IMAPIProp Interface.
                    Guid guidIMAPIProp = new Guid(IID_IMAPIProp);

                    // try to retrieve the IMAPIProp interface from IMessage Interface, everything else is sensless.
                    if (Marshal.QueryInterface(IMessage, ref guidIMAPIProp, out IMAPIProp) != S_OK) return;

                    // double check, if we wave no pointer, exit...
                    if (IMAPIProp == NULL) return;

                    // Alloc memory for the text and create a pointer to it
                    IntPtr ptrToValue = Marshal.StringToHGlobalAnsi(text);

                    // Create our structure with data
                    propValue = new SPropValue();
                    // Wich property should be set
                    propValue.ulPropTag = propertyTag;
                    propValue.dwAlignPad = 0;
                    propValue.Value = (long)ptrToValue;

                    ptrPropValue = Marshal.AllocHGlobal(Marshal.SizeOf(propValue));
                    Marshal.StructureToPtr(propValue, ptrPropValue, false);

                    // try to set the Property ( Property Tags can be found with Outlook Spy from Dmitry Streblechenko )
                    HrSetOneProp(IMAPIProp, ptrPropValue);

                catch (System.Exception ex)
                    // Free used Memory structures
                    if (ptrPropValue != NULL) MAPIFreeBuffer(ptrPropValue);

                    // cleanup all references to COM Objects
                    if (IMAPIProp != NULL) Marshal.Release(IMAPIProp);
                    if (IMessage != NULL) Marshal.Release(IMessage);
                    if (IUnknown != NULL) Marshal.Release(IUnknown);

    Wednesday, May 2, 2007 5:52 PM
  • I would suggest that you post a comment in response to Helmut's original code sample, providing the link to this discussion (so you don't have to duplicate the code posting). He drops in here, as well, so if it's in both places, eventually he should see it.
    Wednesday, May 2, 2007 6:22 PM
  • Hi all,


    I've seen this Post and I have no finnished sample for this.

    But you have my proof of concept code already that can read and write properties.

    You can do it also with redemption: see http://www.dimastr.com/redemption/


    Dmitry has shown us the way to do it:


    tag = sItem.GetIDsFromNames("{00020386-0000-0000-C000-000000000046}", "x-test-header")
      tag = tag or &H1E     'the type is PT_STRING8
      sItem.Fields(Tag) = "test value"

    I will try to create a sample C# snippet - but it takes a little time to hack this out.

    If I got it I will post it here - on my and on Sue's site.


    Greets, Helmut



    Thursday, May 3, 2007 8:30 AM
  • Helmut,

    Thanks alot for taking a look at this.  I figured we would need to interop with GetIDsFromNames, but I am pretty new at interop, so I really appreciate any help you could give me with that C# code.  As, I said, I was trying to avoid using redemption simply because I hate to have to pay for the license when I think this is the only thing I would really need it for.  I believe a method for doing this without any other libraries would be helpful to many judging from the number of posts I have seen on this topic.

    Thanks, Jarrod
    Thursday, May 3, 2007 5:20 PM
  • Hello Jarrod,


    I played around and I wasn't able to get it working.

    I'm stucking at one point where I have a handle as Int32Ptr and I simply can't execute a function wich isn't defined.

    So the Idea is to define an IMAPIProp Interface and convert the Int32Ptr to this Interface and the execute the Method.


    I will try my best (In my spare time) to get it working - but if you need it quick: You should get Redemption


    Greets Helmut


    Thursday, May 10, 2007 6:32 AM
  • Yeah, we are looking at redemption right now.  I really appreciate your help.  That interop stuff gets really messy so if you can get this working it would be great, but if not I think we may end up using redemption.  We have to get this one way or another...

    Thursday, May 10, 2007 5:11 PM
  • Jarrod, can I mark this as answered or were there outstanding issues?



    Wednesday, May 23, 2007 9:36 PM
  • Hello Jarrod,


    I have written an Article that addresses this and brings you the solution.

    I hope this helps.


    see: http://www.codeproject.com/useritems/BridgingTheGap.asp


    Greets, Helmut

    Tuesday, June 12, 2007 6:17 AM