The first answer I give to customers asking for tips on making their store provider work with the latest versions of Outlook is "be well behaved". I've had a couple message store provider cases recently that illustrate this and give me an opportunity to expand on that statement and give some concrete guidance.
The customer's message store wasn't working well in Outlook 2007. Outlook would recreate it's persisted search folders every time it booted. In fact, it would create them, then try to create them again and again, chewing up cycles. They had checked their code to ensure that the folders got created, and that every property Outlook set was available. Except - some properties weren't accessible through the hierarchy table.
When Outlook loaded the hierarchy table for the message store, it looked for any folders with PR_EXTENDED_FOLDER_FLAGS stamped on them. Those that had this property, with the appropriate flags set in it, would be added to the list of known search folders. Later, when Outlook needed to access one of the search folder, it would consult the list it had built. Since this particular store didn't expose a number of properties through the hierarchy table, this list was empty. So Outlook concluded it needed to create the search folder.
The first lesson we learn is this:
Properties set on messages and folders should be accessible through contents tables and hierarchy tables
This lesson was behind the PR_RECORD_KEY issues and it's been the source of countless other store providers issues I've debugged over the years. Outlook (and MFCMAPI and even Outlook Spy) expect most properties to come back the same regardless of whether they're requested through the table or through the object. The solution is not to special case properties you think Outlook might want, but instead to ensure the general case works. That's exactly what this customer did and his provider's working much better now.
Different customer this time. Their issue was a bit more complicated. A user would send a recurring meeting request to another user, both running their message store. The recipient would let Outlook create a tentative appointment on the calendar. If at this point, they open the series, the info bar would prompt "Please respond". If they then opened one instance of the meeting and accepted it, then went back and opened the series, the info bar now indicated "Tentatively accepted". When the same steps were performed with Exchange as the back end, both times the info bar would prompt "Please respond".
Outlook's logic for determining which string to display hinges on two properties - dispidResponseStatus and dispidApptReplyTime. If the status is set to none or not responded, we display "Please respond". If it's set to tentative, we display "Tentatively accepted", but ONLY if the reply time is set. If the reply time isn't set, we display "Please respond" instead. When the recipient accepted a single instance of the meeting, Outlook changed the response status on the master to tentative, but didn't set a reply time. However, when Outlook then looked at the properties on the meeting, the reply time was set, so it displayed "Tentatively accepted".
How did the reply time property get set? This leads to the second lesson:
Message stores which manufacture Outlook's business logic properties do so at their own risk
If Outlook didn't set a property, it's probably not a good idea to manufacture it. And if you do decide to go that route, you better be prepared to crack open the Exchange Protocol documentation and spend a good long time making sure you get everything right. In this case, the customer got out of the property manufacturing business and their meeting started working as expected.
In both cases, as in most message store cases (see this and this), the advice boils down to "if your store acts weird, so will Outlook". Hopefully though, these lessons will help a few of you identify the source of weird behavior in your own providers.
Duncan Smith noted recently that MAPI appears to have a problem with IPv6. He contacted me to see if I knew anything about the issue. I didn't, but one of the Exchange PMs spotted the connection between Duncan's issue and the recently reported DSProxy issue over on the Ehlo blog. It turns out that issue affects regular MAPI connections - not just RPC/HTTP.
The gist is - right now, DSProxy doesn't listen on IPv6. It only listens on IPv4. So if you're running a MAPI client on an IPv6 enabled box (such as, say, installing the MAPI download on an IPv6 enabled Exchange 2007 server), then profile creation is going to fail when the client tries to ask the server for a referral to a GC.
As the Ehlo article notes, a fix is in the works. Until then, you can try one of the following:
We just released an update to our calendar rebasing library, Tzmovelib.dll. This update includes a refresh to the documentation. One thing that didn't make it into this refresh was an updated sample and documentation for a new exported function, primarily because I didn't have it ready in time. So I'm documenting the new function here, and it will make it into the MSDN the next time we refresh the docs.
We've added a new export to tzmovelib.dll in this refresh. It's name is HrCreateApptRebaserEx. It functions exactly the same as HrCreateApptRebaser unless a new flag, REBASE_FLAG_PHYSICAL_MOVE is passed. In that case, we use the new pTZFrom parameter to effect a rebase from an old time zone to a new time zone. Additionally, we renamed the pftInstallDateUTC parameter to ptfRebaseExpireUTC, and the REBASE_FLAG_ONLY_CREATED_PRE_PATCH flag to REBASE_FLAG_ONLY_REBASE_BEFORE_EXPIRE to reflect that this parameter and flag apply to physical moves as well. The behavior of this parameter and flag did not change.
The HrCreateApptRebaserEx function initializes a IOlkApptRebaser object for use in rebasing appointments.
const FILETIME *pftRebaseExpireUTC,
const TZDEFINITION *pTZFrom,
const TZDEFINITION *pTZTo,
const TZDEFINITION *pTZMissing,
ulFlags [in] Bitmask of flags used to control how rebasing is performed. The following flags can be set:
Items in which the user is the meeting organizer will be rebased. Note that by default this will cause meeting updates to be sent out to all attendees of any meeting being rebased. Combine this flag with either REBASE_FLAG_FORCE_NO_EX_UPDATES or REBASE_FLAG_FORCE_NO_UPDATES to change the behavior of how meeting updates are handled.
Update items that aren't marked with a time zone. If this flag is specified, the pTZMissing value passed to HrCreateApptRebaser will be used as created-in time zone for all items that do not have time zone data.
Update only recurring items.
Do not show any UI (prevents the display of any logon dialog boxes when opening the message store).
Do not rebase items that will only have changes which occur in the past.
Do not check the organizer for rebasing decisions (enables items in which the user is the attendee to be rebased).
Only send updates if the user is the organizer and recipient is non-EX.
Never send updates.
Only rebase single instance items created before a specified date, which is either a patch install date or a physical move date.
Do not actually rebase, just report items that would be rebased.
Send meeting updates to resources (attendees explicitly specified on the "Resource" line of a meeting).
Don't skip rebasing of unaffected future recurring appointments for physical move. Only valid for HrCreateApptRebaseEx. If this flag is set, pTZFrom must be a valid time zone definition representing the "from" time zone and pTZTo then represents the "to" time zone. If this new flag is not set, then pTZFrom must be NULL, and HrCreateApptRebaseEx functions exactly the same as HrCreateApptRebase. REBASE_FLAG_PHYSICAL_MOVE is not valid with REBASE_FLAG_UPDATE_ONLYRECURRING. REBASE_FLAG_PHYSICAL_MOVE implies REBASE_FLAG_UPDATE_UNMARKED and REBASE_FLAG_UPDATE_MINIMIZEAPPTS.
pSession [in] Required. Pointer to a MAPI session interface.
pCalendarMsgStore [in] Required. Pointer to a message store containing items to be rebased.
pCalendarFolder [in] Require. Pointer to a Calendar folder containing items to be rebased.
pwszUpdatePrefix [in] Optional. Pointer to a string containing the prefix to be prepended on meeting requests. May be NULL.
pftRebaseExpireUTC [in] Optional. Only rebase items before this date. Only used if the REBASE_FLAG_ONLY_REBASE_BEFORE_EXPIRE flag is set.
lExpansionDepth [in] Expansion depth when expanding distribution lists to exclude EX recipients. Only used if the REBASE_FLAG_FORCE_NO_EX_UPDATES flag is set.
pTZFrom [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be rebased from.
pTZTo [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be rebased to.
pTZMissing [in] Required. Pointer to a TZDEFINITION structure describing the time zone to be assumed if time zone information is not stamped on an item. Must not be NULL, but only used if the REBASE_FLAG_UPDATE_UNMARKED flag is set.
ppError [out] Optional. Pointer to a pointer to a MAPIERROR structure containing version, component, and context information for the error. Can be NULL if no extended error information is desired. Free with MAPIFreeBuffer.
ppApptRebase [out] Pointer to a pointer to the returned IOlkApptRebaser interface.
Obtain this function from tzmovelib.dll by calling GetProcAddress with the function name HrCreateApptRebaserEx@48. When REBASE_FLAG_PHYSICAL_MOVE and pTZFrom are not passed, this function is identical in behavior to HrCreateApptRebaser.
Not all of the flags are valid in combination with each other. View the "Glossary of command-line options for the Outlook Time Zone Data Update tool" section of http://support.microsoft.com/kb/931667 for more information on the various options.
The August 2008 Release (build 126.96.36.1998) is live: http://www.codeplex.com/MFCMAPI
I'm continuing to dig through the protocol docs (now on version 1.01). Last release was about getting the plumbing in place for Smart Views. This release is all about parsing structures, most especially, parsing for the recurrence blob on Calendar and Task items. Here's the change list - see the Issue Tracker on Codeplex for more details, or look at the code:
Ed has asked if a few of us could help out with his survey by posting a link to it here. I'm happy to oblige:
Greetings Blog Readers, My name is Ed Jolly, and I am a director in the Commercial Technical Support (CTS) organization at Microsoft. I am here to request a few minutes of your time. We would like to learn more about blog readership through a brief survey. This is an opportunity for us to better understand what is valuable to you and what you would like to see in the future. Below is a link that will take you to another website to complete the survey. Based on what we learn, we may request more feedback in future surveys like this. When you open the survey, you will see a list of blogs that CTS engineers contribute to across many different products. We have not posted a listing of these blogs in the past, and I hope it helps you find other blogs that are helpful to you. The blog survey is completely anonymous. · Location: http://www.tsisurveys.com/mssurveys/blog/index.asp · Availability: Until August 22. You may receive a request to complete this survey through multiple RSS feeds. You need only to complete it one time. · Length: The survey can be a maximum of 11 questions. · Time: Less than 5 minutes (but providing more information in the open text fields may take a minute or two extra, improving our ability to understand your needs in these blogs). Thank you in advance for your time, participation and assistance. Ed Jolly (firstname.lastname@example.org)
Greetings Blog Readers,
My name is Ed Jolly, and I am a director in the Commercial Technical Support (CTS) organization at Microsoft. I am here to request a few minutes of your time.
We would like to learn more about blog readership through a brief survey. This is an opportunity for us to better understand what is valuable to you and what you would like to see in the future.
Below is a link that will take you to another website to complete the survey. Based on what we learn, we may request more feedback in future surveys like this. When you open the survey, you will see a list of blogs that CTS engineers contribute to across many different products. We have not posted a listing of these blogs in the past, and I hope it helps you find other blogs that are helpful to you.
The blog survey is completely anonymous.
· Location: http://www.tsisurveys.com/mssurveys/blog/index.asp
· Availability: Until August 22. You may receive a request to complete this survey through multiple RSS feeds. You need only to complete it one time.
· Length: The survey can be a maximum of 11 questions.
· Time: Less than 5 minutes (but providing more information in the open text fields may take a minute or two extra, improving our ability to understand your needs in these blogs).
Thank you in advance for your time, participation and assistance.
Ed Jolly (email@example.com)
We had a customer recently who was sending mails with Outlook's MAPI. They wanted to know how to force the message to be plain text, like it is in Outlook when you select Send Plain Text only in the properties for the recipient:
They were using CreateOneOff to create their recipient, and they were passing MAPI_SEND_NO_RICH_INFO. Yet, if the message they were sending contained an attachment, such as a Word document, the content type of the attachment on the received mail was always application/ms-tnef instead of application/msword. In other words, they were still getting a winmail.dat attachment. If they addressed a mail in Outlook with the Send Plain Text only option, sending the same attachment, it came across without the TNEF.
One thing to note right off the bat is that sending rich text (RTF) data and sending TNEF (winmail.dat) are actually two different concepts. RTF data describes the formatting of the text of the message, much in the same way HTML can also describe the formatting of text. TNEF is a mechanism for encapsulating MAPI properties that Exchange and Outlook do not convert natively into MIME format. One such MAPI property commonly encapsulated in TNEF is PR_RTF_COMPRESSED. But TNEF can encapsulate many more properties than just PR_RTF_COMPRESSED, such as properties dealing with certain attachments. So the presence of winmail.dat doesn't necessarily mean rich text is being transmitted. That's what's happening in this case.
If you're trying to avoid TNEF completely, MAPI_SEND_NO_RICH_INFO is a step in the right direction, but doesn't actually tell the content converter not to use TNEF - it just tells it not to use RTF. To avoid TNEF altogether, we need to set some more flags. But which flags? We can look at what CreateOneOff generates to find out. Fortunately, we don't need to reverse engineer the structure - it was recently documented in section 188.8.131.52 of the following Exchange Protocol Documentation: [MS-OXCDATA].pdf
Now - the documentation's in "bytes on the wire - what's a constant?" format, but with a little elbow grease, we can produce the following structure:
} ONEOFFEIDHEADER, FAR *LPONEOFFEIDHEADER;
which we can map our entry ID to so we can check which flags are set. The following flags are valid (determined by matching the docs up to MAPI's headers):
#define MAPI_SEND_NO_RICH_INFO ((ULONG) 0x00010000)
#define ENCODING_PREFERENCE ((ULONG) 0x00020000)
#define ENCODING_MIME ((ULONG) 0x00040000)
#define BODY_ENCODING_HTML ((ULONG) 0x00080000)
#define BODY_ENCODING_TEXT_AND_HTML ((ULONG) 0x00100000)
#define MAC_ATTACH_ENCODING_UUENCODE ((ULONG) 0x00200000)
#define MAC_ATTACH_ENCODING_APPLESINGLE ((ULONG) 0x00400000)
#define MAC_ATTACH_ENCODING_APPLEDOUBLE ((ULONG) 0x00600000)
#define OOP_DONT_LOOKUP ((ULONG) 0x10000000)
With this, we can see that setting the Plain Text Only option in Outlook translates to setting the ENCODING_PREFERENCE and ENCODING_MIME flags. Unfortunately, the CreateOneOff function doesn't let you pass those flags. If you try, you'll get MAPI_E_UNKNOWN_FLAGS. We won't let that stop us though - we can use our structure to set the flags as well as read them!
Here's how we can put this all together and get a one-off entry ID which will send without TNEF:
} ONEOFFEIDHEADER, FAR *LPONEOFFEIDHEADER;
hr = lpAddrBook->CreateOneOff(
LPONEOFFEIDHEADER lp1Off = (LPONEOFFEIDHEADER) (ENTRYID*) lpEntryID;
if ((lp1Off->dwBitmask & MAPI_SEND_NO_RICH_INFO))
lp1Off->dwBitmask |= (ENCODING_PREFERENCE | ENCODING_MIME);
[Update: 8/4/2008 5:45 - rewrote the RTF/TNEF paragraph to make it clearer][Update: 8/4/2008 9:36 - realized I hadn't proofread the code - fixed it]
Sorry for not posting this sooner - I just found out about it today. As I noted previously, we recently fixed the MAPI download to work on Vista and Windows Server 2008. This broke some ISVs who weren't expecting Exchange's MAPI to use the MAPI stub library. So, to give ISVs time to validate their applications with the new download, we've reposted the old one. Now there are two download pages:
Note that the older installer is only being offered until August 15th.
Update: The pre-Vista package has been taken down. See Pre Vista Fix MAPI Download No Longer Available.
One of our customers reported that if they used IConverterSession::MIMEToMAPI to generate an MSG file, then opened the MSG file and hit Forward, the body would be gone. Further investigation revealed more funkiness. Reply worked, but if you tried Forward, closed the forwarded message, then tried Reply again, the body would be missing there as well. I could also repro the problem with some messages I saved to MSG from MFCMAPI.
If you've read my other article on MSG files you know this behavior doesn't surprise me. I did file a bug on this, but it got rejected since it turns out there's an easy workaround for the case my customer was hitting.
The problem basically comes down to the best body logic as it applies to an MSG file. All of the MSG files we were dealing with here had PR_BODY_HTML set, not PR_RTF_COMPRESSED. This is fine when we initially render the message, as well as when we re-render for the reply. But when we render for the forward, we go through a different code path that hinges on getting PR_STORE_SUPPORT_MASK from the store in which the message lives. But MSG files don't live in a message store. So we don't get the prop, and the code path fails to render the body. Along the way, it also clears some bits that were allowing the reply case to work, so it now fails.
MSG files saved from Outlook didn't exhibit the problem - they had PR_RTF_COMPRESSED set in them instead of PR_BODY_HTML. That sends us through yet another code path gets much more testing and doesn't have these problems. And this is the key to the workaround: When using MIMEToMAPI to generate MSG files, always include the CCSF_USE_RTF flag. This will write the body into the PR_RTF_COMPRESSED property (even if it's HTML), avoiding the problem.
There's a bunch of us here that work in developer support for the Messaging APIs that actively blog. We've been kicking around the idea of putting together a team blog, but no one wanted to give up our own blogs. So we've come up with what I think is a good compromise. Most of us use blogs.msdn.com, which has a tags url allowing you to search for a tag across all blogs. To take advantage of this, we've tagged all of our posts with the tag DevMsgTeam. Now all of our post will show up together here:
With the corresponding RSS feed:
So there - now we've got a "team" blog. Enjoy! :)
One of my customers reported that using the MAPI download, they were unable to get a referral from Exchange 2007, even after following the instructions in Jason Johnston's article. The same code worked if they were targeting an Exchange 2003 machine, and Outlook's MAPI had no problems getting a referral from Exchange 2007.
A little bit about how we connect to the DC Exchange's MAPI (this description does NOT apply to Outlook's MAPI, which has different logic, and leaves out a few details not relevant to the discussion, such as how we react when offline):
We're focused on the referral step. The referral request is a Remote Procedure Call (RPC), so we have to set up for the RPC, select one of the available RPC bindings, set our authentication mechanism, and finally make the call. Given default settings, this means we'll be using the default authentication mechanism for RPC, RPC_C_AUTHN_WINNT. This is where we run in to trouble.
During the development of Exchange 2007, a few of the RPC interfaces were moved around. This created the need to consolidate how authentication was handled among the various interfaces. RPC_C_AUTHN_WINNT was cut as an authentication mechanism for the RPC interface which handles referrals, leaving RPC_C_AUTHN_GSS_NEGOTIATE as the preferred authentication mechanism. For the most part, this change had little effect. Outlook did not use RPC_C_AUTHN_WINNT as an authentication mechanism for the referral RPC, and since a failure to get a referral defaults to binding to the Exchange server, most clients didn't see a noticeable impact.
Given all the settings we read in, including the tantalizingly named PR_PROFILE_AUTH_PACKAGE, I wondered if there was any way we could work around this and get Exchange's MAPI to ask for a referral. Investigation of our source confirmed that PR_PROFILE_AUTH_PACKAGE did take an RPC authentication mechanism constant as it's value, so we could set it to RPC_C_AUTHN_GSS_NEGOTIATE (this article discusses this as well).Testing showed that didn't work though. The address book code was convinced it didn't need special authentication, so it wouldn't set the authentication mechanism I had specified.
More digging led to PR_PROFILE_CONNECT_FLAGS. If the CONNECT_NO_RPC_ENCRYPTION flag was set in this property, we'd assume we didn't need special authentication. So I tried removing this flag. Of course, this didn't work either. In fact, although I could remove the CONNECT_NO_RPC_ENCRYPTION flag, every time I tried to connect, it got set back again!
Even more digging. Basically, the if EDK_PROFILEUISTATE_ENCRYPTNETWORK flag wasn't set in PR_PROFILE_UI_STATE, we'd add CONNECT_NO_RPC_ENCRYPTION back to PR_PROFILE_CONNECT_FLAGS. Guess what? Setting the EDK_PROFILEUISTATE_ENCRYPTNETWORK flag didn't work either! But I was getting much closer: the error changed.
Previously, the RPC call had been failing with RPC_S_SERVER_UNAVAILABLE. This was because the authentication mechanism was valid, but the RPC interface we were trying to call to hadn't registered for that authentication mechanism. After I made all my settings, when we called RpcBindingSetAuthInfo we got RPC_S_UNKNOWN_AUTHN_SERVICE back. I had been testing everything locally on my Exchange 2007 server, so all RPC calls to the server used local RPC. Since RPC_C_AUTHN_GSS_NEGOTIATE isn't a valid authentication mechanism for local RPC, the RPC layer wouldn't let us use it. This was excellent news. It meant I had succeeded in forcing Exchange's MAPI to attempt a different authentication method!
I knew the customer always ran their MAPI code remotely anyway, so I tested my settings from a MAPI client on a different machine. Whereas without my settings we were always binding directly to the Exchange server for address book lookups, with the new settings, we asked for and got a referral, then bound to the GC!
Here's a summary of the settings you'll want to make on your MAPI profiles if you want to get GC referral and reconnect working against Exchange 2007. These settings will also work when connecting to Exchange 2003, though only the properties from Jason's article are strictly necessary:
In the MUIDEMSAB profile section (dca740c8c042101ab4b908002b2fe182):
In the global profile section (13DBB0C8AA05101A9BB000AA002FC45A):
I tested all of these settings using MFCMAPI, following the outline of the steps I gave here.
BTW - Global Catalog servers still lie on shutdown, so you'll also want to enable the EMSAB_UserAuth_Credentials key if that's a concern. That code was incorporated into the MAPI Download a long time ago.
The latest MAPI download has an interesting fix in it. Prior to this fix, if you used Exchange's MAPI to connect to Exchange 2007 running on Windows Server 2008 you might crash when you release your message store. If Exchange was running on Windows Server 2003, the same code ran fine. At first, I didn't really believe the version of the OS on the server could trigger a crash on the client, but he had dumps and repro steps, so we debugged it to see what was going on.
Nothing was wrong with his code - we were able to build a simple repro that did nothing more than logon, open the message store, open a folder then release everything. We found some tweaks they could make in their code to avoid the crash, but nothing they were doing was wrong - we even found that MFCMAPI could be walked through to the point of crashing.
The dump of the crash showed we were cleaning up a thread that handled notifications. When we ran the code against an Exchange 2007 server running on Windows 2003, this thread didn't get cleaned up. In fact, it never got created! A bit of debugging back showed the point where we diverged. In both, we tried to register for push notifications with Exchange (these are MAPI's typical UDP based notifications). The Windows Server 2003 based machine succeeded in registering the push notification, but the Windows Server 2008 based machine returned MAPI_E_NO_SUPPORT. This sent us down a little used code path in Exchange's implementation of MAPI. It spun up a timer thread and began polling for notifications. When it tried to tear down this thread, released an object that had been created on another thread. This object had initialized COM, so it tried to uninitialize it when it's destroyed. Since the COM was never initialized on the timer thread, we crashed.
The fix, of course, is to ensure that the object that uses COM is created and destroyed on the same thread. That's the fix we put into last months MAPI download - so if you've been getting unusual crashes while shutting down MAPI clients connected to Exchange 2007 servers on Windows 2008, you'll want to grab this update. A KB article documenting the fix is in the works - I'll link it when it's ready. KB 951992 documents the fix.
Now - you may be wondering - what was happening on the server side? This thread in the newsgroups explains it. EnumProtocols is broken in both Windows 2008 and Vista. It's been replaced by WSAEnumProtocols, which is easy enough to drop in, but of course, you'll need to recompile. We've got a fix for Exchange 2007 in the works that replaces it's call of EnumProtocols with a call to WSAEnumProtocols. If you need it now, you can open a support case and ask for an interim update (ask for KB 951251). Else, it's scheduled to be in Update Rollup 4 for Exchange Server 2007 Service Pack 1 (not the next rollup, but the one after that).
[This is now documented here: http://msdn.microsoft.com/en-us/library/ff976789.aspx]
We've decided to document another function in the Account Management API, DisplayAccountList. This function allows you to display the Account Settings dialog:
and Add New E-Mail Account property sheet:
DisplayAccountList hangs off of IOlkAccountManager interface, occupying the second slot in the v-table, like so:
interface IOlkAccountManager : IOlkErrorUnknown
//Init Initializes the account manager for use.
virtual STDMETHODIMP Init(IOlkAccountHelper* pAcctHelper, DWORD dwFlags);
//DisplayAccountList Displays the account list wizard
virtual STDMETHODIMP DisplayAccountList(
LPCWSTR lpwszReserved, // Not used
DWORD dwReserved, // Not used
const CLSID * pclsidReserved1, // Not used
const CLSID * pclsidReserved2); // Not used
I'm working on a little sample to demonstrate the Account Manager API that I hope to post soon. It'll have an updated header. In the meantime, here's my attempt at MSDN style documentation for the function:
Initializes the account manager for use.
HRESULT IOlkAccountManager::DisplayAccountList (
const CLSID * pclsidReserved1,
const CLSID * pclsidReserved2
The client calls IOlkAccountManager::DisplayAccountList to display either the Account Settings dialog or the Add New E-mail property sheet. The parameters dwReserved, pclsidReserved1 and pclsidReserved2 are not used at this time and MUST be NULL. The parameter lpwszReserved is not used and SHOULD be NULL.
#define E_ACCT_UI_BUSY 0x800C8102
#define ACCTUI_NO_WARNING 0x0100
#define ACCTUI_SHOW_DATA_TAB 0x0200
#define ACCTUI_SHOW_ACCTWIZARD 0x0400
BTW - Hey look! Pictures! Woo hoo!
That's right - version 1.0 of the Exchange Server Protocol Documentation has been published in the MSDN:
Here's the press release:
And the page with links to all the licenses if you want to make use of the documentation:
As noted before, support for this documentation is conducted through the forums. Enjoy
In a comment on my post about the MAPI download working with Vista, JP pointed out that something in the updated DLLs is changing the current working directory. I got a chance to look at this today and here is what I found: this is the MAPI Stub's doing. In order to support some wonky old MAPI implementations, the stub library will change the current working directory to the directory of the MAPI implementation it's loading. As far as I can tell, the stub has always done this. It's only being noticed with Exchange's MAPI now because now Exchange's MAPI can be loaded through the stub library.
There's two ways to work around this if it's causing problems in your apps. One is to realize the directory might be changed on you and change it back:
The other way to work around the issue is to load Exchange's MAPI directly. You can pull the path from the same key the stub library uses (HKEY_LOCAL_MACHINE\SOFTWARE\Clients\Mail\ExchangeMAPI::DLLPathEx) and load the DLL yourself. If you're already dynamically loading MAPI this wouldn't be a major change. If you're statically linking mapi32.lib, then you'll need to use the first workaround.
I've given this solution to a couple customers so far and it appears to be working for them, so I thought I'd share it with the world.
The Problem: You've written an application which uses IExchangeExportChanges::Synchronize to synchronize data between your back end database and Exchange. This application is typically deployed directly on an Exchange server, or a machine where the MAPI download has been installed. Occasionally, folder will have items in them which cannot be read through MAPI - typically these are messages which arrived via SMTP and which fail content conversion. From Outlook, you can usually move these messages around, but you can't open them. When the Synchronize method processes a folder containing one of these messages, it returns MAPI_E_CORRUPT_DATA and the whole folder sync is aborted.
Before we can discuss workarounds, we need to understand the problem: The client sets up to do a sync between the client and the server, possibly using IExchangeExportChanges::Config. Then the synchronization is started. During the synchronization, the client will ask the server to fill buffers with data. The server will fill these buffers with data representing the messages on the server. If the server has trouble dealing with one of the messages, for any reason, an error state is entered. If this error state is not resolved, then the entire synchronization operation fails. Exchange's MAPI doesn't know how to resolve the error state, so we fail.
Given that Exchange's implementation of IExchangeExportChanges has worked this way for over a decade, and that Exchange's MAPI implementation enters Extended support next year, there's little chance we'd be able to implement logic for resolving the error.
The Workaround: If you dig around in edkmdh.h, you'll find an interesting interface: IExchangeExportChanges3, which introduces ConfigForSelectiveSync. This function is identical in use to Config except it adds a new parameter, lpMsgList. What this parameter allows us to do is to tell the synchronizer which messages we want it to sync. Now - we'll still have the problem that if the set of messages we've asked to synchronize includes one of these bad messages, we'll get MAPI_E_CORRUPT_DATA from Synchronize. But we can apply a little binary search style logic to get around the problem. Here's the logic:
Of course, we could skip straight from step 1 to step 3, but on very large folders synchronizing a single message at a time could be rather slow. In fact, it might make sense to use even larger batches, say 1/10th the size of the folder. Then, if a batch fails, split it up into tenths and iterate until a single message at a time is being synchronized.
The Other Workaround: If you're doing the synchronization manually using the Exchange Server Protocol Documentation (and that's a big IF, as it ain't easy), then you can implement code to resolve the error condition. The place to start in that document is section 184.108.40.206.4 errorInfo.