• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            (轉(zhuǎn)載)使用VC++/ATL創(chuàng)建一個(gè)Office2K AddIn Com 組件

            原文:http://www.xaradio.com/errorpages/GeneralError.htm?aspxerrorpath=/ShowFAQ.aspx


            作者:Amit Dey 譯:劉濤
            近來,我寫了一個(gè)outlook2000的Addin Com作為我建立CRM 工具的工程的一部分。當(dāng)我為這個(gè)工程寫代碼的時(shí)候,我想這可能是一個(gè)很好的題目,因?yàn)槲以趇nternet上找到的與Office相關(guān)的資料大部分是VB/VBA 相關(guān)的,幾乎沒有與ATL相關(guān)的。
            在這篇文章里的代碼并沒有進(jìn)行優(yōu)化,為了使讀者便于跟隨,我盡量將它寫的淺顯易懂。我寫這篇文章花了一些時(shí)間,并且也盡了我的最大努力,萬一還存在什么錯(cuò) 誤,請爽快的給我發(fā)封郵件。如果你喜歡這篇文章或者覺得它讀起來很有趣,并給我一個(gè)高的評(píng)價(jià)或是發(fā)郵件告訴我你的看法,我將非常高興。謝謝!

            概況:
            通過這篇文章,我們將會(huì)了解怎樣使用純ATL Com 對象編寫Outlook2000/2K+ COM addin程序。我們將從寫一個(gè)最基本的Com AddIn程序開始。接下來我將向你們展示怎樣將標(biāo)準(zhǔn)的界面元素比如工具欄或是菜單項(xiàng)加入到outlook中去,并響應(yīng)他們的事件。緊接著,我們要為Outlook's Tools->Options加入我們自己編寫的屬性表。接著我們將看一些相關(guān)的注冊鍵和ATL向?qū)У囊恍┓浅S杏玫奶卣鞑⑶覍W(xué)習(xí)有效地使用他們。
            雖然我們寫的是一個(gè)Outlook2000 COM addin的程序。但是Office2000的應(yīng)用程序,比如Word,Access等等,他們的Com AddIn的寫法是非常相似的。除了注冊鍵和接口,其余的部分基本上是一樣的。
            我假設(shè)你是一個(gè)VC++ Com的開發(fā)人員,并且也有一些基于ATL的組件開發(fā)和OLE/自動(dòng)化方面的經(jīng)驗(yàn),盡管這也不是必須的。創(chuàng)建和測試這個(gè)AddIn程序,你必須安裝Office2000,至少有outlook2000。程序代碼使用VC++ 6.0 sp3+/ATL3.0創(chuàng)建,使用的操作系統(tǒng)是:安裝了Office2000的Windows2000。

            開始:
            Office AddIn 是一個(gè)可以動(dòng)態(tài)擴(kuò)充和增強(qiáng)的Com 自動(dòng)化組件,可以控制任何的Office應(yīng)用程序。微軟的Office2000和以后的版本都支持創(chuàng)建Add_Ins的一個(gè)新的、統(tǒng)一的應(yīng)用設(shè)計(jì)架構(gòu)。AddIn通常都被置于一個(gè)ActiveX動(dòng)態(tài)庫中(進(jìn)程內(nèi)服務(wù)器),并且能被用戶動(dòng)態(tài)的從主程序中引導(dǎo)和卸載。
            Office AddIn 必須實(shí)現(xiàn) _IDTExtensibility2 接口。IDTExtensibility2接口定義于MSADDin Designer typelibrary (MSADDNDR.dll/MSADDNDR.tlb)文件中。一般在/Program Files/Common Files/Designer目錄下。
            接口象這樣定義:
            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);
            };
            所有的Com AddIn繼承于IDTExtensibility2,而且必須實(shí)現(xiàn)他的五個(gè)方法。
            當(dāng)AddIn被引導(dǎo)和卸載的時(shí)候,OnConnection 和 OnDisconnection, 就像他們的名字顯示的一樣。AddIn程序可以被引導(dǎo),也可以在應(yīng)用程序使用過程中被用戶啟動(dòng)或者通過自動(dòng)化和enumerator ext_Connect 指示連接到那些模塊。當(dāng)一組Com AddIn組件被改變,那么OnAddinsUpdate被調(diào)用。OnStartupComplete 只有在應(yīng)用程序使用過程中啟動(dòng)Com AddIn組件時(shí)才被調(diào)用,如果AddIn在主應(yīng)用程序被關(guān)掉的時(shí)候斷開與主應(yīng)用程序的連接,那么OnBeginShutdown 被調(diào)用。

            注冊AddIn組件:
            使用主應(yīng)用程序注冊AddIn組件,我們需要在注冊表目錄:
            HKEY_CURRENT_USER\Software\Microsoft\Office\<TheOfficeApp>\Addins\<ProgID> 下創(chuàng)建兩個(gè)子鍵,這里ProgID指的是Addin Com對象的唯一標(biāo)識(shí)符。別的入口通過AddIn提供的關(guān)于他自己的信息和制定的引導(dǎo)選項(xiàng)給主應(yīng)用程的是:
            FriendlyName – 字符串 – 主應(yīng)用程序顯示的這個(gè)AddIn程序的名字。
            Description – 字符串 – 對AddIn的描述.
            LoadBehavior - DWORD 值. –一個(gè)決定AddIn怎樣被主應(yīng)用程序引導(dǎo)的值的組合。 設(shè)置成 0x03 表示主應(yīng)用程序啟動(dòng)時(shí)引導(dǎo),設(shè)置成0x08表示由用戶來激活。
            CommandLineSafe - DWORD 值. 0x01(TRUE) 或者 0x00(FALSE).
            對于所有值和可選項(xiàng)的完整描述,請參考MSDN。

            創(chuàng)建一個(gè)小的Com AddIn:
            現(xiàn)在我們了解了足夠的知識(shí),應(yīng)該朝前一步編寫一個(gè)小的Outlook2K COM addin。創(chuàng)建一個(gè)新的ATL COM Appwizard 工程,命名為OutlookAddin。記住如果你把他命名成別的,他可能會(huì)不能運(yùn)行(開個(gè)玩笑)。
            在向?qū)У牡谝粋€(gè)對話框中接收默認(rèn)的服務(wù)器類型Dynamic Link Library(DLL),檢查Allow merging of proxy-stub code,選擇這個(gè)可選項(xiàng),點(diǎn)擊完成。接著點(diǎn)擊OK,產(chǎn)生工程文件。
            下一步,點(diǎn)擊Insert->New ATL Object菜單項(xiàng),通過從Category中選擇Objects從Objects列表中選擇Simple Object插入一個(gè)ATL simple object到工程中。點(diǎn)擊Next,輸入”AddIn”作為ShortName,在屬性表里選上Support ISupportErrorInfo。接受剩下的默認(rèn)選項(xiàng),然后點(diǎn)擊OK。
            到現(xiàn)在為止,向?qū)б呀?jīng)給我們了一個(gè)置于動(dòng)態(tài)鏈接庫中的自動(dòng)化兼容的、DispInterface-savvy的進(jìn)程內(nèi)的Com對象。默認(rèn)的情況下,一個(gè)加到Com對象上的指定注冊值的注冊腳本文件被提交給我們。Build這個(gè)工程,看看一切是否運(yùn)行良好。
            如果你想我一樣雄心勃勃,起碼在繼續(xù)往下進(jìn)行前還應(yīng)該編譯你工程中的.idl文件。現(xiàn)在就去做吧。
            接下來我們?yōu)锳ddIn寫一些特定的代碼去實(shí)現(xiàn)IDTExtensibility2 接口。在類視圖里,我們在CAddIn類上右鍵點(diǎn)擊,選擇Implement Interface,這將帶出ATL Implement Interface 向?qū)А|c(diǎn)擊Add Typelib,在Browse Typelibraries對話框里向下滾動(dòng),選上Microsoft Add-in Designer(1.0),點(diǎn)擊OK。在AddinDesignerObjects列表中選擇_IDTExtensibility2接口點(diǎn)擊OK。
            向?qū)镮DTExtensibility2接口的五個(gè)方法中每一個(gè)生成默認(rèn)的實(shí)現(xiàn),將他們加到CAddIn類中,并且更新 COM_INTERFACE_MAP()宏。當(dāng)然在加有些有用的代碼之前每個(gè)方法都只會(huì)返回E_NOTIMPL。現(xiàn)在,為ComAddIn進(jìn)行必要的注 冊,我們的Com AddIn已經(jīng)就緒了。
            使用主應(yīng)用程序注冊我們的Addin組件。如果是outlook2000,打開工程的AddIn.rgs注冊腳本文件。把下面的代碼加到文件的結(jié)尾。
            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'
            }
            }
            }
            }
            }
            }
            }
            既然我們希望在程序啟動(dòng)的時(shí)候AddIn被引導(dǎo),那么LoadBehavior設(shè)置為3。現(xiàn)在Build這個(gè)工程。如果一切順利,那么將會(huì)創(chuàng)建成功并且注 冊了這個(gè)AddIn。為了測試這個(gè)AddIn,我們要運(yùn)行這個(gè)工程并輸入完整的outlook.exe的完整的路徑(\Program Files\Microsoft Office\Office\Outlook.exe),或者在注冊了這個(gè)DLL之后從VC++IDE環(huán)境外運(yùn)行outlook。如果你的AddIn被成 功的注冊了,那么在outlook里,點(diǎn)擊Tools->Options,在Other頁點(diǎn)擊Advanced Options->COM Addins,我們的AddIn應(yīng)該已經(jīng)出現(xiàn)在可獲得的AddIns的列表中。字符串是我們在腳本中為'FriendlyName'指定的值。
            AddIn可以被編寫來執(zhí)行各種不同的任務(wù)。典型的,包括為outlook添加一些界面元素,比如工具條和菜單項(xiàng),而且用戶可以控制AddIn。通過點(diǎn)擊這個(gè)工具條按鈕和菜單項(xiàng),用戶可以實(shí)現(xiàn)AddIn的功能。接下來我們將制作這樣一個(gè)工具條和附加的菜單項(xiàng)。

            命令與征服:
            在Office應(yīng)用程序中,菜單和工具條被組合在一個(gè)名叫“CommandBars “的完全可編程的集合中。CommandBars通常是可共享可編程的對象,并且作為所有的office應(yīng)用程序的一部分被暴露。CommandBars 代表一個(gè)同一的機(jī)制,通過他可以將單個(gè)的工具條和菜單項(xiàng)加到相應(yīng)的應(yīng)用程序里。每一個(gè)CommandBars由幾個(gè)獨(dú)立的CommandBar對象組成。 每一個(gè)CommandBar又由CommandBarControl對象集合組成,這個(gè)集合被叫做CommandBarControls。
            CommandBarControls代表了一個(gè)復(fù)雜的對象和組成它的子對象層次。一個(gè)CommandBarControl能被包含在一個(gè) CommandBar中,并且通過控件的CommandBar屬性訪問。最后每一個(gè)在控件的CommandBarControls集合中的 CommandBarControl即可能是CommandBarComboBox、CommandBarButton(工具條按鈕)也可能是 CommandBarPopup(彈出式菜單)。我很希望我能畫出一個(gè)代表這個(gè)對象層次的圖例,但是我很不擅長這個(gè)(我很誠實(shí)!)。我保證在MSDN中一 定有關(guān)于MS Office CommandBars描述的圖例。
            在我們的AddIn里,我想加入以下的界面元素:
            ? 在一個(gè)新的工具條里加入兩個(gè)位圖按鈕。
            ? 在“Tool“菜單里添加一個(gè)新的帶位圖的彈出式菜單項(xiàng)。
            首先,我們應(yīng)該將office和outlook的類型庫導(dǎo)入到我們的工程中。我們打開stdAfx.h,然后添加以下語句:
            #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;
            注意:你應(yīng)該改變這些路徑,是他們匹配你安裝的office的路徑。
            好了,現(xiàn)在讓我們來看看代碼。首先式ToolBand和ToolBar Button。
            在outlook模塊里,Application 對象位于代表整個(gè)應(yīng)用程序的對象層次的最頂層。通過他的ActiveExplorer 方法我們可以得到代表當(dāng)前窗口的Explorer對象。下來我們使用GetCommandBars方法得到CommandBars對象(他是 outlook工具條和菜單項(xiàng)的集合)。我們使用CommandBars集合的Add方法加上相應(yīng)的參數(shù)就可以添加一個(gè)新的工具條。如果想向工具條中加入 按鈕只需要得到工具條的CommandBarControls集合,接著調(diào)用他的Add方法。最后我們?yōu)槟切?yīng)于按鈕的 CommandBarButton對象(我們可以用它來設(shè)置按鈕的風(fēng)格和別的屬性,比如標(biāo)題、提示和文本等等)。
            代碼片斷如下:
            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
            //..........
            我們用相似的方法來給outlook的Tools菜單添加菜單項(xiàng),我們照以下方法做。CommandBars的ActiveMenuBar屬性返回一個(gè)表 示在Application容器中活動(dòng)的菜單。我們通過GetControls方法找到活動(dòng)的菜單控件集合。我們想要加入一個(gè)彈出式的菜單項(xiàng)到 outlook的Tools菜單(第6個(gè)菜單項(xiàng)),我們從Activemenubars控件集合中可以找到第6個(gè)菜單項(xiàng),直接調(diào)用Add方法創(chuàng)建一個(gè)新的 菜單項(xiàng)并且將他連接到Tools菜單。這里沒有什么新東西。
            相應(yīng)的代碼片斷如下所示:
            //......
            //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;
            }
            點(diǎn)擊F5,如果一切都沒問題,那么工程將成功建立,并且你將第一次看見你的AddIn程序的運(yùn)行。現(xiàn)在我們運(yùn)行outlook來測試我們的AddIn。在'Executable for Debug'對話框,設(shè)置outlook可執(zhí)行程序的當(dāng)前路徑,現(xiàn)在我們準(zhǔn)備測試。在outlook中點(diǎn)擊Tools->Option,點(diǎn)擊Other頁面,點(diǎn)擊Advanced Options。在Advanced Option對話框中,點(diǎn)擊Com AddIns 按鈕。接著從可獲得的AddIns列表中選擇我們的AddIn并點(diǎn)擊OK。當(dāng)我們的AddIn被引導(dǎo),一個(gè)停靠工具條將被創(chuàng)建,你也可以看到你加入到Tools菜單的菜單項(xiàng)。
            他們就在那里!一個(gè)有你寫的AddIn的outlook,一個(gè)帶有很酷的工具條和新的菜單項(xiàng)的擴(kuò)展的outlook!感謝ATL!你的小于50Kb的AddIn同樣提供了輕量級(jí)的有意義的Com服務(wù)。享受這一刻吧!

            單單放置兩個(gè)工具條按鈕和一個(gè)菜單項(xiàng)并沒有什么用處,除非我們寫命令處理代碼和響應(yīng)他們的事件。現(xiàn)在我們回到正題。當(dāng)然在這里,點(diǎn)擊不同的按鈕和菜單項(xiàng),我們緊緊彈出簡單的對話框。這就是你添加AddIn功能的地方。從CRM 工具、自動(dòng)聯(lián)系管理、郵件通知、郵件過濾到高級(jí)的文檔管理到各種各樣的應(yīng)用,Com AddIns可以執(zhí)行各種各樣的任務(wù)的驗(yàn)證。
            CommandBarButton控件暴露了一個(gè)點(diǎn)擊事件(當(dāng)用戶點(diǎn)擊一個(gè)Command Bar 按鈕時(shí)觸發(fā))。當(dāng)用戶點(diǎn)擊工具條按鈕或者是點(diǎn)擊菜單項(xiàng)的時(shí)候我們將使用這個(gè)事件去運(yùn)行代碼。對于這些,我們的Com AddIn對象不得不處理_CommandBarButtonEvents事件。點(diǎn)擊事件被聲明如下:
            //...
            //....Office objects typelibrary
            //....

            [id(0x00000001), helpcontext(0x00038271)]
            void Click(
            [in] CommandBarButton* Ctrl,
            [in, out] VARIANT_BOOL* CancelDefault);

            //....
            //...
            我們不得不做所有我們能做的事情去實(shí)現(xiàn)那些將被事件源通過規(guī)范的連接點(diǎn)協(xié)議調(diào)用的接收器接口(無論什么時(shí)候一個(gè)工具條按鈕或菜單項(xiàng)被點(diǎn)擊)。通過回調(diào)函數(shù)我們可以得到一個(gè)源CommandBarButton 對象的指針和一個(gè)用來接受和取消默認(rèn)操作的布爾值。就像實(shí)現(xiàn)一個(gè)dispatch接收器接口一樣,那也不是什么新東西,作為一個(gè)ATL程序員你可能要花一段時(shí)間去做這些。
            但是對于那些非初始化的,ATL為ATLCom對象提供兩個(gè)模板類IDispEventImpl<> 和 IDispEventSimpleImpl<> ,這為IDispatch接口提供了實(shí)現(xiàn)。我更喜歡用輕量級(jí)的IDispEventSimpleImpl,因?yàn)樗恍枰硗獾念愋蛶煨畔ⅰD愕念惥o緊源于 IDispEventSimpleImpl<>。建立你的接收器映射,通過_ATL_SINK_INFO結(jié)構(gòu)體設(shè)置你的回調(diào)參數(shù),最后調(diào)用 DispEventAdvise 和 DispEventUnadvise從源接口連接和斷開。對于我們的工具條按鈕和菜單項(xiàng),如果我們要寫一個(gè)單一的回調(diào)函數(shù)來處理所有的事件,那么,一旦我 們有一個(gè)指向觸發(fā)事件的CommandBarButton的指針,我們可以使用GetCaption去得到這個(gè)按鈕的文本,在這個(gè)基礎(chǔ)上,我們可以執(zhí)行一 些選擇性的動(dòng)作。但是對于這個(gè)例子,我們?yōu)槊恳粋€(gè)事件編寫一個(gè)回調(diào)函數(shù)。
            下面是編寫的步驟:
            使你的類繼承于IDispSimpleEventImpl-第一個(gè)參數(shù)是封裝在ActiveX控件中的子窗口的ID。但是對于我們來說,它可以是任何預(yù)先定義的唯一標(biāo)識(shí)事件源的整數(shù)(在這里指的是第一個(gè)工具條按鈕)。
            class ATL_NO_VTABLE CAddin :
            public CComObjectRootEx < CComSingleThreadModel>,
            .....
            .....
            public IDispEventSimpleImpl<1,CAddin,&__uuidof(Office::_CommandBarButtonEvents>
            建立回調(diào)函數(shù)-第一個(gè)我們定義的,如下所示:
            void __stdcall OnClickButton(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
            接下來我們使用_ATL_SINK_INFO結(jié)構(gòu)去描述回調(diào)參數(shù)。打開AddIn.h文件,在文件頂部添加如下聲明:
            ? extern _ATL_FUNC_INFO OnClickButtonInfo;
            接著打開AddIn.cpp,添加如下定義:
            ? _ATL_FUNC_INFO OnClickButtonInfo ={CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}};
            OnClickButton是非常基礎(chǔ)的,就像下面的:
            ? void __stdcall CAddin::OnClickButton(IDispatch* /*Office::_CommandBarButton* */ Ctrl,
            ? VARIANT_BOOL * CancelDefault)
            ? {
            ? USES_CONVERSION;
            ? CComQIPtr<Office::_CommandBarButton> pCommandBarButton(Ctrl);
            ? //the button that raised the event. Do something with this...
            ? MessageBox(NULL, "Clicked Button1", "OnClickButton", MB_OK);
            ?
            ? }
            我們使用ATL宏BEGIN_SINK_MAP() 和 END_SINK_MAP()建立接收器消息映射。接收器消息映射由SINK_ENTRY_XXX組成。接收器消息映射提供定義事件的Dispatch ID和處理他的成員函數(shù)。
            ? BEGIN_SINK_MAP(CAddin)
            ? SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01,
            ? OnClickButton, &OnClickButtonInfo)
            ? END_SINK_MAP()
            現(xiàn)在每一件事情都到位了,我們不得不使用DispEventAdvise() and DispEventUnadvise()連接和斷開事件源.我們的CAddIn類的OnConnection() 和OnDisconnection()僅僅是替代了這些。對于DispEventAdvise() and DispEventUnadvise()的參數(shù)分別是事件源上的任何的接口和任何被期望的事件源上的接口。
            //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);
            為我們的命令按鈕和菜單項(xiàng)實(shí)現(xiàn)Dispatch 接收器是很相似的,寫處理代碼并且連接和斷開他們就像上面的描述。如果每一步都進(jìn)行的暢通無阻,在你Rebuild你的程序并且運(yùn)行它。無論什么時(shí)候,按鈕和菜單項(xiàng)被點(diǎn)擊,你的回調(diào)函數(shù)將被執(zhí)行。

            添加屬性頁:
            在這篇文章里我們最后要學(xué)會(huì)去做的是添加我們自己的“Option“屬性頁到outlook的Tools->Option的屬性表中。
            下來我們要加一個(gè)頁到outlook的option菜單里作為我們我們的AddIn的一部分。我們將象ActiveX控件一樣實(shí)現(xiàn)實(shí)現(xiàn)屬性頁。當(dāng)用戶點(diǎn)擊 Tools->Option菜單項(xiàng),應(yīng)用程序?qū)ο蟀l(fā)出一個(gè)OptionsPagesAdd事件(通過outlook對象模塊中的 _ApplicationEvents接口)。
            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);
            };

            OptionsPagesAdd事件傳遞給我們我們一個(gè)PropertyPages Dispatch接口,他的Add方法用來添加頁。Add方法的參數(shù)是我們的控件的ProgID和新的頁的標(biāo)題文本。相似的,我們調(diào)用Remove()方法和要?jiǎng)h除頁的索引來刪除頁。
            現(xiàn)在我們來加一個(gè)ActiveX復(fù)合控件。我們點(diǎn)擊Insert->new ATL Object.從Category中選擇Controls,從Object列表中選擇Lite Composite Control,點(diǎn)擊OK。在ShortName中輸入PropPage,在屬性頁面選上Support ISupportErrorInfo選項(xiàng)。點(diǎn)擊Ok,接受所有的默認(rèn)選項(xiàng)。
            現(xiàn)在我們要來實(shí)現(xiàn)PropertyPage接口。在類視圖里右鍵點(diǎn)擊CPropPage,選擇Implement Interface,點(diǎn)擊Add TypeLib按鈕。選中Microsoft Outlook 9.0 Object Library 點(diǎn)擊OK。從接口列表中選擇PropertyPage點(diǎn)擊OK。
            向?qū)ё詣?dòng)為PropertyPage接口添加三個(gè)方法:Apply()、Get_Dirty()、GetPageInfo() 。現(xiàn)在做下面的修改,在Com Map中把這一行:
            COM_INTERFACE_ENTRY(IDispatch)
            改成:
            COM_INTERFACE_ENTRY2(IDispatch,IPropPage)
            以排除不明確的地方。
            接下來實(shí)現(xiàn)IDispatch,我們使用IDispatchImpl<>模板類。我們在CPropPage類的聲明部分用以下代碼:
            public IDispatchImpl < Outlook::PropertyPage,&__uuidof(Outlook::PropertyPage),
            &LIBID_OUTLOOKADDINLib>
            替換掉:
            class ATL_NO_VTABLE CPropPage :
            public CComObjectRootEx<CComSingleThreadModel>,
            public IDispatchImpl<IPropPage, &IID_IPropPage, &LIBID_TRAILADDINLib>,
            ....
            ....
            public PropertyPage
            從PropPage.h文件的頂部刪掉多余的#import語句。類型庫已經(jīng)在stdAfx.h中導(dǎo)入了,因此這里沒有必要再導(dǎo)入。
            下來我們要連接和斷開ApplicationEvents接口,并為他寫回調(diào)函數(shù)。你已經(jīng)知道該做什么了。我們再次使用 IDispEventSimpleImpl<>為ApplicationEvents建立Dispatch接收器,更新接收器映射,為 OptionsAddPage事件寫回調(diào)函數(shù)。因?yàn)槲覀兌啻问褂昧薎DispEventSimpleImpl<>, 我們?yōu)槊恳粋€(gè)接口事件使用TypeDef。代碼片段如下:
            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)> 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");
            }
            最后,在OnConnection和OnDisConnection里,調(diào)用DispEventAdvise 和 DispEventUnadvise連接和斷開ApplicationEvents。現(xiàn)在一切就緒,我們ReBuild工程。下來點(diǎn)擊F5,點(diǎn)擊 Outlook的Tools->Options菜單。你應(yīng)該看見了我們新加的頁。但是當(dāng)我們點(diǎn)擊這個(gè)新的頁,一個(gè)對話框?qū)⒊霈F(xiàn)告訴我們屬性頁不能被 顯示。發(fā)生了什么?難道我們的辛苦勞動(dòng)白費(fèi)了?
            發(fā)生這個(gè)情況的原因是:盡管我們的屬性頁創(chuàng)建了,但是outlook并沒有得到關(guān)于這個(gè)頁的鍵盤行為的任何信息。IOleControl的 GetControlInfo方法的ATL的默認(rèn)實(shí)現(xiàn)返回E_NOTIMPL,因此包容器無法為這個(gè)屬性頁和包容器處理擊鍵事件。因此我們的頁不能被顯 示。修改這個(gè)問題,只需重載GetControlInfo()方法,讓他返回S_OK。
            在.PropPage.h里添加如下聲明:
            STDMETHOD(GetControlInfo)(LPCONTROLINFO lpCI);
            我們在PropPage.cpp文件里重載GetControlInfo()方法,僅僅將返回值改為S_OK,代碼如下:
            STDMETHODIMP CPropPage::GetControlInfo(LPCONTROLINFO lpCI)
            {
            return S_OK;
            }
            就是這些了。現(xiàn)在再次Build工程,點(diǎn)擊outlook的tools->Option,激活我們的頁,現(xiàn)在我們的屬性頁應(yīng)該正確無誤的顯示了。
            我們的學(xué)習(xí)要結(jié)束了。我們能在office里我們能做的事情無窮無盡。因?yàn)樵谝粋€(gè)AddIn里你可以獲得父應(yīng)用程序的內(nèi)部對象模塊,你能做所有主應(yīng)用程序能做的事,或者更多。另外你也能使用別的接口比如MS Assistant(并不直接關(guān)聯(lián)到應(yīng)用程序)。沒有做不到的只有想不到的。


            posted on 2007-06-20 16:58 迷宮の未來 閱讀(2175) 評(píng)論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2025年7月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(10)

            隨筆檔案

            文章檔案

            最新隨筆

            搜索

            積分與排名

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久久综合中文字幕| 久久精品国产网红主播| 伊人久久精品线影院| 国产伊人久久| 成人综合久久精品色婷婷| 亚洲AV乱码久久精品蜜桃| 久久偷看各类wc女厕嘘嘘| 色综合久久88色综合天天| 一日本道伊人久久综合影| 国产精品一久久香蕉国产线看观看| 国产激情久久久久影院小草 | 中文精品久久久久人妻| 亚洲AV无码久久寂寞少妇| 美女久久久久久| 久久本道伊人久久| 国产色综合久久无码有码| 久久99精品国产99久久6| 久久久久亚洲AV成人片| 亚洲欧美日韩精品久久亚洲区 | 伊人久久大香线蕉综合热线| 国产精品99久久久久久人| 久久久久久久女国产乱让韩| 久久激情亚洲精品无码?V| 国产精品久久久久久搜索| 久久夜色精品国产亚洲| 性高湖久久久久久久久AAAAA| 国产美女久久久| 狠狠色婷婷久久一区二区三区| 香蕉久久夜色精品国产尤物| 久久精品亚洲男人的天堂| 9999国产精品欧美久久久久久 | 国产AV影片久久久久久| 久久99国产精品久久久| 亚洲国产精品无码久久久秋霞2| 亚洲另类欧美综合久久图片区| 品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 国产91久久综合| 精品乱码久久久久久夜夜嗨| 99久久精品这里只有精品| 99久久精品免费看国产免费| 国产精品午夜久久|