Addin mulithreading

  • Thread starter Thread starter Semut
  • Start date Start date
S

Semut

Hello,

I am thinking of spawning a thread of processing the MAPIFolder selected
everytime when the BeforeFolderSwitch was trigger. I overheard that Outlook
VBA/OOM is a single threaded, is it true? And even if it is single threaded,
can I do a multhreading programming? Any precautions that I need to look
into or anyone has done it in the past. Reason I want to do this is my addin
need to response to the network, sometimes there are some lagging occur in
the network.

By the way, I am using Redemption and extended MAPI too. Do these have issue
on the multi-threading?

thanks.
 
All Outlook objects are apartment threaded, so all the calls to the OOM
objects from a secondary thread will be marshlled to the main Outlook
thread.
You can of course do non-OOM processing on the secondary thread.
Extended MAPI and Redemption are thread-safe, but your mileage may vary of
course.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Ok, supposing I spawn my secondary thread and it will connect to network and
update the MAPIFolder of the Outlook using OOM. In my main thread, the
Outlook itself, I will probably do some work like adding items to the
MAPIfolder manually. (probably the same MAPIFolder points to as the
MAPIFolder spawned in the thread.)

Do I need to call

::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);and ::CoUninitialize();in
my secondary thread so that it will create two standard single threaded
apartments?(or it is not necessary?)How to do about?thanks
 
You must call CoInitializeEx (or CoInitialize) in your secondary threads if
you are using COM on that thread.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Semut said:
Ok, supposing I spawn my secondary thread and it will connect to network and
update the MAPIFolder of the Outlook using OOM. In my main thread, the
Outlook itself, I will probably do some work like adding items to the
MAPIfolder manually. (probably the same MAPIFolder points to as the
MAPIFolder spawned in the thread.)

Do I need to call

::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);and ::CoUninitialize();in
my secondary thread so that it will create two standard single threaded
apartments?(or it is not necessary?)How to do about?thanks
Of course you need CoInitialize but single threaded or multi threaded
depends what else compomets do you plan to use from that thread.
So your thread will work independently to main Outlook thread.
But when you call some OOM methods your thread will stop and wait while
system switch thread context to the main thread and call requested method in
the context of the main thread. After returning from OOM the call you thread
againg works on its own.

The only you need is to obtain pointers to the OOM objects in the proper way.

CoCreateInstance will do.
If it is existing object you shoud marshal it manualy between main thread an
the worker thread.
 
I will be utilizing the global variable of Outlook::_Application in my
worker thread.

Then, through the Application object, I obtain the Outlook::_Namespace
variable and then the MAPIFolder.

This is to avoid the security alert by Outlook.

Will it be fine that way?

Also, I will be passing a MAPIFolder to my worker thread function by

myCall( spMAPIFolder.Detach());

so that my worker thread will release the ref count it self. It this ok?


Ok, my worker thread function do call.

::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

....

::CoUninitialize();

Previously, without calling these, the functions in the thread, especially
those CoCreateInstance will fail.

The only you need is to obtain pointers to the OOM objects in the proper
way.

CoCreateInstance will do.
If it is existing object you shoud marshal it manualy between main thread
an
the worker thread.

Can you show a code snippet for that?
Supposingly, I need to pass a MAPIFolder to my worker thread. How do I
marshal or use the CoCreateInstance. (I am using Redemption and Extendded
MAPI also, so you can show in Redemption also no problem, not bound to be
OOM only. )

At the moment, the MAPIFolder smart pointer (CComPtr < Outlook :: MAPIFolder
) give me problem when it try to Release the ref count when it goes out
of the scope. In a non multi-threaded environment, this call was fine but
when I switch to MT. The program will complain for error about the
MAPIfolder.
 
At the moment, I pass a MAPIFolder to my thread function like


callMyThreadFunc(spMAPIFolder.Detach());

The spMAPIFolder will get release in the thread function itself cause I pass
the ownership to a LPDISPATCH in the thread function.

So, the reading of the MAPIFolder have no problem.

