用VS2010開發支援MFC的office外掛
通過參考網上的文章,再加上自己的摸索,走了不少彎路,終於用VS2010開發出MS Office 2007的外掛.特寫此文以作備忘.(記性太差,之前開發過一次,後來再開發又忘了怎麼用^ _^)
下面以開發Excel外掛為例,其他office外掛開發過程類似.
首先建立一個支援MFC的ATL DLL解決方案,介面如下:
在"Allow merging of proxy/stub code"和"Support MFC"兩個選項前打勾,然後點選"Finish",到此最原始的解決方案已經建成.接著通過類嚮導給專案增加一個"ATL Simple Object"類.除了輸入類名之外,"ISupportErrorInfo
接下來在上面建立的ATL類Cexceladdin中實現庫"Microsoft Add-In Designer<1.0>"中的"_IDTExtensibility2"介面,後面需要用到此介面中的"OnConnection","OnDisconnection","OnAddInsUpdate","OnStartupComplete"和"OnBeginShutdown"方法.這四個方法是宿主(Office軟體)執行時載入和解除安裝外掛相關的.
以上操作用"Implement Interface Wizard"即可完成,如下圖:
編譯一下,沒有什麼錯誤.到這裡還不能在Excel軟體的介面看到外掛,還需要實現下面另一個介面,也是我花費最多時間摸索的地方.
還是在類Cexceladdin中實現介面,通過介面實現嚮導,選擇"Microsoft Office 12.0 Object Library<2.4> "庫(office 2007版本對應的就是12),新增"IRibbonExtensibility"介面.如下圖:
新增好介面之後,再編譯,會出現以下錯誤資訊:
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\stdafx.h(41): warning C4278: 'RGB': identifier in type library 'C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL' is already a macro;
use the 'rename' qualifier
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\stdafx.h(41): warning C4278: 'RGB': identifier in type library 'C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL' is already a macro; use the 'rename' qualifier
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\stdafx.h(41): warning C4278: 'RGB': identifier in type library 'C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL' is already a macro; use the 'rename' qualifier
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\stdafx.h(41): warning C4278: 'RGB': identifier in type library 'C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL' is already a macro; use the 'rename' qualifier
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\stdafx.h(41): warning C4278: 'RGB': identifier in type library 'C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL' is already a macro; use the 'rename' qualifier
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\debug\mso.tlh(2082): error C2011: 'IAccessible' : 'struct' type redefinition
1> c:\program files\microsoft sdks\windows\v7.0a\include\oleacc.h(556) : see declaration of 'IAccessible'
1>d:\my documents\visual studio 2010\projects\2007exceladdin\2007exceladdin\debug\mso.tlh(2169): error C2504: 'IAccessible' : base class undefined
根據錯誤資訊的提示,大概意思是說"Iaccessible"這個結構體重複定義了.只要把stdafx.h檔案中的
#import "C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL" raw_interfaces_only, raw_native_types, no_namespace, named_guids, auto_search
改成
#import "C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSO.DLL" raw_interfaces_only, raw_native_types, no_namespace, named_guids, auto_search rename("IAccessible", "IMSOAccessible")
即可消除此錯誤.
將GetCustomUI的實現改成以下:
STDMETHOD(GetCustomUI)(BSTR RibbonID, BSTR * RibbonXml)
{
if (!RibbonXml)
return E_POINTER;
*RibbonXml = SysAllocString(
_T("<customUI xmlns=\"http://schemas.microsoft.com/office/2006/01/customui\">")
_T(" <ribbon>")
_T(" <tabs>")
_T(" <tab id=\"CustomTab\"" )
_T(" label=\"Test\">" )
_T(" <group id=\"CustomGroup\"" )
_T(" label=\"Tools\">" )
_T(" <button id=\"CustomButton\"" )
_T(" imageMso=\"HappyFace\"")
_T(" size=\"large\"")
_T(" label=\"Button\"")
_T(" onAction=\"OnStart\"/>")
_T(" </group>")
_T(" </tab>")
_T(" </tabs>")
_T(" </ribbon>")
_T("</customUI>")
);
return (*RibbonXml ? S_OK : E_OUTOFMEMORY);
}
同時先把_IDTExtensibility2介面對應的4個方法"OnConnection"等改成如下:
STDMETHOD(OnConnection)(LPDISPATCH Application, ext_ConnectMode ConnectMode, LPDISPATCH AddInInst, SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnAddInsUpdate)(SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnStartupComplete)(SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnBeginShutdown)(SAFEARRAY * * custom)
{
return S_OK;
}
也就是把各個函式的
return E_NOTIMPL;
改為
return S_OK;
此時最功能最簡單的外掛基本完成,但這個外掛還不知道他的宿主是誰,是Excel還是Word?這個需要修改一下"exceladdin.rgs"檔案,它是負責往登錄檔註冊外掛資訊的.此檔案原始內容大致如下:
HKCR
{
NoRemove CLSID
{
ForceRemove {0F1D173C-61FE-44E6-BBA2-7D16D37C9DAC} = s 'exceladdin Class'
{
ForceRemove Programmable
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
TypeLib = s '{D4E1923A-0EF0-42B2-B00E-32978D0E8C66}'
Version = s '1.0'
}
}
}
HKCU
{
Software
{
Microsoft
{
Office
{
Excel
{
Addins
{
'My2007exceladdin.exceladdin'
{
val FriendlyName = s 'Excel2007 Addin'
val Description = s 'Excel2007 Addin'
val LoadBehavior = d '00000003'
val CommandLineSafe = d '00000001'
}
}
}
}
}
}
}
編譯之後,執行excel 2007,沒發現這個外掛.看來還是少了什麼.經過對以上參考文章原始碼的研究,發現文章裡少講了一部分,還需要在rgs檔案裡的HKCR部分增加程式碼,修改後是這樣子:
HKCR
{
My2007exceladdin.exceladdin.1 = s 'ExcelAddin Class'
{
CLSID = s '{0F1D173C-61FE-44E6-BBA2-7D16D37C9DAC}'
}
My2007exceladdin.exceladdin = s 'ExcelAddin Class'
{
CLSID = s '{0F1D173C-61FE-44E6-BBA2-7D16D37C9DAC}'
CurVer = s 'My2007exceladdin.exceladdin.1'
}
NoRemove CLSID
{
ForceRemove {0F1D173C-61FE-44E6-BBA2-7D16D37C9DAC} = s 'exceladdin Class'
{
ForceRemove Programmable
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
TypeLib = s '{D4E1923A-0EF0-42B2-B00E-32978D0E8C66}'
Version = s '1.0'
}
}
}
注意紅色部分,別改錯了,不然也是外掛執行不起來的.到這裡外掛就可以顯示了.晒一下效果圖:
雖然可以運行了,但按鈕點選會出錯,接下來也是麻煩事....
==========================================>>
首先, 通過嚮導在介面Iexceladdin中增加引數為IDispatch*的OnStart方法, 這個方法在GetCustomUI函式中的onAction呼叫到.大致如圖:
接下來在OnStart方法中新增測試程式碼,方便測試此方法是否有執行到.在此示例中直接新增彈窗提示,如下:
STDMETHODIMP Cexceladdin::OnStart(IDispatch* RibbonControl)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your implementation code here
MessageBox(NULL, _T("Test"), _T("Excel"), MB_OK);
return S_OK;
}
最後,開啟"exceladdin.h"檔案,找到對映巨集程式碼部分,增加"COM_INTERFACE_ENTRY2(IDispatch, Iexceladdin)", 如下程式碼所示:
BEGIN_COM_MAP(Cexceladdin)
COM_INTERFACE_ENTRY(Iexceladdin)
COM_INTERFACE_ENTRY2(IDispatch, Iexceladdin)//此語句不會自動新增,需手動新增,否則Ribbon的按鈕無法對映對應的函式
COM_INTERFACE_ENTRY2(IDispatch, _IDTExtensibility2)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
COM_INTERFACE_ENTRY(IRibbonExtensibility)
END_COM_MAP()
到這裡按鈕的事件基本完成,編譯之後,再次開啟Excel2007,點選那個大大的笑臉,即可看到熟悉的視窗.
注:由於第一次發文章,前面的截圖沒先儲存再上傳,而是直接貼上上來,導致截圖丟失.如果哪個步驟不清楚,歡迎交流.
本文參考以下文章: