I wrote these rules out while debugging a crash in another MS product:
I won't name the app, but it violated all 4 rules.
Known consequences of violating these rules:
Stephen, great article. How would you handle using Outlook 2000 MAPI in multithreated enviroment? Outlook 2000 MAPI doesn't support NoCoInit flag - which makes it imposible to use in in threads. Jan
Actually, MAPI_NO_COINIT just tells MAPI not to call co-initialize (http://support.microsoft.com/kb/239853). This only relates to multithreading IF you're also using COM and want to initialize it with COINIT_MULTITHREADED.
So the answer there is: If you want to use Outlook 2000's MAPI in a multithreaded environment, don't use COM. :)
Ok - not exactly. But there are a few things you need to configure on your SQL box if you want SQLMail
Maybe you can help me this...
Here is my case: In my COM Object, hosted in a COM+ application, I call MAPIInitialize in the FinalConstruct method (that happens in thread A). This object can be pooled (I allow object pooling to optimize MAPI connections) so this object is reusable. Next time the object is used it probably used from thread B. When the object is disposed that probably happens in thread C.
My previous logic was to call MAPIUninitialize when the object was destroyed (thread C). As you can see this violates a few rules. As expected, I am having weird problems, like MAPIInitialize starts to fail after a few minutes, hours, days or never. When it happens the solution is to restart the CAS server.
My key implementation problem here is I don't have a way to control when the threads are created or destroyed to initialize/uninitialize MAPI. The only place I can have an idea of the life-time of a thread is in the WinMain function of my hosting DLL, but that is not a good place to call MAPI Initialize/Uninitialize. Right?
My plan B solution is: When an object is created I check if MAPI is not initialized in the current thread (using TLS) and then I Initialize MAPI. When an object is destroyed I check if MAPI was initialized in the calling thread (using TLS) and then I uninitialize MAPI. So far it is working, but I am not sure what is going to happen tomorrow.
Question: If a thread that initialized MAPI finishes without uninitializing MAPI, what happens? The application leaks memory?
I know this is complicated, but if you can drop any idea, I will appreciate it.
PD: this is how I initialize MAPI
mapicfg.ulVersion = MAPI_INIT_VERSION;
mapicfg.ulFlags = MAPI_NO_COINIT | MAPI_MULTITHREAD_NOTIFICATIONS | MAPI_NT_SERVICE;
HRESULT hr = MAPIInitialize(&mapicfg);
>Question: If a thread that initialized MAPI finishes without uninitializing MAPI, what happens? The application leaks memory?
Or worse - you could end up with a crash since MAPI as a whole might still think that thread is valid.
Lets say in every method of my service objects I initialize MAPI, do some work, then uninitialize MAPI.
My point (prev. post) was to have a bunch of service objects to optimize MAPI connections (with COM+ object pooling). If I initialize MAPI, create a some MAPI objects (e.g. a session), if after that I uninitialize MAPI my MAPI objects will be hanging, no?
It is hard to follow the 4 rules, I am trying really hard here. :)
?? The rules shouldn't be that hard to follow. If you initialize MAPI, uninitialize it. And of course, at the point where you're uninitializing MAPI, you should have already cleaned everything up - so there wouldn't be any objects "hanging around".
Can I initialize MAPI twice? (and then uninitialize then twice) on ine thread.
There is my problem: after all work, when application completed work (MAPI had uninitialized) my App crashed on some thread with with stack:
[Frames below may be incorrect and/or missing, no symbols loaded for EMSMDB32.DLL]
Can a single thread call MAPIInitialize and then MAPIUninitialize and then again call MAPIInitialize and then MAPIUninitialize?
MFC - yes, technically that's OK, though in practice I wouldn't recommend it. Most versions of MAPI have leaks of some form or the other that only surface when you call MAPIInitialize/MAPIUninitialize in a loop like that.
Thanks for the reponse. I have an object that calls MAPIInitialize in the constructor and MAPIUninitialize in the deconstructor. Another app creates and destroys the object. The second time the object is created from the same instance of the application there is a failure to open the message store on Open Message Store. Would my workflow be the cause of this?
MFC - I'm working with the owner of the case you opened. He'll send you instructions for gathering some data to figure out the cause of the failure.
We had an issue recently where DDE broadcasts were being blocked on a system. The customer noticed that
We have an Exchange server gateway (based on the old EDK sample code) which only calls MapiInitialize/Uninitialize on the main Windows message processing thread. It doesn't use MAPI_MULTITHREAD_NOTIFICATIONS. Multiple worker threads in the gateway use MAPI calls.
With reference to your rule & consequence #1, we do not see any MAPI call failures in the worker threads - I don't understand why it works for us.
Can you explain what benefit calling MAPIInitialize on each thread may have in our multi-threading gateway?
I'm asking because we can see that there's apparently a lot of contention in our worker threads on MAPI operations. Randomly breaking into the application inevitably shows multiple threads waiting inside MAPI operations on a critical section and only 1 thread that's doing some rpc operation in the depths of MAPI.
Somebody must be calling MAPIInitialize for you on the worker threads. That or you've been very lucky.