The problem is when I want to add item. The smart pointer will complaint
exception (0x0eedfade). It is something to do with the MAPIFolder smart
pointer when it tries to release a ref count which has been released.
Previously, not in Multi Threaded environment, there wasn;t any of this
problems.

By the way,

in the my thread func.

I have

::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
......
.....
......
::CoUninitialize();



The only you need is to obtain pointers to the OOM objects in the proper
way.

CoCreateInstance will do.
If it is existing object you shoud marshal it manualy between main thread
an
the worker thread.

Can you show a code snippet for that. Say, I need to pass a MAPIFolder to my
thread function.

And, the MAPIFolder would problably need to be updated.



thanks
 
See some snipped
that is for Application pointer. ALL OOM pointers should be marshalled like
this.

MAPIFolder is thread safe so it can be easily passed between threads.


That peace of code from D.Box's book "Essential COM".
I've modified it a bit.

HRESULT WritePtr(Outlook::_Application *pApplication, HGLOBAL& rhglobal)
{
IStream *pStm = 0;
Çhglobal = 0;
// alloc and wrap block of memory
HRESULT hr = CreateStreamOnHGlobal(0, FALSE, &pStm);
if (SUCCEEDED(hr)) {
// write marshaled object reference to memory
hr = CoMarshalInterface(pStm, __uuidof(Outlook::_Application),
pApplication,
MSHCTX_INPROC, 0,
MSHLFLAGS_NORMAL);
// extract handle to underlying memory
if (SUCCEEDED(hr))
hr = GetHGlobalFromStream(pStm, &rhglobal);
pStm->Release();
}
return hr;
}

HRESULT ReadPtr(HGLOBAL hglobal, Outlook::_Application* &pApplication)
{
IStream *pStm = NULL; pApplication = NULL;
// wrap block of existing memory passed on input
HRESULT hr = CreateStreamOnHGlobal(hglobal, FALSE, &pStm);
if (SUCCEEDED(hr)) {
// get a pointer to the object that is legal in this apt.
hr = CoUnmarshalInterface(pStm, __uuidof(Outlook::_Application),
(void**)&pApplication);
pStm->Release();
}
return hr;
}

So you write pointer in the main thread and obtain HGLOBAL handle
pass that handle to the worker thread. In the thread call ReadPtr and obtain
marshalled pointer.
use GlobalFree to free HGLOBAL handle.



WBR
Henry
 
thanks.


Henry Gusakovsky said:
See some snipped
that is for Application pointer. ALL OOM pointers should be marshalled
like
this.

MAPIFolder is thread safe so it can be easily passed between threads.


That peace of code from D.Box's book "Essential COM".
I've modified it a bit.

HRESULT WritePtr(Outlook::_Application *pApplication, HGLOBAL& rhglobal)
{
IStream *pStm = 0;
?hglobal = 0;
// alloc and wrap block of memory
HRESULT hr = CreateStreamOnHGlobal(0, FALSE, &pStm);
if (SUCCEEDED(hr)) {
// write marshaled object reference to memory
hr = CoMarshalInterface(pStm, __uuidof(Outlook::_Application),
pApplication,
MSHCTX_INPROC, 0,
MSHLFLAGS_NORMAL);
// extract handle to underlying memory
if (SUCCEEDED(hr))
hr = GetHGlobalFromStream(pStm, &rhglobal);
pStm->Release();
}
return hr;
}

HRESULT ReadPtr(HGLOBAL hglobal, Outlook::_Application* &pApplication)
{
IStream *pStm = NULL; pApplication = NULL;
// wrap block of existing memory passed on input
HRESULT hr = CreateStreamOnHGlobal(hglobal, FALSE, &pStm);
if (SUCCEEDED(hr)) {
// get a pointer to the object that is legal in this apt.
hr = CoUnmarshalInterface(pStm, __uuidof(Outlook::_Application),
(void**)&pApplication);
pStm->Release();
}
return hr;
}

So you write pointer in the main thread and obtain HGLOBAL handle
pass that handle to the worker thread. In the thread call ReadPtr and
obtain
marshalled pointer.
use GlobalFree to free HGLOBAL handle.



WBR
Henry
 
Back
Top