用VS2010开发支持MFC的office插件

来源:互联网 发布:重生之软件帝国 编辑:程序博客网 时间:2024/06/16 01:16

本文原创,转载请注明出处:http://blog.csdn.net/banket004

通过参考网上的文章,再加上自己的摸索,走了不少弯路,终于用VS2010开发出MS Office 2007的插件.特写此文以作备忘.(记性太差,之前开发过一次,后来再开发又忘了怎么用^ _^)


下面以开发Excel插件为例,其他office插件开发过程类似.

如果不需要支持MFC,可参考此文章http://blogs.msdn.com/b/jensenh/archive/2006/12/08/using-ribbonx-with-c-and-atl.aspx的方法,开发更简便,本人已实验过.

首先创建一个支持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 ProgrammableInprocServer32 = s '%MODULE%'{val ThreadingModel = s 'Apartment'}TypeLib = s '{D4E1923A-0EF0-42B2-B00E-32978D0E8C66}'Version = s '1.0'}}}

根据文章http://www.vckbase.com/index.php/wv/1442里面的描述,针对Excel插件,往文件添加以下代码:

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 hereMessageBox(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,点击那个大大的笑脸,即可看到熟悉的窗口.



注:由于第一次发文章,前面的截图没先保存再上传,而是直接粘贴上来,导致截图丢失.如果哪个步骤不清楚,欢迎交流.

示例代码下载地址:http://download.csdn.net/detail/banket004/5891395

本文参考以下文章:

http://www.vckbase.com/index.php/wv/1442

http://www.vckbase.com/index.php/wv/1452

http://blogs.msdn.com/b/jensenh/archive/2006/12/08/using-ribbonx-with-c-and-atl.aspx


原创粉丝点击