Here's one of my add-ins. It uses ATL, but I highly recommend doing
so.
Here's the OnConnection handler that adds event sinks for all the
explorers that are extant when the add-in is loaded and adds an event
sink to watch for new explorers.
STDMETHODIMP CBmaOutlookAddIn::OnConnection(IDispatch *pApplication,
AddInDesignerObjects::ext_ConnectMode /*ConnectMode*/, IDispatch * /
*pAddInInst*/, SAFEARRAY ** /*custom*/ )
{
DebugLog( _T( "Entering CBmaOutlookAddIn::OnConnection"));
mOutlookApplication = pApplication;
try
{
mExplorers = mOutlookApplication->GetExplorers();
if( mExplorers)
{
for( int i = 1; i <= mExplorers->GetCount(); i++)
{
Outlook::_ExplorerPtr explorer = mExplorers->Item( i);
CExplorerEventSink *sink = new CExplorerEventSink( this);
if( SUCCEEDED( sink->Initialize( explorer)))
mExplorerEventSinks.Add( sink);
else
sink->Release();
}
ExplorersEvents:
ispEventAdvise( mExplorers);
}
....
Here's the header file for my explorer event sink. It inherits from
my general toolbar event sink since I use similar toolbars on
inspector windows and the explorer context menu.
class CExplorerEventSink : public CToolbarEventSink,
public IDispEventSimpleImpl< 1, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)>,
public IDispEventSimpleImpl< 2, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)>,
public IDispEventSimpleImpl< 3, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)>,
public IDispEventSimpleImpl< 4, CExplorerEventSink,
&__uuidof(Office::_CommandBarsEvents)>
{
public:
CExplorerEventSink( CBmaOutlookAddIn *addIn);
virtual ~CExplorerEventSink();
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
HRESULT Initialize( Outlook::_ExplorerPtr explorer);
virtual void Uninitialize();
Outlook::_ExplorerPtr GetExplorer() const{ return mExplorer;}
virtual void OnButtonClicked( CButtonEventSink::Id id);
void __stdcall OnExplorerActivate();
void __stdcall OnExplorerFolderSwitched();
void __stdcall OnExplorerClose();
void __stdcall OnCommandBarsUpdated();
BEGIN_SINK_MAP(CExplorerEventSink)
SINK_ENTRY_INFO( 1, __uuidof(Outlook::ExplorerEvents_10),
0x0000f001, OnExplorerActivate, &NoParamsInfo)
SINK_ENTRY_INFO( 2, __uuidof(Outlook::ExplorerEvents_10),
0x0000f002, OnExplorerFolderSwitched, &NoParamsInfo)
SINK_ENTRY_INFO( 3, __uuidof(Outlook::ExplorerEvents_10),
0x0000f008, OnExplorerClose, &NoParamsInfo)
SINK_ENTRY_INFO( 4, __uuidof(Office::_CommandBarsEvents),
0x00000001, OnCommandBarsUpdated, &NoParamsInfo)
END_SINK_MAP()
protected:
virtual Office::_CommandBarsPtr GetToolbars(){ return mCommandBars;}
void RemoveContextMenuButtons();
protected:
long mRefCount;
Outlook::_ExplorerPtr mExplorer;
Office::_CommandBarsPtr mCommandBars;
CContextMenuEventSink *mContextMenuEventSink;
CHiddenWindow *mHiddenWindow;
};
Here's the relevant pieces of my .cpp file. I haven't included the
toolbar event sink's source, but it's not the interesting bit.
#include "stdafx.h"
#include "ExplorerEventSink.h"
#include "HiddenWindow.h"
typedef IDispEventSimpleImpl< 1, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)> ExplorerEvent1;
typedef IDispEventSimpleImpl< 2, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)> ExplorerEvent2;
typedef IDispEventSimpleImpl< 3, CExplorerEventSink,
&__uuidof(Outlook::ExplorerEvents_10)> ExplorerEvent3;
typedef IDispEventSimpleImpl< 4, CExplorerEventSink,
&__uuidof(Office::_CommandBarsEvents)> CommandBarsEvent;
CExplorerEventSink::CExplorerEventSink( CBmaOutlookAddIn *addIn) :
CToolbarEventSink( addIn)
{
mRefCount = 1;
mContextMenuEventSink = NULL;
mHiddenWindow = NULL;
}
CExplorerEventSink::~CExplorerEventSink()
{
Uninitialize();
if( mHiddenWindow)
{
mHiddenWindow->DestroyWindow();
delete mHiddenWindow;
}
}
HRESULT CExplorerEventSink::Initialize( Outlook::_ExplorerPtr
explorer)
{
HRESULT hResult = S_OK;
if( FAILED( hResult = ExplorerEvent1:
ispEventAdvise( explorer)))
return hResult;
if( FAILED( hResult = ExplorerEvent2:
ispEventAdvise( explorer)))
return hResult;
if( FAILED( hResult = ExplorerEvent3:
ispEventAdvise( explorer)))
return hResult;
mExplorer = explorer;
OnExplorerActivate();
return S_OK;
}
void CExplorerEventSink::Uninitialize()
{
CToolbarEventSink::Unitialize();
if( mExplorer)
{
RemoveContextMenuButtons();
if( mCommandBars)
{
CommandBarsEvent:
ispEventUnadvise( mCommandBars);
mCommandBars = NULL;
}
ExplorerEvent3:
ispEventUnadvise( mExplorer);
ExplorerEvent2:
ispEventUnadvise( mExplorer);
ExplorerEvent1:
ispEventUnadvise( mExplorer);
mExplorer = NULL;
}
}
void __stdcall CExplorerEventSink::OnExplorerActivate()
{
DebugLog( _T( "Entering CExplorerEventSink::OnExplorerActivate"));
try
{
if( !mCommandBars && mExplorer->GetCommandBars())
{
mCommandBars = mExplorer->GetCommandBars();
CommandBarsEvent:
ispEventAdvise( mCommandBars);
AddButtons();
}
}
IGNORE_COM_EXCEPTION
DebugLog( _T( "Leaving CExplorerEventSink::OnExplorerActivate"));
}
void __stdcall CExplorerEventSink::OnExplorerFolderSwitched()
{
DebugLog( _T( "Entering
CExplorerEventSink::OnExplorerFolderSwitched"));
try
{
Outlook::MAPIFolderPtr currentFolder = mExplorer-
if( currentFolder && currentFolder->GetDefaultItemType() ==
Outlook:
lMailItem)
{
if( !HasButton())
AddButtons();
}
else
RemoveButtons();
}
IGNORE_COM_EXCEPTION
DebugLog( _T( "Leaving
CExplorerEventSink::OnExplorerFolderSwitched"));
}
void __stdcall CExplorerEventSink::OnCommandBarsUpdated()
{
DebugLog( _T( "Entering CExplorerEventSink::OnCommandBarsUpdated"));
try
{
if( mCommandBars)
{
if( HasButton())
{
RemoveContextMenuButtons();
Office::CommandBarPtr menu = mCommandBars->GetItem( _T( "Context
Menu"));
if( menu)
{
CContextMenuEventSink *sink = new CContextMenuEventSink( this,
mAddIn);
if( SUCCEEDED( sink->Initialize( menu)))
mContextMenuEventSink = sink;
else
sink->Release();
}
}
else
RemoveContextMenuButtons();
}
}
IGNORE_COM_EXCEPTION
DebugLog( _T( "Leaving CExplorerEventSink::OnCommandBarsUpdated"));
}
void __stdcall CExplorerEventSink::OnExplorerClose()
{
Uninitialize();
mAddIn->RemoveExplorerEventSink( this);
}
void CExplorerEventSink::RemoveContextMenuButtons()
{
if( mContextMenuEventSink)
{
mContextMenuEventSink->Uninitialize();
mContextMenuEventSink->Release();
mContextMenuEventSink = NULL;
}
}
STDMETHODIMP_(ULONG) CExplorerEventSink::AddRef()
{
InterlockedIncrement( &mRefCount);
return mRefCount;
}
STDMETHODIMP_(ULONG) CExplorerEventSink::Release()
{
ULONG refCount = InterlockedDecrement( &mRefCount);
if( !refCount)
delete this;
return refCount;
}
Maybe creating add-ins is easier in VB, but I've run across several
gotcha's in C++/ATL. I'm doing some odd stuff (including mixing OOM
and MAPI), which is part of the problem. For example, though, all the
example code I've seen (including that linked by Dmitry above) shows
how to inherit from IDispEventSimpleImpl to sink events from Outlook,
but none of them mention that you may want to override AddRef and
Release. They're virtuals on IDispEventSimpleImpl and its default
implementation just returns 1 in both cases. If you leave it at that,
it works most of the time, but there are cases where Outlook will
(wisely) addref an event sink, call it, then release it, and if you've
disappeared in the meantime, it'll crash Outlook.