|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
A FAQ is now included at the bottom of this article. IntroductionRecently, I wrote an Outlook2000 COM addin as a part of a project to build a CRM tool. While coding the project, I thought this would make a good topic for an article especially since most of the Office related stuff I found on the Internet were VB/VBA related and almost none with ATL. OverviewThrough this article/tutorial, we will learn how to program an Outlook2000/2K+ COM addin using a pure ATL COM object. We'll startout by writing a basic functional COM addin. Then I'll show you how to add standard UI elements like toolbars and menu items to Outlook and how to respond to their events. Next we'll add our own propertysheet for the addin to Outlook's Tools->Options. Along the way we'll see the relevant registry keys and also take a look at useful features of the ATL Wizards and learn to use them effectively Although we'll be writing an Outlook2000 COM addin, COM addins for other Office2000 applications like Word,Access etc can be built very similarly. Except a couple of minor things like registry keys, for instance, the fundamentals remain the same.I'm assuming that you are a VC++ COM programmer, and have had some experience with ATL based component development and OLE/Automation, although this is not strictly necessary. To build and test the addin, you must have MS Office 2000 installed on your system, or at least Outlook2K.The project code has been built with VC++ 6.0 sp3+/ATL3.0 and tested on Win2K with Office 2000 installed. Getting startedAn Office addin is a COM Automation component that dynamically extends/enhances and controls any Office suite of applications. Microsoft Office 2000 and later support a new, uniform design architecture for building such application add-ins. Typically such addins are housed in ActiveX dlls(inproc server) and can be dynamically loaded and unloaded by the user through the main application. An Office COM addin must implement the enum { ext_cm_AfterStartup = 0, ext_cm_Startup = 1, ext_cm_External = 2, ext_cm_CommandLine = 3 } ext_ConnectMode; enum { ext_dm_HostShutdown = 0, ext_dm_UserClosed = 1 } ext_DisconnectMode; ... ... ... interface _IDTExtensibility2 : IDispatch { [id(0x00000001)] HRESULT OnConnection( [in] IDispatch* Application, [in] ext_ConnectMode ConnectMode, [in] IDispatch* AddInInst, [in] SAFEARRAY(VARIANT)* custom); [id(0x00000002)] HRESULT OnDisconnection( [in] ext_DisconnectMode RemoveMode, [in] SAFEARRAY(VARIANT)* custom); [id(0x00000003)] HRESULT OnAddInsUpdate([in] SAFEARRAY(VARIANT)* custom); [id(0x00000004)] HRESULT OnStartupComplete([in] SAFEARRAY(VARIANT)* custom); [id(0x00000005)] HRESULT OnBeginShutdown([in] SAFEARRAY(VARIANT)* custom); }; All COM add-ins inherit from Registering an addinTo register the COM addin with the host application, we also need to create a couple of registry subkeys under the hive Description - a string value - a description of the addin. LoadBehavior - a DWORD value. - A combination of values that determine how the addin will be loaded by the host app.Set to 0x03 to load on app startup or 0x08 for user controlled activation. CommandLineSafe - a DWORD value. Set to 0x01(TRUE) or 0x00(FALSE). For a full description of the all values and options, please consult MSDN documentation. Building a minimal COM addinOK, now we know enough to go ahead and code a minimal Outlook2K COM addin.To build the addin project files fireup VC++ IDE, create a new ATL COM Appwizard project and name it OutlookAddin. Remember,if you name it anything else it probably wouldn't work.(just kidding!) In the follwing Appwizard Step 1 of 1 dialog, accept the default Server Type Dynamic Link Library(DLL),check Allow merging of proxy-stub code to enable this option and click Finish. Then click OK to generate the project files. Next goto Insert->New ATL Object menu and insert a new ATL simple object to the project by choosing Objects from Category and Simple Object from Objects list in the ATL Object Wizard dialog. Click Next and type Addin as ShortName.In the Attributes tab check Support ISupportErrorInfo. Accept the default options for the rest and click OK. So far the Wizard has given us an Automation-compatible,dispinterface-savvy inproc COM object housed in a dll. By default,a registry script(.rgs) to add the COM object specific registry entries have also been handed to us. Build the project and checkout if everything is in order. Next we will write our addin specific code to implement To register our addin with the host application,in this case Outlook2000, open the project's Addin.rgs registry script file (under FileView->Resource Files) and add the following to the end of the file. HKCU
{
Software
{
Microsoft
{
Office
{
Outlook
{
Addins
{
'OutlookAddin.Addin'
{
val FriendlyName = s 'ADOutlook2K Addin'
val Description = s 'ATLCOM Outlook Addin'
val LoadBehavior = d '00000008'
val CommandLineSafe = d '00000000'
}
}
}
}
}
}
}
Since we want our addin to load at a startup, LoadBehavior is set to 3.Now build the project. If everything is in order, the project gets built successfully and registers the addin. To test the addin, either run the project and specify the fullpath to Outlook2K(\Program Files\Microsoft Office\Office\Outlook.exe) in Executable for Debug Session, or run Outlook2K outside the VC++ IDE after you have registered the dll. To check if our addin has been registered successfully, in Outlook, goto Tools->Options and under Other tab click Advanced Options->COM Addins. An entry for our COM addin should have been added to the Addins Available list; the string is what we specified as 'FriendlyName' in our registry script. ![]() While an addin can be programmed for different uses, there are certain common tasks. Typically, this includes adding UI elements like toolbars/toolbands and menu items to Outlook, through which the user can control the addin. By clicking on these buttons and menu items, the user can access the addin's functionality. So next up we'll tackle such toolbar and menu item additions. Command and ConquerIn Office applications, menus and toolbars are combined into a fully programmable collection called CommandBars. CommandBars are common sharable programmable objects that are exposed by all Office applications as a part of it's object model . CommandBars represent a unified mechanism through which individual toolbars and menu items can be added to the corresponding application. Each CommandBars collection comprises of individual CommandBar objects. .Each CommandBar object again contains a collection of CommandBarControl objects, called CommandBarControls. CommandBarControls represent a complex hierarchy of objects and subobjects that comprise it's object model. A CommandBarControl can itself contain a CommandBar object, accessible through the CommandBar property of the control. Finally, each CommandBarControl object, within the CommandBarControls collection of controls, can be either a CommandBarComboBox(toolbar combobox), a CommandBarButton(toolbar button), or a CommandBarPopup(popup menu). I wish I could draw a nice diagrammatic representation of the object hierarchy, but I'm terrible at such things(honest!), and I'm sure there are such diagrams depicting MS Office CommandBars object model at MSDN. In our addin, we'd like to add the following UI elements to Outlook
First we need to import Office and Outlook typelibraries to our project. To do this open the project's stdafx.h file and add the following #import directive. #import "C:\Program Files\Microsoft Office\Office\mso9.dll" \ rename_namespace("Office") named_guids using namespace Office; #import "C:\Program Files\Microsoft Office\Office\MSOUTL9.olb" rename_namespace("Outlook"), raw_interfaces_only, named_guids using namespace Outlook;
STDMETHODIMP CAddin::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
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);
HRESULT 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);
//..........
//..........
//code to add new menubar to be added here
//read on
//..........
Similarly to add a new menu item to Outlook's Tools menu, we do the following. The ActiveMenuBar property of the CommandBars collection returns a //...... //code to add toolbar here //...... _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); return S_OK; } With that under our belt, it's F5 time. If everything has gone OK, the project builds successfully and you are about to get a first glimpse of your addin in action(if you haven't already, that is). Since we are going to run Outlook to test our addin, in the 'Executable for Debug' dialog, browse to the current path of Outlook executable (Outlook.exe) and we're finally ready to go. In Outlook goto Tools->Options and under the Other tab, click Advanced Options. From the Advanced Options dialog, click on COM Addins button. Next check the entry for our addin in the Addins Available listbox and click OK. As our addin is loaded a new docked toolband with 2 buttons gets created.Also checkout the cool looking menu item that you just added to Outlook's Tools menu. ![]() ![]() Lord of the (disp) SinksPutting together a couple of toolbars and menu items, by themselves, are not very useful unless we can write command handlers that respond to their events. So let's get right to it. Of course here, on click of the different buttons and menu items, we'll be just popping up simple messageboxes. But this is where you'd code your addins functionality. From CRM tools, automated contact management, mail notification and filtering to advanced document management systems to full fledged applications, COM addins can perform an endless variety of tasks. The CommandBarButton control exposes a Click event that is triggered when a user clicks a command bar button. We are going to use this event to run code when the user clicks the toolbar buttons or the menu item. For this, our COM object has to implement an event sink interface. For the CommandBarButton control, this event dispinterface is _CommandBarButtonEvents.The Click method is declared as //... //....Office objects typelibrary //.... [id(0x00000001), helpcontext(0x00038271)] void Click( [in] CommandBarButton* Ctrl, [in, out] VARIANT_BOOL* CancelDefault); //.... //... Thus all we have to do is implement sink interface/s that will be called by the event source through regular connection point protocol whenever a menu or a toolbar button is clicked. What we get for parameters to our callback method is a pointer to the the source But for the uninitiated, ATL provides 2 template classes
Now that everything is in place, we have to connect to and disconnect from the event source with //connect to event source in OnConnection // m_spButton member variable is a smart pointer to _CommandBarButton // that is used to cache the pointer to the first toolbar button. DispEventAdvise((IDispatch*)m_spButton,&DIID__CommandBarButtonEvents); //when I'm done disconnect from the event source //some where in OnDisconnection() DispEventUnadvise((IDispatch*)m_spButton); Similarly implement disp sinks for all your command buttons and menu items, write handlers and connect and disconnect from them as described above. That's all there is to it. If everything went without a hitch, after you rebuild and run the addin, whenever the buttons or menu item is clicked, your respective callback method should be executed. Adding a propertypage The last thing we'll learn to do in this tutorial is how to add our own 'Options' property page to Outlook's Tools->Options property sheet. ![]() The catch here is that to add a page to Outlook's Tools->Options menu as a part of our addin, our addin as to implement a propertypage as an activeX control. When the user goes to Tools->Options menu item, the Application object fires an dispinterface ApplicationEvents
{
....
[id(0x0000f005), helpcontext(0x0050df87)]
void OptionsPagesAdd([in] PropertyPages* Pages);
....
}
[
odl,
uuid(00063080-0000-0000-C000-000000000046),
helpcontext(0x0053ec78),
dual,
oleautomation
]
....
....
interface PropertyPages : IDispatch {
[id(0x0000f000), propget, helpcontext(0x004deb87)]
HRESULT Application([out, retval] _Application** Application);
....
....
[id(0x0000005f), helpcontext(0x00526624)]
HRESULT Add([in] VARIANT Page,
[in, optional] BSTR Title);
[id(0x00000054), helpcontext(0x00526625)]
HRESULT Remove([in] VARIANT Index);
};
The OptionsPagesAdd event passes us a pointer to the PropertyPages dispinterface, whose Add method will finally add the page. The parameters to the Add method are the the ProgID of our control and the new tab caption text. Similarly, to remove a page, call Remove() with the index of the targeted page. Now let's get down to the nitty gritty of it. Begin by adding a new ATL activex composite control to the project through Insert->New ATL Object. Select Controls from category and Lite Composite Control from Objects listbox and click Next. Enter the ShortName as PropPage and in the Attributes tab, check the Support ISupportErrorInfo option. click on OK to accept all the default options. Now we are going to implement the PropertyPage interface. So in ClassView, rightclick on CPropPage class, choose Implement Interface and click Add Typelib button as before.This time check the Microsoft Outlook 9.0 Object Library and click OK. From the Interfaces listbox check PropertyPage and click OK. The Wizard adds default implementations for the 3 methods of PropertyPage, Apply(), get_Dirty() and GetPageInfo().Now make the following modifications. In the com map, modify the line COM_INTERFACE_ENTRY(IDispatch)to COM_INTERFACE_ENTRY2(IDispatch,IPropPage)to resolve any ambiguity. Next to implement IDispatch, we'll use the IDispatchImpl<> template class. So in the CPropPage class declaration replace class ATL_NO_VTABLE CPropPage : public CComObjectRootEx<CComSingleThreadModel>, public IDispatchImpl<IPropPage, &IID_IPropPage, &LIBID_TRAILADDINLib>, .... .... public PropertyPagewith public IDispatchImpl < Outlook::PropertyPage,&__uuidof(Outlook::PropertyPage), &LIBID_OUTLOOKADDINLib>Also remove the redundant #import statement at the top of the PropPage.h file. The typelib has already been imported once in stdafx.h, so this is not needed. Next we are going to connect and disconnect our addin to extern _ATL_FUNC_INFO OnOptionsAddPagesInfo; class ATL_NO_VTABLE CAddin : .... .... public IDispEventSimpleImpl<4,CAddin,&__uuidof(Outlook::ApplicationEvents)> { public: //typedef for applicationEvents sink implementation typedef IDispEventSimpleImpl</*nID =*/ 4,CAddin, &__uuidof("outlook::ApplicationEvents">Outlook::ApplicationEvents)> AppEvents; .... .... .... BEGIN_SINK_MAP(CAddin) .... SINK_ENTRY_INFO(4,__uuidof(Outlook::ApplicationEvents), /*dispid*/0xf005,OnOptionsAddPages,&OnOptionsAddPagesInfo) END_SINK_MAP() public: //callback method for OptionsAddPages event void __stdcall OnOptionsAddPages(IDispatch *Ctrl); }; //in PropPage.cpp file _ATL_FUNC_INFO OnOptionsAddPagesInfo = (CC_STDCALL,VT_EMPTY,1,{VT_DISPATCH}}; void __stdcall CAddin::OnOptionsAddPages(IDispatch* Ctrl) { CComQIPtr<Outlook::PropertyPages> spPages(Ctrl); ATLASSERT(spPages); //ProgId of the propertypage control CComVariant varProgId(OLESTR("OutlookAddin.PropPage")); //tab text CComBSTR bstrTitle(OLESTR("OutlookAddin")); HRESULT hr = spPages->Add((_variant_t)varProgId,(_bstr_t)bstrTitle); if(FAILED(hr)) ATLTRACE("\nFailed adding propertypage"); }Finally in OnConnection() and OnDisconnection(), call DispEventAdvise and DispEventUnadvise to connect and disconnect to ApplicationEvents, as before. Now that we have everything in place, rebuild the project. Next hit F5 and goto Outlook's Tools->Options menu. You should see the new tab that we just added. But hangon, when you click on the new tab, instead of the propertypage, a MessageBox tells us that the propertypage cannot be displayed. What happened? Has all our hard work gone to waste? Fear not! What is happening is that although the propertypage gets created, Outlook doesn't get any information about the propertypage control's keyboard behavior. The default ATL implementation of IOleControl's In the PropPage.h file, add the declaration: STDMETHOD(GetControlInfo)(LPCONTROLINFO lpCI); Our overridden method just returns STDMETHODIMP CPropPage::GetControlInfo(LPCONTROLINFO lpCI)
{
return S_OK;
}
That's it. Now when you build the project and activate the OutlookAddin tab of Options propertysheet,our new propertypage should get displayed without any errors. UpdateApril 02,2003 The response this article has generated on CP has led to this little Q & A section. When I posted this, I was not ready for the barrage of questions and comments that have come up. But I feel, this is a good thing. So keep those questions coming! Some of the topics and code discussed here are applicable for all Office addins.
Handling New Inspector Event
Q : How do I add custom button/menuitems to Outlook's windows like New->Mail Message window and others? You can get access to all of Outlook's open windows through Inspector and the Inspectors collection. In particular, ....
dispinterface InspectorsEvents {
properties:
methods:
[id(0x0000f001), helpcontext(0x0050e6f0)]
void NewInspector([in] _Inspector* Inspector);
};
One way to handle the NewInspector event in the addin class is through the ATL class ATL_NO_VTABLE CAddin : public CComObjectRootEx < CComSingleThreadModel>, public CComCoClass < CAddin, &CLSID_Addin>, ... ... public IDispatchImpl < "Outlook::InspectorEvents">Outlook::InspectorEvents, &DIID_InspectorsEvents, &LIBID_OLADDINLib> { ... ... private: CComQIPtr <IConnectionPointContainer,&IID_IConnectionPointContainer> Next declare your overridden STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*,
VARIANT*, EXCEPINFO*, UINT*);
and implement it as : HRESULT CAddin::Invoke(DISPID dispidMember,REFIID riid,LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams, VARIANT* pvarResult,
EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
if(dispidMember = 0xf001)
{
if (!pDispParams)
return E_INVALIDARG;
if (pDispParams->cArgs > 0)
{
//the only parameter is an Inspector *
IDispatchPtr pDisp(pDispParams->rgvarg[0].pdispVal);
CComQIPtr<_Inspector> spInspector(pDisp);
CComPtr<_CommandBars> pCmdBars;
HRESULT hr = spInspector->get_CommandBars(&pCmdBars);
if(FAILED(hr))
return hr;
//once you have access to the new windows CommandBars collection,
//now add your own menubars and toolbar items to it
....
....
}
}
};
Finally we need to CComPtr<Outlook::_Inspectors> spInspectors; hr = spApp->get_Inspectors(&spInspectors); m_spInspectorsCPC = spInspectors; ATLASSERT(m_spInspectorsCPC); CComPtr and break it up on CComPtr<IConnectionPoint> spCP; hr = m_spInspectorsCPC->FindConnectionPoint(DIID_InspectorsEvents,&spCP); if(FAILED(hr)) return hr; hr = spCP4->Unadvise(m_dwInspectors); if(FAILED(hr)) return hr; Provided everything has gone alright, you should be successfully handling the Customize Outlook Menus
Q : How do I make an addition to Outlook's standard menuitems like File,Edit etc.? Once you get Outlook CommandBars collection, you have a to first search for "Menu Bar" CommandBar and then iterate over it's CommandBarControl collection (which contains File,Edit and other menuitems) to get the target menuitem. Next get it's CommandBarControls collection and add your menuitem. Remember that a CommandBarPopup represents a menuitem and CommandBarButton a toolbaritem. You can also use Here's a tiny little method I call during HRESULT CAddin::AddNewMenu(_CommandBars *pCmdBars)
{
CComPtr < Office::CommandBar> spNewCmdBar;
HRESULT hr = pCmdBars->get_ActiveMenuBar(&spNewCmdBar);
if (FAILED(hr))
return hr;
int nCount;
hr =pCmdBars->get_Count(&nCount);
if (FAILED(hr))
return hr;
CComBSTR bstrBarName;
CComBSTR bstrToolbarName(OLESTR("Menu Bar"));
CComPtr < Office::CommandBar> pTempCmdBar;
for (int i = 1; i <= nCount; i++)
{
pTempCmdBar = pCmdBars->GetItem(CComVariant(i));
ATLASSERT(pTempCmdBar);
pTempCmdBar->get_Name(&bstrBarName);
if (_bstr_t(bstrBarName, TRUE) == _bstr_t(bstrToolbarName, TRUE))
{
CComPtr< Office::CommandBarControl > spCmdBarCtrl;
CComPtr< Office::CommandBarControls > spCmdBarCtrls;
spCmdBarCtrls = pTempCmdBar->GetControls();
int n = spCmdBarCtrls->GetCount();
for (int j = 1; j < n; j++)
{
spCmdBarCtrl = spCmdBarCtrls->GetItem(CComVariant(j));
CComBSTR bstrCaption;
spCmdBarCtrl->get_Caption(&bstrCaption);
CComBSTR bstrTarget(OLESTR("&File"));
// we want to add to File menu
if (_bstr_t(bstrCaption, TRUE) == _bstr_t(bstrTarget, TRUE))
{
// File is a menu item
CComQIPtr<Office::CommandBarPopup> Q : How do I add a new menubar say, between Outlook's File and Edit menus? This is somewhat related to the earlier question about additions to standard Outlook menu items. Again, what we need to do is: from Outlook's CommandBars collection, get the active menubar CommandBar,which represents all of Outlook's menus. Next, make an addition to this CommandBar object, through it's CommandBarControls collection. A menu is a CommandBarPopup object( Like the earlier little function, HRESULT CAddin::AddNewMenubar(_CommandBars *pCmdBars)
{
CComPtr < Office::CommandBar > spNewCmdBar;
HRESULT hr = pCmdBars->get_ActiveMenuBar(&spNewCmdBar);
if (FAILED(hr))
return hr;
CComPtr< Office::CommandBarControls > spCtrls;
spCtrls = spNewCmdBar->GetControls();
ATLASSERT(spCtrls);
CComVariant varType(10);
CComVariant vtEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
CComVariant vtTemp(VARIANT_TRUE);
CComVariant vtBefore(2);
CComPtr< CommandBarControl > spCtrl;
spCtrl = spCtrls->Add(varType, vtEmpty, vtEmpty, vtBefore, vtTemp);
ATLASSERT(spCtrl);
CComQIPtr< Office::CommandBarPopup > spPopup(spCtrl->GetControl());
ATLASSERT(spPopup);
// set menu caption
spPopup->PutCaption(OLESTR("Custom"));
spPopup->PutVisible(VARIANT_TRUE);
// now add a menu item to the menubar
// as a CommandBarButton so you can specify styles
CComPtr< Office::CommandBarControls > spCmdBarCtrls;
spCmdBarCtrls = spPopup->GetControls();
// A menu is a CommandBarPopup
// object(MsoControlType::msoControlPopup = 10),
// to which we have to add our menuitems i.e.
// CommandBarButton(MsoControlType::msoControlButton = 1).
// you can also add popup menus similarly.
CComVariant vType(1); // button
CComPtr< Office::CommandBarControl > spBarCtrl;
spBarCtrl = spCmdBarCtrls->Add(vType, vtEmpty, vtEmpty, vtEmpty, vtTemp);
ATLASSERT(spBarCtrl);
CComQIPtr< Office::_CommandBarButton > spButton(spBarCtrl);
ATLASSERT(spButton);
// set button styles
spButton->PutCaption(OLESTR("Menu1"));
spButton->PutFaceId(4);
spButton->PutStyle(Office::msoButtonIconAndCaption);
spButton->PutVisible(VARIANT_TRUE);
return S_OK;
}
That's it for now. Cheers! :) References and AcknowledgementsMSKB Articles:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||