前兩天,在CSDN瞎逛悠,見(jiàn)一老兄問(wèn)到此問(wèn),卻沒(méi)有人作答(頂?shù)娜说惯€不少,國(guó)內(nèi)的論壇是不是都這樣?),還發(fā)了些牢騷,俺也順便跟著發(fā)了點(diǎn)牢騷:)
于是坐下來(lái)靜下心研究了一下,今日終于成了正果,不敢私吞成果,特搬弄出來(lái),讓大家分享分享(切,無(wú)非就是虛榮而已啦,把自己說(shuō)得那么偉大?!)!
點(diǎn)擊這里下載工程源代碼
我看還是做一篇教程寫(xiě)好了,寫(xiě)清楚一點(diǎn),呵呵:)
哦,先說(shuō)明白,俺用的是VC6啊(俺的工程是以O(shè)utlook插件為例的,因?yàn)镃SDN上那位老兄問(wèn)的就是Outlook,所以咱就對(duì)癥下藥啦,不過(guò)Office系列的插件都大同小異啦,只是做具體功能時(shí)才會(huì)天差地別哦):
1. 新建ATL工程,全部默認(rèn),完成Wizard。
2. 新建ATL Object,選擇Simple Object,其他默認(rèn)。
3. 打開(kāi)stdafx.h,這里需要導(dǎo)入幾個(gè)庫(kù):
#import "C:\Documents and Settings\Administrator\My Documents\msoffice9\mso9.dll" rename_namespace("Office")
using namespace Office;
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB" rename_namespace("VBE6")
using namespace VBE6;
#import "C:\Documents and Settings\Administrator\My Documents\msoffice9\MSOUTL9.OLB" named_guids,rename_namespace("MSOutlook")
using namespace MSOutlook;
4. 打開(kāi).rgs文件,需要添加幾行注冊(cè)腳本:
HKCU
{
NoRemove Software
{
NoRemove Microsoft
{
NoRemove Office
{
NoRemove Outlook
{
NoRemove Addins
{
'OutlookAddin.COutlookAddinSample'
{
val FriendlyName = s 'Azhi sample'
val Description = s 'Azhi sample'
val LoadBehavior = d '00000003'
val CommandLineSafe = d '00000000'
}
}
}
}
}
}
}
這里注意了:OutlookAddin.COutlookAddinSample這玩意是該COM的ProgID。
5. 現(xiàn)在打開(kāi)COutlookAddinSample.h文件。
a. 先引入一個(gè)庫(kù):
#import "C:\Program Files\Common Files\Designer\MSADDNDR.DLL" raw_interfaces_only, raw_native_types, no_namespace, named_guids
b. 在接口(類(lèi))定義中添加對(duì)_IDTExtensibility2接口的支持:
class ATL_NO_VTABLE CCOutlookAddinSample :
.
.
public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2, &LIBID_AddInDesignerObjects>
.
c. 添加基于_IDTExtensibility2接口方法的重載:
.
// _IDTExtensibility2
public:
STDMETHOD(OnConnection)(IDispatch * Application, ext_ConnectMode ConnectMode, IDispatch * AddInInst, SAFEARRAY * * custom);
STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom);
STDMETHOD(OnAddInsUpdate)(SAFEARRAY * * custom);
STDMETHOD(OnStartupComplete)(SAFEARRAY * * custom);
STDMETHOD(OnBeginShutdown)(SAFEARRAY * * custom);
.
.
d. 定義一個(gè)變量保存Outlook的Application實(shí)例:
private:
CComPtr<MSOutlook::_Application> m_spApp;
e. 修改COM_MAP映射表:
.
// COM_INTERFACE_ENTRY(IDispatch) // 刪除此行,以下兩行皆為新添加的
COM_INTERFACE_ENTRY2(IDispatch, ICOutlookAddinSample)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
.
.
至此,COutlookAddinSample.h文件的第一輪修改已經(jīng)完成。
好了,我們接著改(為我們的COM按鈕添加事件響應(yīng)):
f. 回到文件頂部,添加申明:
extern _ATL_FUNC_INFO OnClickButtonInfo1;
extern _ATL_FUNC_INFO OnClickButtonInfo2;
g. 回到類(lèi)定義處,添加對(duì)按鈕事件的支持:
class ATL_NO_VTABLE CCOutlookAddinSample :
.
.
public IDispEventSimpleImpl<1,CCOutlookAddinSample,&__uuidof(Office::_CommandBarButtonEvents)>,
public IDispEventSimpleImpl<2,CCOutlookAddinSample,&__uuidof(Office::_CommandBarButtonEvents)>
.
h. 在類(lèi)中添加類(lèi)型定義:
typedef IDispEventSimpleImpl<1,CCOutlookAddinSample, &__uuidof(Office::_CommandBarButtonEvents)> CommandButtonEvents1;
typedef IDispEventSimpleImpl<2,CCOutlookAddinSample, &__uuidof(Office::_CommandBarButtonEvents)> CommandButtonEvents2;
i. 添加事件連接映射:
BEGIN_SINK_MAP(CCOutlookAddinSample)
SINK_ENTRY_INFO(1,__uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton1, &OnClickButtonInfo1)
SINK_ENTRY_INFO(2,__uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton2, &OnClickButtonInfo2)
END_SINK_MAP()
j. 添加兩個(gè)方法以響應(yīng)按鈕事件:
// ICOutlookAddinSample
public:
VOID __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
VOID __stdcall OnClickButton2(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
k. 定義兩個(gè)變量保存按鈕實(shí)例:
private:
.
.
CComPtr<Office::_CommandBarButton> m_spButton1;
CComPtr<Office::_CommandBarButton> m_spButton2;
.
恩,至此該文件就全部修改完畢了(別得意哦,下面還有很多事要做啊)。
6. 打開(kāi)COutlookAddinSample.cpp文件。
a. 定義事件連接變量:
_ATL_FUNC_INFO OnClickButtonInfo1 = {CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}};
_ATL_FUNC_INFO OnClickButtonInfo2 = {CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}};
b. 添加按鈕事件響應(yīng)方法:
VOID __stdcall CCOutlookAddinSample::OnClickButton1(IDispatch* /*Office::_CommandBarButton* */ Ctrl,VARIANT_BOOL * CancelDefault)
{
MessageBox(GetForegroundWindow(),"¹þ¹þ£¬²âÊÔ°´Å¥£¡","sample",MB_OK | MB_ICONINFORMATION);
}
VOID __stdcall CCOutlookAddinSample::OnClickButton2(IDispatch* /*Office::_CommandBarButton* */ Ctrl,VARIANT_BOOL * CancelDefault)
{
MessageBox(GetForegroundWindow(),"¹þ¹þ£¬²âÊÔµ¯³ö²Ëµ¥£¡","sample",MB_OK | MB_ICONINFORMATION);
}
c. 添加繼承自_IDTExtensibility2接口的方法:
STDMETHODIMP CCOutlookAddinSample::OnConnection(IDispatch * Application, ext_ConnectMode ConnectMode, IDispatch * AddInInst, SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHODIMP CCOutlookAddinSample::OnDisconnection(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHODIMP CCOutlookAddinSample::OnAddInsUpdate(SAFEARRAY * * custom)
{
return E_NOTIMPL;
}
STDMETHODIMP CCOutlookAddinSample::OnStartupComplete(SAFEARRAY * * custom)
{
return E_NOTIMPL;
}
STDMETHODIMP CCOutlookAddinSample::OnBeginShutdown(SAFEARRAY * * custom)
{
return E_NOTIMPL;
}
休息一下,到這里,這個(gè)插件的基本框架已經(jīng)搭建完畢。
以下我們接著完成按鈕和彈出菜單的創(chuàng)建以及按鈕事件到方法的連接:
e. 在OnConnection方法中首先獲得CommandBars接口指針
.
CComQIPtr<MSOutlook::_Application> spApp(Application);
ATLASSERT(spApp);
CComPtr<MSOutlook::_Explorer> spExplorer;
spExplorer = spApp->ActiveExplorer();
ATLASSERT(spExplorer);
CComPtr<Office::_CommandBars> spCmdBars;
HRESULT hr = spExplorer->get_CommandBars(&spCmdBars);
.
.
在Outlook中比Word以及Excel都要多用一個(gè)方法才能得到CommandBars。
在Word以及Excel中直接可以通過(guò)Application對(duì)象獲得:spApp->get_CommandBars(&spCmdBars);
f. 接著創(chuàng)建自己的CommandBar:
CComVariant vName("sample");
CComVariant vPos(Office::msoBarTop);
CComVariant vTemp(VARIANT_TRUE);
CComVariant vEmpty(DISP_E_PARAMNOTFOUND,VT_ERROR);
CComPtr<Office::CommandBar> spNewCmdBar;
spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp);
// 得到CommandBar的Controls集合,通過(guò)集合的Add方法添加自己的按鈕以及其他
CComPtr<Office::CommandBarControls> spBarControls;
spBarControls = spNewCmdBar->GetControls();
ATLASSERT(spBarControls);
g. 為自己的CommandBar添加按鈕:
CComVariant vToolBarType(Office::msoControlButton);
CComVariant vShow(VARIANT_TRUE);
CComPtr<Office::CommandBarControl> spNewBar;
spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
ATLASSERT(spNewBar);
CComQIPtr<Office::_CommandBarButton> spCmdButton1(spNewBar);
ATLASSERT(spCmdButton1);
spCmdButton1->PutStyle(Office::msoButtonIconAndCaption);
spCmdButton1->PutVisible(VARIANT_TRUE);
spCmdButton1->PutCaption("sample1");
spCmdButton1->PutEnabled(VARIANT_TRUE);
spCmdButton1->PutTooltipText("sample1");
spCmdButton1->PutTag("sample1");
HBITMAP hBmp = (HBITMAP)LoadImage(_Module.GetResourceInstance(),MAKEINTRESOURCE(IDB_SAMPLE),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
::OpenClipboard(NULL);
::EmptyClipboard();
::SetClipboardData(CF_BITMAP,(HANDLE)hBmp);
::CloseClipboard();
::DeleteObject(hBmp);
hr = spCmdButton1->PasteFace();
if(FAILED(hr))return hr;
h. 好了,我們就要?jiǎng)?chuàng)建彈出菜單了,看好了:
// 首先我們需要?jiǎng)?chuàng)建一個(gè)Popup類(lèi)型的CommandBarButton
// 實(shí)際上微軟稱(chēng)他為:CommandBarPopup
CComVariant vPopupType(Office::msoControlPopup);
spNewBar = spBarControls->Add(vPopupType, vEmpty, vEmpty, vEmpty, vShow);
ATLASSERT(spNewBar);
CComQIPtr<Office::CommandBarPopup> spNewPopup(spNewBar);
ATLASSERT(spNewPopup);
spNewPopup->PutVisible(VARIANT_TRUE);
spNewPopup->PutCaption("popup");
spNewPopup->PutEnabled(VARIANT_TRUE);
spNewPopup->PutTooltipText("popup");
spNewPopup->PutTag("popup");
// 同樣可以有自己的Controls集合
// 以后往該集合中添加的按鈕都是這個(gè)Popup中的一個(gè)菜單項(xiàng)啦
CComPtr<Office::CommandBarControls> spPopupControls;
spPopupControls = spNewPopup->GetControls();
ATLASSERT(spPopupControls);
i. 恩,創(chuàng)建一個(gè)菜單項(xiàng)試試:
spNewBar = spPopupControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
ATLASSERT(spNewBar);
CComQIPtr<Office::_CommandBarButton> spCmdButton2(spNewBar);
ATLASSERT(spCmdButton2);
spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
spCmdButton2->PutVisible(VARIANT_TRUE);
spCmdButton2->PutCaption("sample2");
spCmdButton2->PutEnabled(VARIANT_TRUE);
spCmdButton2->PutTooltipText("sample2");
spCmdButton2->PutTag("sample2");
hBmp = (HBITMAP)LoadImage(_Module.GetResourceInstance(),MAKEINTRESOURCE(IDB_SAMPLE),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
::OpenClipboard(NULL);
::EmptyClipboard();
::SetClipboardData(CF_BITMAP,(HANDLE)hBmp);
::CloseClipboard();
::DeleteObject(hBmp);
hr = spCmdButton2->PasteFace();
if(FAILED(hr))return hr;
j. 現(xiàn)在可以保存變量以及進(jìn)行事件連接了:
m_spApp = spApp;
m_spButton1 = spCmdButton1;
hr = CommandButtonEvents1::DispEventAdvise((IDispatch*)m_spButton1);
if(FAILED(hr))return hr;
m_spButton2 = spCmdButton2;
hr = CommandButtonEvents2::DispEventAdvise((IDispatch*)m_spButton2);
if(FAILED(hr))return hr;
到這里,OnConnection方法就已經(jīng)完成了:)
7. 在OnDisconnection方法中斷開(kāi)事件的連接:
HRESULT hr = CommandButtonEvents1::DispEventUnadvise((IDispatch*)m_spButton1);
if(FAILED(hr))return hr;
hr = CommandButtonEvents2::DispEventUnadvise((IDispatch*)m_spButton2);
if(FAILED(hr))return hr;
好了,終于又完成一篇文章了。
寫(xiě)的很累啊,即使是像我這樣CP,也覺(jué)得很累啊(老了?!)!
呵呵,也不知道寫(xiě)的是不是清楚,或許大家看起來(lái)很覺(jué)得不知所然吧?
唉,文學(xué)細(xì)胞太少,看樣子不是做文學(xué)的料啊,實(shí)在不明白的還是請(qǐng)大家讀我的工程源代碼吧:)