Click here to Skip to main content
15,895,084 members
Articles / Desktop Programming / ATL

Building an Office2K COM addin with VC++/ATL

Rate me:
Please Sign up or sign in to vote.
4.95/5 (110 votes)
30 Apr 200320 min read 1.6M   5.1K   224  
This article shows how to program an Outlook2000/2K+ COM addin using a pure ATL COM object.
////////////////////////////////////////////////////////////////////////////
//	Copyright : Amit Dey 2002
//
//	Email :amitdey@joymail.com
//
//	This code may be used in compiled form in any way you desire. This
//	file may be redistributed unmodified by any means PROVIDING it is 
//	not sold for profit without the authors written consent, and 
//	providing that this notice and the authors name is included.
//
//	This file is provided 'as is' with no expressed or implied warranty.
//	The author accepts no liability if it causes any damage to your computer.
//
//	Do expect bugs.
//	Please let me know of any bugs/mods/improvements.
//	and I will try to fix/incorporate them into this file.
//	Enjoy!
//
//	Description : Outlook2K Addin 
////////////////////////////////////////////////////////////////////////////

// Addin.h : Declaration of the CAddin

#ifndef __ADDIN_H_
#define __ADDIN_H_
#include "stdafx.h"
#include "resource.h"       // main symbols
#import "C:\Program Files\Common Files\Designer\MSADDNDR.DLL" raw_interfaces_only, raw_native_types, no_namespace, named_guids 

/////////////////////////////////////////////////////////////////////////////
// CAddin
extern _ATL_FUNC_INFO OnClickButtonInfo;
extern _ATL_FUNC_INFO OnOptionsAddPagesInfo;

class ATL_NO_VTABLE CAddin : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CAddin, &CLSID_Addin>,
	public ISupportErrorInfo,
	public IDispatchImpl<IAddin, &IID_IAddin, &LIBID_OUTLOOKADDINLib>,
	public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2, &LIBID_AddInDesignerObjects>,
	public IDispEventSimpleImpl<1,CAddin,&__uuidof(Office::_CommandBarButtonEvents)>,
	public IDispEventSimpleImpl<2,CAddin,&__uuidof(Office::_CommandBarButtonEvents)>,
	public IDispEventSimpleImpl<3,CAddin,&__uuidof(Office::_CommandBarButtonEvents)>,
	public IDispEventSimpleImpl<4,CAddin,&__uuidof(Outlook::ApplicationEvents)>
{
public:

	typedef IDispEventSimpleImpl</*nID =*/ 1,CAddin, &__uuidof(Office::_CommandBarButtonEvents)> CommandButton1Events;
	typedef IDispEventSimpleImpl</*nID =*/ 2,CAddin, &__uuidof(Office::_CommandBarButtonEvents)> CommandButton2Events;
	typedef IDispEventSimpleImpl</*nID =*/ 3,CAddin, &__uuidof(Office::_CommandBarButtonEvents)> CommandMenuEvents;
	typedef IDispEventSimpleImpl</*nID =*/ 4,CAddin, &__uuidof(Outlook::ApplicationEvents)> AppEvents;
	CAddin()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_ADDIN)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CAddin)
	COM_INTERFACE_ENTRY(IAddin)
//DEL 	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(ISupportErrorInfo)
	COM_INTERFACE_ENTRY2(IDispatch, IAddin)
	COM_INTERFACE_ENTRY(_IDTExtensibility2)
END_COM_MAP()

BEGIN_SINK_MAP(CAddin)
SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton, &OnClickButtonInfo)
SINK_ENTRY_INFO(2, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton2, &OnClickButtonInfo)
SINK_ENTRY_INFO(3, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickMenu, &OnClickButtonInfo)
SINK_ENTRY_INFO(4,__uuidof(Outlook::ApplicationEvents),/*dispinterface*/,0xf005,OnOptionsAddPages,&OnOptionsAddPagesInfo)
END_SINK_MAP()



