NoRemove Software
NoRemove Microsoft
NoRemove Office
NoRemove Outlook
NoRemove Addins
NativeAddin.Connect
val Description = s
5、设置调试。将调试的命令属性设为预启动的Excel,则在调试项目时会自动启动Excel并加载本加载项。
以上步骤完成后,即可完成项目创建。我们可以在OnConnection函数中增加一个弹窗,用以证明加载项的确已被成功加载。调试程序时,可能会出现注册表写入失败,因为WIN7的权限设置问题,需要在DllRegisterServer和DllUnregisterServer函数的最上方添加一行代码ATL::AtlSetPerUserRegistration(true);
二、自定制ribbon菜单
自定制ribbon菜单需要实现
IRibbonExtensibility接口,此接口在程序库Microsoft Office 12.0 Object Library<2.4> 中找到(名称的版本号会随Office安装版本的不同而略有区别)。
1、实现接口。将继承声明中的&LIBID_Office改为&__uuidof(__Office),在stdafx.h中对命名空间和方法重命名以避免冲突。如下所示
#import "C:\Program Files (x86)\Common Files\DESIGNER\MSADDNDR.DLL" auto_rename auto_search raw_interfaces_only rename_namespace("AddinDesign")
#import "C:\Program Files (x86)\Common Files\Microsoft Shared\OFFICE12\MSO.DLL" auto_rename auto_search raw_interfaces_only rename_namespace("Office") rename("RGB","MsoRGB") rename("SearchPath","MsoSearchPath")
using namespace AddinDesign;
using namespace Office;
2、添加ribbon描述xml。可用MSDN中的例子修改。
3、实现GetCustomUI接口。这个直接用MSDN中的源码即可。
STDMETHOD(GetCustomUI)(BSTR RibbonID, BSTR * RibbonXml)
if(!RibbonXml)
return E_POINTER;
*RibbonXml = GetXMLResource(IDR_XML1);
return S_OK;
HRESULT CConnect::HrGetResource(int nId, LPCTSTR lpType, LPVOID* ppvResourceData, DWORD* pdwSizeInBytes)
HMODULE hModule = _AtlBaseModule.GetModuleInstance();
if (!hModule)
return E_UNEXPECTED;
HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(nId), lpType);
if (!hRsrc)
return HRESULT_FROM_WIN32(GetLastError());
HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
if (!hGlobal)
return HRESULT_FROM_WIN32(GetLastError());
*pdwSizeInBytes = SizeofResource(hModule, hRsrc);
*ppvResourceData = LockResource(hGlobal);
return S_OK;
BSTR CConnect::GetXMLResource(int nId)
LPVOID pResourceData = NULL;
DWORD dwSizeInBytes = 0;
HRESULT hr = HrGetResource(nId, _T("XML"),
&pResourceData, &dwSizeInBytes);
if (FAILED(hr))
return NULL;
CComBSTR cbstr(dwSizeInBytes, reinterpret_cast<LPCSTR>(pResourceData));
return cbstr.Detach();
4、自定制按钮图片和按钮事件。先将IConnect设为默认的响应接口。对自定义图片,需要在按钮上用image属性标志自定义图片,在根节点上用loadImage回调来加载自定义图片。对按钮事件,需在按钮上用onAction回调。所有回调函数的签名可以参见Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3),在项目的idl文件中加入回调函数接口的声明,并在Connect.h中将其实现
BEGIN_COM_MAP(CConnect)
COM_INTERFACE_ENTRY(IConnect)
COM_INTERFACE_ENTRY2(IDispatch, IConnect)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
COM_INTERFACE_ENTRY(IRibbonExtensibility)
END_COM_MAP()
interface IConnect : IDispatch{
HRESULT HistoryButtonClicked([in] IDispatch* ribbon);
HRESULT GetImage([in] BSTR *pbstrImageId, [out, retval] IPictureDisp ** ppdispImage);
STDMETHOD(HistoryButtonClicked)(IDispatch* ribbon);
STDMETHOD(GetImage)(BSTR *pbstrImageId,IPictureDisp ** ppdispImage);
按钮事件的具体实现代码就很简单了,这里说一下加载图片的接口的实现。第一个参数表示图片的名称,是image属性的值,第二个参数是将图片的信息输出给Excel。这里可以将图片放入资源中,利用GDI+将其转为Bitmap并通过OleCreatePictureIndirect将其存入ppdispImage。
STDMETHODIMP CConnect::GetImage(BSTR *pbstrImageId,IPictureDisp ** ppdispImage)
int pngId(0);
pngId = lexical_cast<int>(*pbstrImageId);
catch(...)
return E_UNEXPECTED;
using namespace Gdiplus;
LPVOID pResourceData = NULL;
DWORD len = 0;
HRESULT hr = HrGetResource(pngId,_T("PNG"), &pResourceData, &len);
BYTE* lpRsrc = reinterpret_cast<BYTE*>(pResourceData);
if (!lpRsrc)
return E_UNEXPECTED;
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem,lpRsrc,len);
GlobalUnlock(m_hMem);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
PICTDESC pic;
memset(&pic, 0, sizeof pic);
Bitmap *png = Bitmap::FromStream(pstm);
HBITMAP hMap = NULL;
png->GetHBITMAP(Color(),&hMap);
pic.picType = PICTYPE_BITMAP;
pic.bmp.hbitmap = hMap;
OleCreatePictureIndirect(&pic,IID_IPictureDisp,true,(LPVOID*)ppdispImage);
return S_OK;
因为资源中图片的ID是数字,而xml中image的属性值是字符串,所有将图片的ID以字符串的方式放在image中,在回调中再转为数字以查找资源,方法比较笨,等以后发现更好的方法再做修改。
经过上述步骤 即可完成自定义ribbon菜单的全部过程。
三、操作Excel
想在加载项中访问并操作Excel,需要在stdafx.h中加入如下语句,注意实际路径和本机安装Office的路径相关
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB"
#import "D:\\Program Files (x86)\\Microsoft Office\\Office12\\EXCEL.EXE" rename( "DialogBox", "ExcelDialogBox" ) rename( "RGB", "ExcelRGB" ) rename( "CopyFile", "ExcelCopyFile" ) rename( "ReplaceText", "ExcelReplaceText" ) exclude( "IFont", "IPicture" ) no_dual_interfaces
using namespace Excel;
然后在OnConnection中加入初始化代码即可访问Excel了
Excel::_ApplicationPtr pExcel;
STDMETHOD(OnConnection)(LPDISPATCH Application, ext_ConnectMode ConnectMode, LPDISPATCH AddInInst, SAFEARRAY * * custom)
pExcel.GetActiveObject("Excel.Application");
return S_OK;
做完上述步骤,则基本上完成了一个com加载项的初始化过程,下面可以开始根据实际业务需求进行开发了