Author: useresu download source code
Summary
This article introduces a simple com plug-in (addin) that uses VC ++ ATL (Active Template Library) and idtextensibility2 to add a simple function to Microsoft Word ), add the toolbar buttons, menus, and other visual components, and add the Event Response Method to them. Finally, the method for mixed programming with office macros is briefly described.
When talking about office-related programming, you may first think of VBA (Visual Basic for application). In fact, ATL is also a good tool. Here we will introduce an Office Programming Method Based on ATL. The function is simple, just an example. The steps are as follows:
1. In the Visual C ++ programming environment, use the Wizard to generate an atl com Appwizard project named wordaddin:
In the first dialog box of the wizard, select the default server type dynamic link library (DLL) for server type single producer, and select the allow merging of proxy-stub code option for the three check boxes below. Click Finish to create an empty ATL Project (1 ).
Figure 1
2. insert our ATL object:
Select Insert-> Insert new ATL object from the menu. The new ATL object wizard dialog box is displayed. Select object from the category list on the left, select simple object on the right, and click Next (2):
Figure 2
In the second dialog box, enter Addin (3) in "short name:" On the names property page ):
Figure 3
On the attribute page, select the support isupporterrorinfo check box and click OK (4 ):
Figure 4
In this way, an atl com object class named wordaddin is generated. Compile (build) The project to check whether everything is normal.
2. Use idtextensibility2 to implement the caddin class:
Idtextensibility2 is a library file defined in msaddin designer typelibrary (msaddndr. dll/msaddndr. TLB). This file is generally in the C:/program files/common files/designer directory. The idtextensibility2 Library provides five events that can be used to manipulate plug-ins and host applications: onconnection, ondisconnection, onaddinsupdate, onstartupcomplete, and onbeginshutdown. For more information about the functions and usage of these events, see msdn. Use the Wizard to implement the idtextensibility2 interface: switch to the classview page, right-click the caddin class, select implement interface in the pop-up menu, the implement interface dialog box appears, select Add tylpelib button (5 ):
Figure 5
(Click OK.) In the displayed browse typelibraries dialog box, select Microsoft add-in designer (1.0) (6 ):
Figure 6
Click OK. On the addindesignerobjects properties page, select idtextensibility2 (7 ):
Figure 7
Click OK. In this way, the wizard adds five specific events of idtextensibility2 to the atl com object, implements some default initial settings for them, and updates com_interface_map ().
4. register the plug-in to its host Program:
Open the addin. RGS file in the file view FileView> resource file and add the following code:
HKCU{ Software { Microsoft { Office { Word { Addins { ''WordAddin.Addin'' { val FriendlyName = s ''WORD Custom Addin'' val Description = s ''Word Custom Addin'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000001'' } } } } } }}
5. recompile (build) The project to register our plug-in.
6. Run: Select executable file as Word 2000. Be sure to select the correct path. If the file runs successfully, the plug-in has been added to word.
7. Add menus and buttons to the plug-in:
Here is a brief introduction to the office command bar: in office, the menu bar, toolbar, and pop-up menu are all called command bar objects ). All these types of command lines can contain other command lines and unlimited controls. For example, a button is its control relative to the command bar on the toolbar. Some controls (such as menus) are command lines and can be nested at multiple levels. All command lines can be controlled by a commandbars set. The commandbars set is a common shared and programmable object. You can use its add () method to add a commandbar object (a command bar) to it ), each commandbar object has a commandbarcontrols set, which contains all the controls in this command bar. You can use the CONTROLS attribute of the commandbar object to reference controls in the command bar.
Add a toolbar, buttons, and a menu to word:
First, add the Office and Word type libraries to the project, and add the following code to the stdafx. h file:
#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("Word"), raw_interfaces_only, named_guids using namespace Outlook;
Note: You must change the installation path to the same as that of the Office.
In the word object model, the application object represents the highest level object of the entire project. We can use its getcommandbars method to obtain the commandbars object. Because the commandbars object is a set of all the tool bar and menu items of word, so you can add a new toolbar by calling its add method. Then add a new button to the toolbar. The method is as simple as that. You can call the getcontrols method of commandbar to obtain the commandbarcontrols set of the toolbar. As mentioned above, the commandbarcontrols set is a set of all controls of the toolbar, the button is one of them, so we can add a new button by calling the Add method of the commandbarcontrols set. The specific implementation code is as follows:
CComQIPtr<_Application> spApp(Application); ATLASSERT(spApp); m_spApp = spApp; HRESULT hr = AppEvents::DispEventAdvise(m_spApp); if(FAILED(hr)) return hr; CComPtr <Office::_CommandBars> spCmdBars; CComPtr <Office::CommandBar> spCmdBar; hr = m_spApp->get_CommandBars(&spCmdBars); if(FAILED(hr)) return hr; ATLASSERT(spCmdBars); // now we add a new toolband to Word // to which we''ll add 2 buttons CComVariant vName("WordAddin"); CComPtr 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); m_spButton = 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 // Word''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")); spCmdButton2->put_OnAction(OLESTR("MYMacro1")); //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); spCmdButton2->put_OnAction(OLESTR("MYMacro2"));
The process of adding a menu item is similar to that of adding a menu item:
First, call the get_activemenubar () method of the commandbars set to obtain a commandbar object, which represents the active menu in the current project, and then call getcontrols of the commandbar to obtain the control set of the current menu. Try to add a new menu item to the "format" menu (5th menus) of word, and call the getitem (5) of commandbarcontrols to obtain the required "format" menu item, it is also a commandbarcontrols set (as mentioned earlier, the control set can be nested), so that you can add a new menu item by calling its add method. The specific implementation code is as follows:
_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 Word''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;
Then, run the program and you can see that the added buttons and menu items have already appeared in word. You also need to add a response event for them to truly extend the word function through the plug-in.
8. Add a response event for the button I just added:
ATL provides two template classes for the idispatch interface of the COM Object: idispeventimpl <> and idispeventsimpleimpl <>. Select idispeventsimpleimpl <> because it does not require additional type library information, inherit a class from idispeventsimpleimpl <>:
class ATL_NO_VTABLE CAddin : public CComObjectRootEx < CComSingleThreadModel>, .....public IDispEventSimpleImpl<1,CAddin, &__uuidof(Office::_CommandBarButtonEvents>
The callback function of the event when the declaration button is clicked:
void __stdcall OnClickButton(Idispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
Use the _ atl_sink_info structure to describe the callback parameter information: Open the caddin. h file and add the following declaration at the top of the file:
Extern _ atl_func_info onclickbuttoninfo; (Be sure to declare it as an external variable)
Open the caddin. cpp file and add the following definitions to it:
_ATL_FUNC_INFO OnClickButtonInfo = {CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}};
The following describes how to add a button and click an event:
void __stdcall CAddin::OnClickButton(IDispatch* /*Office::_CommandBarButton* */ Ctrl, VARIANT_BOOL * CancelDefault){ USES_CONVERSION; CComQIPtr pCommandBarButton(Ctrl); //the button that raised the event. Do something with this... MessageBox(NULL, "Clicked Button1", "OnClickButton", MB_OK);}
Add the following information to the interface ing macro:
BEGIN_SINK_MAP(CAddin) SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents), /*dispid*/ 0x01, OnClickButton, &OnClickButtonInfo) END_SINK_MAP()
Finally, call dispeventadvise () and dispeventunadvise () to connect to and disconnect the message source in onconnection () of the caddin class respectively.
Here we have implemented a simple com plug-in to run the program. Click the new button on the toolbar to bring up the ("clicked button1") message box.
If you are familiar with VBA programming, you can use the written macro as the button to respond to events. You only need to call the put_onaction () method of the button:
spCmdButton->put_OnAction(OLESTR("YourMacroName"));
This article does not discuss ATL in depth. If you want to know about ATL, refer to: ATL internals.