// ISupportsErrorInfo
	STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
	void __stdcall OnClickButton(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
	void __stdcall OnClickButton2(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
	void __stdcall OnClickMenu(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
	void __stdcall OnOptionsAddPages(IDispatch* /*PropertyPages**/ Ctrl);
// IAddin
public:
// _IDTExtensibility2
	STDMETHOD(OnConnection)(IDispatch * Application, ext_ConnectMode ConnectMode, IDispatch * AddInInst, SAFEARRAY * * custom)
	{
		CComPtr < Office::_CommandBars> spCmdBars; 
	CComPtr < Office::CommandBar> spCmdBar;

	// QI() for _Application
	CComQIPtr <Outlook::_Application> spApp(Application); 
	ATLASSERT(spApp);
	// get the CommandBars interface that represents Outlook's 		//toolbars & menu 
	//items	
	m_spApp = spApp;

	CComPtr<Outlook::_Explorer> spExplorer; 	
	spApp->ActiveExplorer(&spExplorer);
	HRESULT hr = spExplorer->get_CommandBars(&spCmdBars);
	if(FAILED(hr))
		return hr;

	ATLASSERT(spCmdBars);
	// now we add a new toolband to Outlook
	// to which we'll add 2 buttons
	CComVariant vName("OutlookAddin");
	CComPtr <Office::CommandBar> spNewCmdBar;
	
	// position it below all toolbands
	//MsoBarPosition::msoBarTop = 1
	CComVariant vPos(1); 

	CComVariant vTemp(VARIANT_TRUE); // menu is temporary		
	CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);			
	//Add a new toolband through Add method
	// vMenuTemp holds an unspecified parameter
	//spNewCmdBar points to the newly created toolband
	spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp);

	//now get the toolband's CommandBarControls
	CComPtr < Office::CommandBarControls> spBarControls;
	spBarControls = spNewCmdBar->GetControls();
	ATLASSERT(spBarControls);
	
	//MsoControlType::msoControlButton = 1
	CComVariant vToolBarType(1);
	//show the toolbar?
	CComVariant vShow(VARIANT_TRUE);
	
	CComPtr < Office::CommandBarControl> spNewBar; 
	CComPtr < Office::CommandBarControl> spNewBar2; 
			
	// add first button
	spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); 
	ATLASSERT(spNewBar);
	// add 2nd button
	spNewBar2 = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
	ATLASSERT(spNewBar2);
			
	_bstr_t bstrNewCaption(OLESTR("Item1"));
	_bstr_t bstrTipText(OLESTR("Tooltip for Item1"));

	// get CommandBarButton interface for each toolbar button
	// so we can specify button styles and stuff
	// each button displays a bitmap and caption next to it
	CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar);
	CComQIPtr < Office::_CommandBarButton> spCmdButton2(spNewBar2);
			
	ATLASSERT(spCmdButton);
	ATLASSERT(spCmdButton2);
			
	// to set a bitmap to a button, load a 32x32 bitmap
	// and copy it to clipboard. Call CommandBarButton's PasteFace()
	// to copy the bitmap to the button face. to use
	// Outlook's set of predefined bitmap, set button's FaceId to 	//the
	// button whose bitmap you want to use
	HBITMAP hBmp =(HBITMAP)::LoadImage(_Module.GetResourceInstance(),
	MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);

	// put bitmap into Clipboard
	::OpenClipboard(NULL);
	::EmptyClipboard();
	::SetClipboardData(CF_BITMAP, (HANDLE)hBmp);
	::CloseClipboard();
	::DeleteObject(hBmp);		
	// set style before setting bitmap
	spCmdButton->PutStyle(Office::msoButtonIconAndCaption);
			
	hr = spCmdButton->PasteFace();
	if (FAILED(hr))
		return hr;

	spCmdButton->PutVisible(VARIANT_TRUE); 
	spCmdButton->PutCaption(OLESTR("Item1")); 
	spCmdButton->PutEnabled(VARIANT_TRUE);
	spCmdButton->PutTooltipText(OLESTR("Tooltip for Item1")); 
	spCmdButton->PutTag(OLESTR("Tag for Item1")); 
	
	//show the toolband
	spNewCmdBar->PutVisible(VARIANT_TRUE); 
			
	spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
	
	//specify predefined bitmap
	spCmdButton2->PutFaceId(1758);  
	
	spCmdButton2->PutVisible(VARIANT_TRUE); 
	spCmdButton2->PutCaption(OLESTR("Item2")); 
	spCmdButton2->PutEnabled(VARIANT_TRUE);
	spCmdButton2->PutTooltipText(OLESTR("Tooltip for Item2")); 
	spCmdButton2->PutTag(OLESTR("Tag for Item2"));
	spCmdButton2->PutVisible(VARIANT_TRUE);


	m_spButton = spCmdButton;
	m_spButton2 = spCmdButton2;

	_bstr_t bstrNewMenuText(OLESTR("New Menu Item"));
	CComPtr < Office::CommandBarControls> spCmdCtrls;
	CComPtr < Office::CommandBarControls> spCmdBarCtrls; 
	CComPtr < Office::CommandBarPopup> spCmdPopup;
	CComPtr < Office::CommandBarControl> spCmdCtrl;

	// get CommandBar that is Outlook's main menu
	hr = spCmdBars->get_ActiveMenuBar(&spCmdBar); 
	if (FAILED(hr))
		return hr;
	// get menu as CommandBarControls 
	spCmdCtrls = spCmdBar->GetControls(); 
	ATLASSERT(spCmdCtrls);

	// we want to add a menu entry to Outlook's 6th(Tools) menu 	//item
	CComVariant vItem(5);
	spCmdCtrl= spCmdCtrls->GetItem(vItem);
	ATLASSERT(spCmdCtrl);
		
	IDispatchPtr spDisp;
	spDisp = spCmdCtrl->GetControl(); 
		
	// a CommandBarPopup interface is the actual menu item
	CComQIPtr < Office::CommandBarPopup> ppCmdPopup(spDisp);  
	ATLASSERT(ppCmdPopup);
			
	spCmdBarCtrls = ppCmdPopup->GetControls();
	ATLASSERT(spCmdBarCtrls);
		
	CComVariant vMenuType(1); // type of control - menu
	CComVariant vMenuPos(6);  
	CComVariant vMenuEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
	CComVariant vMenuShow(VARIANT_TRUE); // menu should be visible
	CComVariant vMenuTemp(VARIANT_TRUE); // menu is temporary		
	
		
	CComPtr < Office::CommandBarControl> spNewMenu;
	// now create the actual menu item and add it
	spNewMenu = spCmdBarCtrls->Add(vMenuType, vMenuEmpty, vMenuEmpty, vMenuEmpty, vMenuTemp); 
	ATLASSERT(spNewMenu);
			
	spNewMenu->PutCaption(bstrNewMenuText);
	spNewMenu->PutEnabled(VARIANT_TRUE);
	spNewMenu->PutVisible(VARIANT_TRUE); 
	
	
	//we'd like our new menu item to look cool and display
	// an icon. Get menu item  as a CommandBarButton
	CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu);
	ATLASSERT(spCmdMenuButton);
	spCmdMenuButton->PutStyle(Office::msoButtonIconAndCaption);
	
	// we want to use the same toolbar bitmap for menuitem too.
	// we grab the CommandBarButton interface so we can add
	// a bitmap to it through PasteFace().
	spCmdMenuButton->PasteFace(); 
	// show the menu		
	spNewMenu->PutVisible(VARIANT_TRUE); 
	m_spMenu = spCmdMenuButton;

	hr = CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton);
	if(FAILED(hr))
		return hr;

	hr = CommandButton2Events::DispEventAdvise((IDispatch*)m_spButton2);
	if(FAILED(hr))
		return hr;

	hr = CommandMenuEvents::DispEventAdvise((IDispatch*)m_spMenu);
	if(FAILED(hr))
		return hr;

	hr = AppEvents::DispEventAdvise((IDispatch*)m_spApp,&__uuidof(Outlook::ApplicationEvents));
	if(FAILED(hr))
	{
		ATLTRACE("Failed advising to ApplicationEvents");
		return hr;
	}


	bConnected = true;
	
	return S_OK;

	}
	STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom)
	{
		if(bConnected)
		{
			HRESULT hr = CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);
			if(FAILED(hr))
				return hr;
			hr = CommandButton2Events::DispEventUnadvise((IDispatch*)m_spButton2);
			if(FAILED(hr))
				return hr;

			hr = CommandMenuEvents::DispEventUnadvise((IDispatch*)m_spMenu);
			if(FAILED(hr))
				return hr;
			hr = AppEvents::DispEventUnadvise((IDispatch*)m_spApp);
			if(FAILED(hr))
				return hr;

			bConnected = false;
		}
		return S_OK;

	}
	STDMETHOD(OnAddInsUpdate)(SAFEARRAY * * custom)
	{
		return E_NOTIMPL;
	}
	STDMETHOD(OnStartupComplete)(SAFEARRAY * * custom)
	{
		return E_NOTIMPL;
	}
	STDMETHOD(OnBeginShutdown)(SAFEARRAY * * custom)
	{
		return E_NOTIMPL;
	}

	private:
		CComPtr<Office::_CommandBarButton> m_spButton; 
		CComPtr<Office::_CommandBarButton> m_spButton2; 
		CComPtr<Office::_CommandBarButton> m_spMenu; 
		CComPtr<Outlook::_Application> m_spApp;
		bool bConnected;
};

#endif //__ADDIN_H_

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
Amit Dey is a freelance programmer from Bangalore,India. Chiefly programming VC++/MFC, ATL/COM and PocketPC and Palm platforms. Apart from programming and CP, he is a self-taught guitar and keyboard player.

He can be contacted at visualcdev@hotmail.com


Comments and Discussions