Exchange Client Extension under Outlook 2002

V

Vladimir Werskov

Hello,

i am facing a strange problem with an Exchange Client Extension. Under
Windows XP (SP2) and Outlook 2002 (without SP, SP1, SP2, SP3) the extension
is behaving differently than on other configurations.
The extension implements the IExchExt, IExchExtMessageEvents interfaces and
the only events i am using are the OnSubmit and OnWriteComplete. Here i need
to access the message which is to be sent. Everything works perfectly until
the user uses the "Send To - Mail Recipient" (either from desktop, Windows
Explorer or any other shell application). If the user sends the message, the
explorer (or the process which has started the Send To function) hangs (does
not respond anymore).
I have narrowed down the problem to one method call, which causes the
problem. Actually when the message is requested using the
lpExchangeCallback->GetObject, the explorer hangs. If i uncomment this call,
everything is working perfect. If i explicitly release the returned pointers
(lpMessage, lpMdb), then also everything is working.
I haven't found any example code or documentation which requires explicit
release on those pointers! And i even do not know whether i should call it or
what are the side effects? Is Outlook 2002 calling an additional AddRef, but
Outlook 2003 not? What can be the difference.

To demonstrate the problem i have modified the sample code from
http://support.microsoft.com/kb/285999
I have added the IExchExtMessageEvents interface to it.

Here is the source code:
// SimplestExt.cpp : Defines the entry point for the DLL application.
//

#include <STDIO.h>
#include <WINDOWS.H>
#include <COMMCTRL.H>
#include <MAPIX.H>
#include <MAPIUTIL.H>
#include <MAPIFORM.H>
#include <INITGUID.h>
#include <EXCHEXT.H>

#include "stdafx.h"

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID
lpReserved)
{
return TRUE;
}

//Declarations
class CMyExt:public IExchExt, IExchExtMessageEvents
{
public:
CMyExt() { refcount = 0; };
~CMyExt() {};

//Methods of IUnknown
inline STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj)
{
*ppvObj = NULL;
IUnknown* punk = NULL;

if ((IID_IUnknown == riid) || (IID_IExchExt == riid))
{
punk = (IExchExt*) this;
}
else if (riid == IID_IExchExtMessageEvents)
{
punk = (IExchExtMessageEvents*)this;
}
else
return E_NOINTERFACE;

if (NULL != punk)
{
*ppvObj = punk;
AddRef();
}
return S_OK;
}

inline STDMETHODIMP_(ULONG) AddRef()
{
refcount++;
return refcount;
}

inline STDMETHODIMP_(ULONG) Release()
{
ULONG ulCount = refcount--;
if (!ulCount)
{
delete this;
}
return ulCount;
}

//This is one method of IExchExt.
STDMETHODIMP Install(IExchExtCallback *pmecb, ULONG mcontext, ULONG ulFlags);

STDMETHODIMP OnRead(IExchExtCallback *lpExchangeCallback);
STDMETHODIMP OnReadComplete(IExchExtCallback *lpExchangeCallback, ULONG
ulFlags);
STDMETHODIMP OnWrite(IExchExtCallback *lpExchangeCallback);
STDMETHODIMP OnWriteComplete(IExchExtCallback *lpExchangeCallback, ULONG
ulFlags);
STDMETHODIMP OnCheckNames(IExchExtCallback *lpExchangeCallback);
STDMETHODIMP OnCheckNamesComplete(IExchExtCallback *lpExchangeCallback,
ULONG ulFlags);
STDMETHODIMP OnSubmit(IExchExtCallback *lpExchangeCallback);
STDMETHODIMP_(VOID) OnSubmitComplete(IExchExtCallback *lpExchangeCallback,
ULONG ulFlags);

private:
ULONG refcount;
};

extern "C" _declspec(dllexport) LPEXCHEXT CALLBACK ExchEntryPoint(void);

LPEXCHEXT CALLBACK ExchEntryPoint()
{
return new CMyExt;
}

STDMETHODIMP CMyExt::Install(
IExchExtCallback *pmecb,
ULONG mcontext,
ULONG ulFlags)
{
return S_OK;
}

STDMETHODIMP CMyExt::OnRead(IExchExtCallback *lpExchangeCallback)
{
return S_FALSE;
}

