• Sign In
 
  • MSDN Blogs
  • Microsoft Blog Images
  • More ...

  • Advanced search options...
  • About
  • Email Blog Author
  • RSS for posts
  • Atom
  • RSS for comments
    • OK
  • CDO (20)
  • Code Snippet (42)
  • Custom Providers (15)
  • Debugging (7)
  • DevMsgTeam (278)
  • Documentation (101)
  • DST (8)
  • EWS (7)
  • Exchange (100)
  • Gotchas (91)
  • Hotfix (28)
  • MAPI (220)
  • MAPI Download (48)
  • MFCMAPI (92)
  • MSDN (54)
  • Non Dev (11)
  • OOM (17)
  • Outlook (163)
  • Outlook 2007 Auxiliary Reference (45)
  • Outlook Integration API (11)
  • Protocol Docs (20)
  • PST/OST (22)
  • Referrals (8)
  • Vista (12)
  • WrapPST (15)
Links:
  • Download MFCMAPI
  • MFCMAPI on Facebook
  • Troubleshooting Outlook Crashes
  • Office Update Center
  • Developer Messaging Team Blog
This site is provided "AS IS" with no warranties, and confers no rights. Use of included code samples are subject to the terms specified in the Terms of Use.
Archives
  • May 2012 (1)
  • April 2012 (3)
  • March 2012 (3)
  • February 2012 (3)
  • January 2012 (1)
  • December 2011 (3)
  • November 2011 (1)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (4)
  • June 2011 (3)
  • May 2011 (3)
  • April 2011 (3)
  • March 2011 (5)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (1)
  • November 2010 (4)
  • October 2010 (1)
  • September 2010 (3)
  • August 2010 (5)
  • July 2010 (3)
  • June 2010 (3)
  • May 2010 (1)
  • April 2010 (3)
  • March 2010 (3)
  • February 2010 (3)
  • January 2010 (2)
  • December 2009 (3)
  • November 2009 (5)
  • October 2009 (4)
  • September 2009 (5)
  • August 2009 (5)
  • July 2009 (11)
  • June 2009 (6)
  • May 2009 (5)
  • April 2009 (3)
  • March 2009 (18)
  • February 2009 (10)
  • January 2009 (3)
  • December 2008 (2)
  • November 2008 (2)
  • October 2008 (5)
  • September 2008 (4)
  • August 2008 (10)
  • July 2008 (6)
  • June 2008 (8)
  • May 2008 (2)
  • April 2008 (4)
  • March 2008 (2)
  • February 2008 (2)
  • January 2008 (5)
  • December 2007 (3)
  • November 2007 (2)
  • October 2007 (3)
  • September 2007 (1)
  • August 2007 (4)
  • July 2007 (5)
  • June 2007 (3)
  • May 2007 (4)
  • April 2007 (1)
  • March 2007 (6)
  • February 2007 (3)
  • January 2007 (2)
  • December 2006 (4)
  • November 2006 (3)
  • October 2006 (1)
  • August 2006 (1)
  • June 2006 (5)
  • May 2006 (5)
  • December 2005 (1)
  • November 2005 (4)
  • October 2005 (2)
  • September 2005 (1)
  • April 2005 (3)
  • December 2004 (2)
  • September 2004 (2)
  • August 2004 (3)
  • July 2004 (3)
Blogs I Read
  • Exchange

  • Raymond Chen [MSFT]

  • Larry Osterman [MSFT]

  • Peter David

  • Aaron Margosis [MSFT]

  • Jason Johnston [MSFT]

  • Matt Stehle (MSFT)

  • Patrick Creehan [MSFT]

  • Ryan Gregg [MSFT]

    Outlook PM
  • WebDav 101

    Dan Bagley (MSFT)
  • Dave Vespa [MSFT]

  • Randy Topken

    Outlook EE

July, 2007

MSDN Blogs > SGriffin's MAPI Internals > July, 2007
  • Subscribe via RSS