STDMETHODIMP CMyExt::OnReadComplete(IExchExtCallback *lpExchangeCallback,
ULONG ulFlags)
{
return S_FALSE;
}

STDMETHODIMP CMyExt::OnWrite(IExchExtCallback *lpExchangeCallback)
{
return S_FALSE;
}

/////////////////////////////////////////////////////////
// When we get called here, MAPI has finished writing all the
// properties to the message, but the message has not been submitted yet.
/////////////////////////////////////////////////////////
STDMETHODIMP CMyExt::OnWriteComplete(IExchExtCallback *lpExchangeCallback,
ULONG ulFlags)
{
LPMESSAGE lpMessage = NULL;
LPMDB lpMdb = NULL;

MessageBox(NULL, "Open message", "Test Extension", MB_OK);
if ((SUCCEEDED(lpExchangeCallback->GetObject(&lpMdb, (LPMAPIPROP
*)&lpMessage)))&& (lpMessage))
{
if (lpMessage != NULL)
{
lpMessage->Release();
lpMessage = NULL;
}
if (lpMdb != NULL)
{
lpMdb->Release();
lpMdb = NULL;
}
}
MessageBox(NULL, "Finished", "Test Extension", MB_OK);

return S_FALSE;
}

STDMETHODIMP CMyExt::OnCheckNames(IExchExtCallback *lpExchangeCallback)
{
return S_FALSE;
}

STDMETHODIMP CMyExt::OnCheckNamesComplete(IExchExtCallback
*lpExchangeCallback, ULONG ulFlags)
{
return S_FALSE;
}

STDMETHODIMP CMyExt::OnSubmit(IExchExtCallback *lpExchangeCallback)
{
return S_FALSE;
}

VOID CMyExt::OnSubmitComplete(IExchExtCallback *lpExchangeCallback, ULONG
ulFlags)
{
}

Thank you for any advice.
Vladimir Werskov
 
D

Dmitry Streblechenko

What do you mwan by "If i explicitly release the returned pointers"? Do you
mean you keep them in global/class variables?
The only ECE object safe to keep in a global variable is IMAPISession.
Everything can only be used in the callback where you call
IExchExtCallback::GetObject etc.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
V

Vladimir Werskov

No, i do not want to cache anything. I just want to use the message inside
the OnWriteComplete method.

Every resource i have found is dealing with it like this:

LPMESSAGE lpMessage = NULL;
LPMDB lpMdb = NULL;

if ((SUCCEEDED(lpExchangeCallback->GetObject(&lpMdb, (LPMAPIPROP
*)&lpMessage)))&& (lpMessage))
{
}

No lpMessage->Release() or lpMdb->Release() is necessary. And it is working.

However, in the mentioned configuration (Win XP, OL 2002), this causes the
explorer process to hang.

So i have modified the code (unfortunately i have posted the "correct" code):

STDMETHODIMP CMyExt::OnWriteComplete(IExchExtCallback *lpExchangeCallback,
ULONG ulFlags)
{
LPMESSAGE lpMessage = NULL;
LPMDB lpMdb = NULL;

if ((SUCCEEDED(lpExchangeCallback->GetObject(&lpMdb, (LPMAPIPROP
*)&lpMessage)))&& (lpMessage))
{
if (lpMessage != NULL)
{
lpMessage->Release();
lpMessage = NULL;
}
if (lpMdb != NULL)
{
lpMdb->Release();
lpMdb = NULL;
}
}
return S_FALSE;
}

This is working on all configurations.

Now my question:
- SHOULD i call the ->Release() method on lpMessage and lpMdb
- am i allowed to do so?
- will it affect the Outlook functionality?

Thanks.
Vladimir Werskov
 
D

Dmitry Streblechenko

Yes, you should release all COM objects retrieved from Outlook (unless the
function documentation explicitly states otherwise, and I can't think of any
that do) - just a general COM rule..

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
V

Vladimir Werskov

Thank you for assuring me. Finally i could locate the documentation in MSDN
Library (there is no documentation ion online msdn anymore) for the GetObject
method:

Remarks:
The interfaces must be released before the call which was given the lpeecb
parameter returns.

So it seems that every example i have found is wrong :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top