Sort by: Most Recent | Most Views | Most Comments
Excerpt View | Full Post View
  • SGriffin's MAPI Internals

    More IConverter For Ya

    Posted over 5 years ago
    by Stephen Griffin - MSFT
    • 14 Comments

    [This is now documented here: http://msdn2.microsoft.com/en-us/library/bb821176.aspx]

    Good news, everyone! We've decided to document some more flags and functions for IConverterSession.

    Here are the new constants:

    #define CCSF_USE_TNEF        0x0010 // the converter should embed TNEF in the MIME message
    #define CCSF_8BITHEADERS     0x0040 // the converter should allow 8 bit headers
    #define CCSF_PLAIN_TEXT_ONLY 0x1000 // the converter should just send plain text
    

    All three of these flags are only valid for MAPI->MIME.

    More interesting are the new functions. They take the place of the first and last placeholder functions in the interface. The first, SetAdrBook, allows you to pass a MAPI address book for the converter to use for name resolution. The second, SetCharSet, allows you to tell the converter which character set to use.

    Here's a newly updated header. Note that you will need to locate and include mimeole.h for this header to work - there are too many types from that header being used to try and define them all here:

    #pragma once
    //Class Identifiers
    // {4e3a7680-b77a-11d0-9da5-00c04fd65685} 
    DEFINE_GUID(CLSID_IConverterSession, 0x4e3a7680, 0xb77a,
    	0x11d0, 0x9d, 0xa5, 0x0, 0xc0, 0x4f, 0xd6, 0x56, 0x85); 
    
    //Interface Identifiers
    // {4b401570-b77b-11d0-9da5-00c04fd65685} 
    DEFINE_GUID(IID_IConverterSession, 0x4b401570, 0xb77b,
    	0x11d0, 0x9d, 0xa5, 0x0, 0xc0, 0x4f, 0xd6, 0x56, 0x85);
    
    // Constants
    #define CCSF_SMTP            0x0002 // the converter is being passed an SMTP message
    #define CCSF_NOHEADERS       0x0004 // the converter should ignore the headers on the outside message
    #define CCSF_USE_TNEF        0x0010 // the converter should embed TNEF in the MIME message
    #define CCSF_INCLUDE_BCC     0x0020 // the converter should include Bcc recipients
    #define CCSF_8BITHEADERS     0x0040 // the converter should allow 8 bit headers
    #define CCSF_USE_RTF         0x0080 // the converter should do HTML->RTF conversion
    #define CCSF_PLAIN_TEXT_ONLY 0x1000 // the converter should just send plain text
    #define CCSF_NO_MSGID        0x4000 // don't include Message-Id field in outgoing messages
    
    interface IConverterSession : public IUnknown
    {
    	public:
    	virtual HRESULT STDMETHODCALLTYPE SetAdrBook(LPADRBOOK pab);
    	virtual HRESULT STDMETHODCALLTYPE SetEncoding(ENCODINGTYPE et);
    	virtual HRESULT PlaceHolder1();
    	virtual HRESULT STDMETHODCALLTYPE MIMEToMAPI(LPSTREAM pstm,
    			LPMESSAGE pmsg,
    			LPCSTR pszSrcSrv,
    			ULONG ulFlags);
    	virtual HRESULT STDMETHODCALLTYPE MAPIToMIMEStm(LPMESSAGE pmsg,
    			LPSTREAM pstm,
    			ULONG ulFlags);
    	virtual HRESULT PlaceHolder2();
    	virtual HRESULT PlaceHolder3();
    	virtual HRESULT PlaceHolder4();
    	virtual HRESULT STDMETHODCALLTYPE SetTextWrapping(BOOL fWrapText,
    			ULONG ulWrapWidth);
    	virtual HRESULT STDMETHODCALLTYPE SetSaveFormat(MIMESAVETYPE mstSaveFormat);
    	virtual HRESULT PlaceHolder5();
    	virtual HRESULT STDMETHODCALLTYPE SetCharset(
    		BOOL fApply,
    		HCHARSET hcharset,
    		CSETAPPLYTYPE csetapplytype);
    };
    

    Some notes:

    • The LPADRBOOK passed in to SetAdrBook is obviously a MAPI address book. While conversion can normally be done without logging on to a MAPI profile, if SetAdrBook is to be used you will need to log on to a profile to get an address book.
    • HCHARSET is a handle, not a constant. NULL is a valid value. For non-NULL values, you'll need to fetch an HCHARSET using a function like MimeOleGetCodePageCharset. This means you'll either need to link in inetcomm.lib or do the LoadLibrary/GetProcAddress thing.
    • If fApply is false, then the parameters passed in SetCharSet aren't used. You'd do this if you wanted to set a character set for a particular message, then return to the defaults for subsequent messages.
    • In SetAdrBook, pab can be NULL. This will reset the converter to not using an address book.
    • SetAdrBook and SetCharSet always returns S_OK. But you should still check in case we later decide there's a case where they can fail.

    I think I documented the new flags correctly - if not, let me know and I'll fix them.

  • SGriffin's MAPI Internals

    What To Do? What To Do?

    Posted over 5 years ago
    by Stephen Griffin - MSFT
    • 3 Comments

    [This is now documented here: http://msdn2.microsoft.com/en-us/library/bb905124.aspx] 

    Melissa put up a blog entry on making a PST show up on the To-Do Bar in Outlook 2007. This naturally raised the question of how you could do this programmatically. It turns out that the To-Do Bar itself selects which stores to list. It does this by looking in each store in the profile for specially constructed search folders. So getting a PST (or any other store) listed in the To-Do Bar is really a matter of getting these search folders created. Unfortunately, this is where things get really complicated really fast.

    But there are workarounds, and development has asked me to document them. The simplest workaround is to make the PST the default store in the profile. Outlook will create the special search folders needed for the To-Do Bar in the default store. After launching Outlook with the PST as the default (you should be able to use the OOM to do this), you can restore the original default store. This should work with any store capable of being a default store in a profile.

    Now - that's a bit of a hack, but it'll work. If you're using the Account Management API already to manage identities for your store, there's another way to get the folders created. Outlook 2007 introduces a new property, PROP_ACCT_DELIVERY_STORE, which is primarily used to control which store is the delivery store for an identity, such as a POP account. But its use is not limited to POP. If your store is listed in PROP_ACCT_DELIVERY_STORE for an account, Outlook will create the search folders, and your store will appear in the To-Do Bar.

    Here's the details on the prop (I wanted to mimic the MSDN here but my HTML skillz are too weak to handle that, so this is what you get.)

    PROP_ACCT_DELIVERY_STORE

    Returns the Entry ID of the default delivery store for the account.

    Quick Info

    See IOlkAccount.

    Identifier: 0x0018
    Property type: PT_BINARY
    Property tag: 0x00180102
    Access: Read-write

    Remarks

    Get or set this property with IOlkAccount::GetProp and IOlkAccount::SetProp.

    7/13/07, 11:39AM Update - development agreed we can document the two props Dmitry pointed out. Yay!

    PROP_ACCT_DELIVERY_FOLDER

    Returns the Entry ID of the default delivery folder for the account. Default is Inbox.

    Quick Info

    See IOlkAccount.

    Identifier: 0x0019
    Property type: PT_BINARY
    Property tag: 0x00190102
    Access: Read-write

    Remarks

    Get or set this property with IOlkAccount::GetProp and IOlkAccount::SetProp.

    PROP_ACCT_SENTITEMS_EID

    Returns the Entry ID of the default sent items folder for the account. Default is Sent Items.

    Quick Info

    See IOlkAccount.

    Identifier: 0x0020
    Property type: PT_BINARY
    Property tag: 0x00200102
    Access: Read-write

    Remarks

    This property is only available on POP3 and IMAP accounts. It cannot be set. 

    Get this property with IOlkAccount::GetProp.

  • SGriffin's MAPI Internals

    Don't Crawl On Me

    Posted over 5 years ago
    by Stephen Griffin - MSFT
    • 0 Comments

    [This is now documented here: http://msdn2.microsoft.com/en-us/library/bb905130.aspx] 

    There are a few places in Outlook that need to enumerate the folders in the message stores in a profile. It's usually looking for folders of a certain type. Normally this isn't a problem, but for some message stores this can be a big deal. Consider the Public Folder store. It will typically have thousands of folders, which may be scattered on servers around the world. Crawling through and opening every public folder could be hugely expensive. So most of the logic doing these crawls detects Public Folders and doesn't crawl them.

    But what if you've implemented your own message store and would prefer that Outlook not crawl it? This was an issue that came up in a number of bugs, most of which were fixed, but only some were documented. This post gathers them all together and completes the documentation.

    Common Information

    All of these features will look for a named property exposed on a message store's IMsgStore object. The named properties are integers (PT_LONG) of kind MNID_String in the PSETID_Common namespace. For each named property, if the property is not found or is found but set to 0, then Outlook assumes the store is OK to crawl.

    The constants CSM_CLIENT_DO_NOT_CHANGE and ASM_CLIENT_DO_NOT_CHANGE, defined below, are for future reference and are not currently implemented. For now, a store can prevent clients from changing any of these flags by hard coding the values that the store returns for the property.

    PSETID_Common has been defined elsewhere, but I repeat it here for completeness:

    const GUID PSETID_Common = {0x00062008, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46}};
    

    Common folder crawl

    This is the crawl that Outlook performs on boot to ensure it knows about all the Contact, Calendar, Task, etc. folders in the opened stores.

    lpwstrName = L"CrawlSourceSupportMask";
    enum 
    {
     CSM_DEFAULT              = 0, // Store doesn't care if we crawl or not.
     CSM_DO_NOT_CRAWL         = 1 << 0x0,    // Store doesn't want us to crawl.
     CSM_CLIENT_DO_NOT_CHANGE = 1 << 0xF     // Store won't let the client set the crawl support mask.
    };

    This property was exposed in hotfixes to both Outlook 2003 (835210) and Outlook 2007 (938893).

    Autoarchive

    This is the crawl Outlook performs to determine which folders need to be autoarchived. This crawl can be turned off globally, or per store using the named property.

    lpwstrName = L"ArchiveSourceSupportMask";
    enum 
    {
       ASM_DEFAULT              = 0,        // Allow archiving
       ASM_DO_NOT_ARCHIVE       = 1 << 0x0, // Do not allow archiving
       ASM_CLIENT_DO_NOT_CHANGE = 1 << 0xF  // Do not allow client to set archiving flags
    };
    

    This property was exposed in a hotfix to Outlook 2003 (826225). It is present in Outlook 2007 RTM.

    Mail Merge Contact Folder Scan

    This is the crawl Outlook and Word perform to find contact folders during Mail Merge.

    lpwstrName = L"NoFolderScan";
    

    This property was exposed in a hotfix to Outlook 2003 (842776). It is present in Outlook 2007 RTM.

  • SGriffin's MAPI Internals

    Whodathunkit?

    Posted over 5 years ago
    by Stephen Griffin - MSFT
    • 1 Comments

    I managed to waste half a morning figuring out something I'm supposed to already know. Dev asked if I could confirm that MrXP works with Outlook 2007 so we can include it in some upcoming documentation. Sure thing. First step is to install it right?

    Here's the simple little script Jason and I were using to install MrXP:

    copy .\release\mrxp32.dll %windir%\system32
    %windir%\system32\rundll32.exe %windir%\system32\mrxp32.dll MergeWithMAPISVC
    

    MergeWithMAPISVC was just an export that did all the file twiddling needed to "install" the provider. This script works just fine on the Windows XP/Office 2003 laptop that we did the bulk of the MrXP testing on. But when I ran it on my 64 bit Vista box with Office 2007, I get this error:

    Error loading mrxp32.dll
    The specified module could not be found.

    Ok - so this must be some sort of UAC related problem right? I turn off UAC and make sure I'm in a command prompt with Admin rights. No luck. Maybe it's a dependency problem? I try running depends.exe - it can't find the file either! By now I should have realized the problem, but instead I debug it. I see rundll32 reads the DLL off the disk and plays with it for a while - checking various exports for things like manifests. But when it actually calls LoadLibrary, it fails. Then, (and this should have been the big clue) it loads syswow64\rundll32.exe and gives it the same command line. That's the one that fails with the "not found" error.

    Being a bit thick, I grab a Platforms EE and ask him to explain this all to me. I manage to lead him on for a while, chasing the same red herrings I was chasing, before he looks at my repro and points out the problem. On 64 bit Vista, "system32" is NOT the 32 bit directory. Nope - it's 64 bit! And "syswow64" is NOT the 64 bit directory. It's 32 bit!

    What was happening was I was copying my 32 bit binary into the 64 bit directory and then running the 64 bit version of rundll32. It tried to load my binary as a 64 bit binary, failed, then asked the 32 bit binary to deal with it. The 32 bit version of rundll32 then tried to load my module from system32. But, due to file system redirection, it ends up looking in syswow64, where the file doesn't live! If I had placed the module in sysWow64, I wouldn't have had a problem.

    Of course, a proper installer will handle all this and place the files in the right directory regardless of the OS and bitness, but this was just a simple little script for testing, so I went with the easy fix:

    copy .\release\mrxp32.dll %windir%\system32
    copy .\release\mrxp32.dll %windir%\sysWow64
    %windir%\system32\rundll32.exe %windir%\system32\mrxp32.dll MergeWithMAPISVC
    

    Now the script works on both 32 and 64 bit and I can get on with my testing.

    BTW - aside from some changes to fix how it handles MAPISVC.INF, MrXP passed the tests with flying colors.

  • SGriffin's MAPI Internals

    You Mean You Want the OOM To Actually Work?

    Posted over 5 years ago
    by Stephen Griffin - MSFT
    • 2 Comments

    We had a customer report recently that when he tried to fetch certain properties using the Outlook Object Model, all he got was garbage. This turned in to an interesting foray into the world of dual dispatch interfaces and VB.

    The properties in question were these:

    ContactItem: 
        Email1EntryID 
        Email2EntryID 
        Email3EntryID
    MailItem: 
        ReceivedByEntryID 
        ReceivedOnBehalfOfEntryID
    

    Note that all of them are entry IDs, but they're not the commonly used entry ID properties like EntryID or StoreID.

    If you ask for one of these properties using VB/VBA, all you get is garbage:

        strEntryID = objContactItem.Email1EntryID
        Debug.Print strEntryID
        On Error Resume Next
        Set objRec = Application.Session.GetRecipientFromID(strEntryID)
        If objRec Is Nothing Then
            Debug.Print "GetRecipientFromID failed"
        Else
            Debug.Print objRec.name
            Debug.Print objRec.entryID
        End If
    

    I'll spare you the C++ code, but suffice it to say that requesting this property via the IDispatch interface works.

    What's going on? The key detail here is how Outlook builds the VARIANT property to return it. It doesn't use something like SAFEARRAY. It doesn't covert the entry ID from a binary format to a hex encoded string. Instead, it uses VT_BSTR, setting the number of bytes as the length prefix and the binary blob as the string.

    This actually works, as long as you never copy the BSTR. And when you're fetching the property with C++, you can just pass the pointer around and treat it however you like. But not in VB. Any manipulation of the variant in VB, even assigning it to a variable, results in an implicit copy of the data. And the routines doing the copy think they're dealing with a NULL terminated string, so they truncate the binary blob at the first NULL.

    Of course, I filed a bug on all this, but ultimately we had to reject it. The only way to fix this is to change the form of the data returned by the property, which would break all existing C++ code that uses the property. Maybe for a future version of Outlook we'll look at adding new properties to expose these entry IDs as hex encoded strings.

    However, if you're using Outlook 2007, you have a workaround - the PropertyAccessor handles binary properties correctly. You just need to know what properties to fetch. The MailItem properties, ReceivedByEntryID and ReceivedOnBehalfOfEntryID are already documented as PR_RECEIVED_BY_ENTRYID and PR_RCVD_REPRESENTING_ENTRYID. The contact properties, like the other contact linking props I documented before, are named properties in the PSETID_Address namespace. Here they are (in C++ form):

    #define dispidEmail1OriginalEntryID 0x8085
    #define dispidEmail2OriginalEntryID 0x8095
    #define dispidEmail3OriginalEntryID 0x80A5

    And here's how you can use them from VB:

        Const dispidEmail1OriginalEntryID As String =
            "http://schemas.microsoft.com/mapi/id/{00062004-0000-0000-C000-000000000046}/80850102"
        Set oPA = objContactItem.PropertyAccessor
        strEntryID = oPA.BinaryToString(oPA.GetProperty(dispidEmail1OriginalEntryID))
        Debug.Print strEntryID
        Set objRec = Application.Session.GetRecipientFromID(strEntryID)
        If objRec Is Nothing Then
            Debug.Print "GetRecipientFromID failed"
        Else
            Debug.Print objRec.name
            Debug.Print objRec.entryID
        End If
    
Page 1 of 1 (5 items)
  • © 2012 Microsoft Corporation.
  • Terms of Use
  • Trademarks
  • Privacy Statement
  • Report Abuse
  • 5.6.402.223