VC6编写Office插件
更新时间:2024-05-20 20:48:01 阅读量: 综合文库 文档下载
- VC6编写串口助手推荐度:
- 相关推荐
用VC6.0编写Word插件(Office2K、XP、03)
作者:hjphy 源代码下载
最近因为工作的需要,学习了一下Office插件的编写方法。在走了不少弯路以后,最后终于把编写插件的原理给搞清楚了,不敢独享,拿出来跟大家共享一下。下面就以Word 2003为例,向大家简单介绍一下。 第一步,利用向导生成一个ATL COM AppWizard的新工程。
图1
在向导的第一个对话框中,服务器类型选择Dynamic Link Library(DLL),然后单击Finish即可。
图2
然后,选取菜单Insert->New ATL Object项,在弹出的ATL对象向导对话框中选中相应Objects对应右侧的Simple Object选项,点击下一步。
图3
在弹出的对话框中ShortName中输入相应名称,点确定完成插入ATL对象。
图4
这样一个简单的基于ATL的COM组件工程就建立成功了。
第二步,通过导入类型库来实现_IDTExtensibility2接口。在ClassView中的新加的类上点鼠标右键,在弹出的右键菜单中选Implement Interface项。
图5
在弹出的实现接口对话框中点击Add Typelib
图6
在弹出的Browse Type Libraries对话框中,选取Microsoft Add-in Designer(1.0)子项,点OK按钮
图7
在弹出的接口列表对话框中选中_IDTExtensibility2接口,点OK按钮完成导入
这样的话,系统将会自动为你生成空的五个所需接口函数,分别是OnConnection、OnDisconnection、OnAddInsUpdate、OnStartupComplete、OnBeginShutdown。
第三步,通过上面的两个步骤,我们的插件框架已经形成,但是Office怎么知道启动的时候要来把我们的插件Load起来呢?Office的不同组件,例如Word、Excel、Outlook等怎么知道去Load自己的插件呢?答案就是在注册表中加入相应的键值。打开文件视图FileView—>Resource File中的rgs文件,加入以下代码: HKCU {
Software {
Microsoft { Office { Word { Addins {
''TestAddin.SimAddin'' {
val FriendlyName = s ''WORD Custom Addin'' val Description = s ''Word Custom Addin'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000001'' } } }
} } } }
以上代码由三个需要注意的地方:
1. Office下面的那个子项代表了这个插件是属于那个组件,Word
、Excel、Outlook等等。 2. Addins下面的那个子项要写成你添加的COM组件的名字,千万不要照着我的工程的名字照抄。 3. 所有的值两边加的都是单引号,而且要用英文下的单引号,不能用双引号。
这样一个Office插件的框架才算完成,你可以在OnConnection函数中加一些测试代码,看看有没有执行到,如果执行成功才能继续,否则检查上面的步骤有没有错误。
第四步,同时需要import两个office的文件,一个是MSO.dll,另一个是MSWORD.OLB。这两个文件可以在以下位置找到(具体位置与office安装路径有关):
C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE11 C:\\Program Files\\Microsoft Office\\OFFICE11 然后在stdafx.h中加入如下语句:
#import \ \\
rename_namespace(%using namespace Office; #import
\
Files\\\\Common
Files\\\\Microsoft
Shared\\\\VBA\\\\VBA6\\\\VBE6EXT.olb\
rename_namespace(%using namespace VBE6; #import
\
Files\\\\Microsoft
Office\\\\OFFICE11\\\\MSWORD.OLB\
rename(\
#import \ \\
rename_namespace(%using namespace Word;
加完以上代码以后一定要编译一下,看看是否能够成功。引入这两个文件的原因,主要是为了引入一些变量类型,为后面的创建UI作准备。
最后一步,编写代码。在OnConnection加入如下代码: CComPtr < Office::_CommandBars> spCmdBars;
CComQIPtr
HRESULT hr = spApp->get_CommandBars(&spCmdBars); if(FAILED(hr)) return hr;
ATLASSERT(spCmdBars);
CComVariant vName(\
CComPtr
CComVariant vTemp(VARIANT_TRUE);
CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp);
CComPtr < Office::CommandBarControls> spBarControls; spBarControls = spNewCmdBar->GetControls(); ATLASSERT(spBarControls);
CComVariant vToolBarType(1); CComVariant vShow(VARIANT_TRUE);
CComPtr < Office::CommandBarControl> spNewBar;
spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar);
CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar); ATLASSERT(spCmdButton);
HBITMAP hBmp =(HBITMAP)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDB_BITMAP),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
::OpenClipboard(NULL); ::EmptyClipboard();
::SetClipboardData(CF_BITMAP, (HANDLE)hBmp); ::CloseClipboard(); ::DeleteObject(hBmp);
spCmdButton->PutStyle(Office::msoButtonIconAndCaption); hr = spCmdButton->PasteFace(); if (FAILED(hr)) return hr;
spCmdButton->PutVisible(VARIANT_TRUE); spCmdButton->PutCaption(OLESTR(\ spCmdButton->PutEnabled(VARIANT_TRUE);
spCmdButton->PutTooltipText(OLESTR(\ spCmdButton->PutTag(OLESTR(\ spNewCmdBar->PutVisible(VARIANT_TRUE);
m_spCmdButton = spCmdButton;
这样,再次打开word,就可以看到如图一所示的界面效果了。
图9
但是点击时没有响应,最后就让我们来解决这个问题。
1. 在COutlookAddin继承类中加入IDispEventSimpleImpl继承,代码如下: class ATL_NO_VTABLE COutlookAddin :
public CComObjectRootEx
IDispEventSimpleImpl<1,COutlookAddin,&__uuidof(Office::_CommandBarButtonEvents)>
2. 声明_ATL_SINK_INFO结构回调参数信息。在OutlookAddin.h文件中加入下面语句: // 按钮事件响应信息声明
extern _ATL_FUNC_INFO OnClickButtonInfo; 在OutlookAddin.cpp文件中加入定义语句,如下: // 按钮事件响应信息定义
_ATL_FUNC_INFO OnClickButtonInfo ={CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}}; 3. 加入Sink映射,如下: EGIN_SINK_MAP(COutlookAddin)
SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton1, &OnClickButtonInfo)
SINK_ENTRY_INFO(2, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickButton2, &OnClickButtonInfo)
SINK_ENTRY_INFO(3, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickMenu, &OnClickButtonInfo) END_SINK_MAP()
4. 加入事件函数。在OutlookAddin.h中加入声明:
void __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault); 在OutlookAddin.cpp中加入实现: // 工具条按钮1点击事件响应函数
void __stdcall CWordAddin::OnClickButton1(IDispatch * { }
MessageBox(NULL, \
/*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault)
5. 最后,打开或断开与接口的连接。方法如下
在OnConnection接口函数的最后部分,加入下面代码来打开连接: 在OnConnection接口函数的最后部分,加入下面代码来打开连接: CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton); 在OnDisconnection接口函数中,加入下面代码来断开连接:
CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);
综上所述,编写一个简单的office的插件,其实并不难,只要按照步骤一步一步进行,肯定能成功,如果大家在使用过程中有什么疑问,欢迎一起探讨。 相关链接:Office2000下内部COM插件的编程实现
最新评论 [发表评论] [文章投稿]
哈哈,终于发现了。用那个title一样的dll就可以。我靠一下! ( pjl110 发表于 2007-11-13 17:16:00)
我没有MSADDNDR.TLB文件,谁发一下给我啊!谢谢!
pjllovelihua@163.com ( pjl110 发表于 2007-11-13 16:53:00)
请问下,怎么将VC6.0与Delphi联系起来编程,我是想写一个程序,用Delphi做前台的按钮控制,而当点击了按钮时能通过Delphi连接到VC上,用VC做后台控制,请问该怎么做啊????????????????
谢谢!! ( dreamf 发表于 2007-9-24 15:04:00)
你好,我是按你的操作进行的,怎么编译StdAfx.h中加入如下语句:
#import \ \\
rename_namespace(%using namespace Office;
#import \amespace(%using namespace VBE6;
#import \itWindowsEx\
#import \ \\
rename_namespace(%using namespace Word; 后
,
就
出
现
了
错
误
,
说
查看所有评论 推荐给好友 打印
“cannot compile the file 'D:\\TestAddin\\StdAfx.h':no compile tool is associated with the file extension ”
可以帮我解决这个问题吗??谢谢!!!!!!!!!! ( dreamf 发表于 2007-9-24 14:57:00)
你好,我是按你的操作进行的,怎么编译StdAfx.h中加入如下语句:
#import \ \\
rename_namespace(%using namespace Office;
#import \amespace(%using namespace VBE6;
#import \itWindowsEx\
#import \ \\
rename_namespace(%using namespace Word; 后
,
就
出
现
了
错
误
,
说
“cannot compile the file 'D:\\TestAddin\\StdAfx.h':no compile tool is associated with the file extension ”
可以帮我解决这个问题吗??谢谢!!!!!!!!!! ( dreamf 发表于 2007-9-24 14:56:00)
您好!我想开发一个QQ的插件,不知道按你这个方法可不可以实现? ( linewei 发表于 2007-4-9 2:57:00)
我最近在写一个excel插件,道理跟上面的差不多,但是我遇到了一些困难,我想捕获鼠标消息,比如鼠标点击了一个单元格,那么单元格的数据立刻会被显示到另外的一个控件中,怎么才能对office中的鼠标消息编写响应函数。上面的例子捕获的是WM_COMMAND消息 ( eyesmart 发表于 2007-4-6 17:02:00)
vc vb dll 控件学习网( 源码 ) http://konny520.diy.myrice.com
vc vb dll 控件学习网( 源码 )
http://konny520.diy.myrice.com ( konny 发表于 2007-3-5 15:58:00)
一个菜鸟问题:请问ATL这些编程方法在哪里可以找到,比如我们怎么就知道要去实现某一个接口的? ( w_xbei 发表于 2007-2-27 14:17:00)
使用VC++ ATL实现Office的COM插件
作者:useresu 下载源代码 摘要
本文介绍了一种使用VC++ ATL(Active Template Library),利用IDTExtensibility2接口,为Microsoft Word加入功能简单的COM插件(addin),加入工具栏按钮和菜单等可视部件,并为其加入响应事件的方法,并在最后简单说明了实现与Office宏混合编程的方法。
说到Office相关的编程,大家首先想到的可能是VBA(Visual Basic for Application),事实上, ATL也是一种很好的工具。这里介绍的就是一种基于ATL的Office编程方法,实现的功能很简单,仅仅是一个示例,步骤如下:
1、在visual C++编程环境下,利用向导生成一个名为WordAddin的ATL COM Appwizard工程:
在向导的第一个对话框中server type单选框选择默认的服务器类型Dynamic Link Library(DLL),下面的三个复选框中选择Allow merging of proxy-stub code选项。然后单击Finish,这样一个空的ATL project就产生了(如图 一)。
图一
2、插入我们的ATL object:
选择菜单Insert—>Insert new ATL object,出现new ATL object向导对话框,左边的category列表中选择object,右边相应的选择simple object,单击下一步(Next)(如图 二):
图二
在第二个对话框names属性页的“Short Name:”中填入Addin(如图三):
图三
在Attribute属性页中,选中Support IsupportErrorInfo复选框,单击OK(如图四):
图四
这样就产生了一个类名为WordAddin的ATL COM object,编译(build)该工程看是否一切正常。
2、用IDTExtensibility2实现CAddin类:
IDTExtensibility2是定义在MSADDin Designer typelibrary(MSADDNDR.dll/MSADDNDR.tlb)中的库文件,该文件一般在C:\\Program Files/Common Files/Designer目录下。IDTExtensibility2 库提供了 5 个可用来操纵插件以及宿主应用程序的事件: OnConnection、OnDisconnection、OnAddInsUpdate、OnStartupComplete 和 OnBeginShutdown。这些事件的具体功能和用法可查阅MSDN。用向导来实现IDTExtensibility2接口:切换到classview页,右键点击Caddin类,在弹出的菜单中选择Implement Interface,出现Implement Interface对话框,选择Add Tylpelib按钮(如图 五):
图五
(单击OK,)在出现的Browse Typelibraries对话框中选择Microsoft Add-in Designer(1.0) (如图六):
图六
单击OK,在AddinDesignerObjects属性页中选择IDTExtensibility2(如图七):
图七
再单击OK。这样向导就在ATL COM object中添加了IDTExtensibility2的5个具体事件,并对他们
进行了一些默认的初始设置,同时还更新了COM_INTERFACE_MAP()。
4、注册插件到它的宿主程序:
打开文件视图FileView—>Resource File中的Addin.rgs文件,加入以下代码: HKCU {
Software {
Microsoft { Office { Word { Addins {
''WordAddin.Addin'' {
val FriendlyName = s ''WORD Custom Addin'' val Description = s ''Word Custom Addin'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000001'' } } } } } } }
5、重新编译(build)该工程注册我们的插件。
6、运行,选择Executable File为word 2000,注意要选择正确的路径,如果运行成功,则插件已经加入到word中。
7、给插件添加菜单和按钮:
这里简单的介绍一下Office 的命令条:在Office中,菜单栏、工具栏和弹出式菜单都叫做命令条(Command Bar对象)。所有这些种类的命令条中都可以包括其他命令条和不限数量的控件,如相对工具栏这个命令条而言,按钮就是它的控件。有的控件(如菜单)本身又是命令条,可以多级嵌套。所有的命令条可用一个CommandBars集合控制。CommandBars集合是一个通用的可共享且可编程的对象,通过它的Add()方法可以为其添加一个CommandBar 对象(即命令条),而每个 CommandBar 对象都有一个CommandBarControls 集合,这个集合里包含了这个命令条里的所有控件。使用 CommandBar 对象的 Controls 属性可引用命令条中的控件。
现在给word加入一个工具条及其按钮和一个菜单:
首先在工程中加入office和Word的类型库,在stdafx.h文件中加入以下代码:
#import \ rename_namespace(%using namespace Office;
#import \ rename_namespace(%using namespace Outlook;
注意:一定要把路径改为和office的安装路径一致。
在Word对象模型中,Application对象是代表整个工程的最高级对象,我们可以用它的GetCommandBars方法得到CommandBars对象,由于CommandBars对象是Word所有工具条和菜单项的集合,所以就可以通过调用它的Add方法添加新的工具条。然后为工具条添加新的按钮,其实方法一样简单,我们可以调用CommandBar的GetControls方法得到工具条的CommandBarControls集合,如前所说,CommandBarControls集合是该工具条所有控件的集合,按钮自
然是其中之一,那么接下来我们就可以通过调用CommandBarControls集合的Add方法添加一个新的按钮了。下面是具体的实现代码: CComQIPtr<_Application> spApp(Application);
ATLASSERT(spApp); m_spApp = spApp;
HRESULT hr = AppEvents::DispEventAdvise(m_spApp);
if(FAILED(hr)) return hr;
CComPtr
if(FAILED(hr)) return hr;
ATLASSERT(spCmdBars);
// now we add a new toolband to Word // to which we''ll add 2 buttons CComVariant vName(\ CComPtr 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
// 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(\
_bstr_t bstrTipText(OLESTR(\
// 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); m_spButton = 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
// Word''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); hr = spCmdButton->PasteFace(); if (FAILED(hr)) return hr;
spCmdButton->PutVisible(VARIANT_TRUE); spCmdButton->PutCaption(OLESTR(\ spCmdButton->PutEnabled(VARIANT_TRUE);
spCmdButton->PutTooltipText(OLESTR(\ spCmdButton->PutTag(OLESTR(\ spCmdButton2->put_OnAction(OLESTR(\
//show the toolband
spNewCmdBar->PutVisible(VARIANT_TRUE);
spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
//specify predefined bitmap spCmdButton2->PutFaceId(1758);
spCmdButton2->PutVisible(VARIANT_TRUE); spCmdButton2->PutCaption(OLESTR(\ spCmdButton2->PutEnabled(VARIANT_TRUE);
spCmdButton2->PutTooltipText(OLESTR(\ spCmdButton2->PutTag(OLESTR(\ spCmdButton2->PutVisible(VARIANT_TRUE);
spCmdButton2->put_OnAction(OLESTR(\添加菜单项的过程与之相类似:
首先通过调用CommandBars集合的get_ActiveMenuBar()方法得到一个CommandBar对象,这个对象代表当前的工程中的活动菜单,然后调用CommandBar的GetControls得到当前菜单的控件集合。尝试在Word的“格式”菜单(第5个菜单)中加入新的菜单项,调用CommandBarControls的GetItem(5)得到需要的“格式”菜单项,它也是一个CommandBarControls集合,(前面曾经提到,控件集是可以嵌套的),这样就可以通过调用它的Add方法添加新的菜单项了。具体的实现代码如下: _bstr_t bstrNewMenuText(OLESTR(\ CComPtr < Office::CommandBarControls> spCmdCtrls;
CComPtr < Office::CommandBarControls> spCmdBarCtrls; CComPtr < Office::CommandBarPopup> spCmdPopup; CComPtr < Office::CommandBarControl> spCmdCtrl;
// get CommandBar that is Word''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;
这时运行程序,可以看到添加的按钮和菜单项已经出现在Word中,还需要为其加入响应事件,这样才能真正的通过插件扩展Word的功能。
8、为我刚加入的按钮加入其响应事件:
ATL为COM对象的Idispatch接口提供了两个模板类:IDispEventImpl<>和IDispEventSimpleImpl<>,选择IDispEventSimpleImpl<>,因为它不需要额外的类型库信息,从IDispEventSimpleImpl<>继承一个类: class ATL_NO_VTABLE CAddin :
public CComObjectRootEx < CComSingleThreadModel>, .....
public IDispEventSimpleImpl<1,CAddin,
&__uuidof(Office::_CommandBarButtonEvents> 声明按钮点击事件的回调函数:
void __stdcall OnClickButton(Idispatch * /*Office::_CommandBarButton**/ Ctrl, VARIANT_BOOL * CancelDefault);
用_ATL_SINK_INFO结构描述回调的参数信息:打开CAddin.h文件,在其最上加入以下声明: extern _ATL_FUNC_INFO OnClickButtonInfo;(注意一定声明为外部变量) 然后打开CAddin.cpp文件为其加入以下定义: _ATL_FUNC_INFO OnClickButtonInfo =
{CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}}; 加入按钮点击事件的具体实现:
void __stdcall CAddin::OnClickButton(IDispatch*
/*Office::_CommandBarButton* */ Ctrl, VARIANT_BOOL * CancelDefault) {
USES_CONVERSION;
CComQIPtr pCommandBarButton(Ctrl);
//the button that raised the event. Do something with this... MessageBox(NULL, \
}
在接口映射宏中加入以下信息: BEGIN_SINK_MAP(CAddin)
SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents), /*dispid*/ 0x01,
OnClickButton, &OnClickButtonInfo) END_SINK_MAP()
最后在分别在CAddin 类的 OnConnection() 和OnDisconnection()中调用DispEventAdvise() 和 DispEventUnadvise()连接和断开连接消息来源。
到这里就实现了一个简单的COM插件,运行程序,点击工具栏上新加入的按钮,就可以弹出(\Button1\)消息框。
如果熟悉VBA编程,就可以把编写的宏作为按钮响应事件,只需调用按钮的put_OnAction()方法: spCmdButton->put_OnAction(OLESTR(\
本文对ATL并没有做深入的探讨,如果想了解ATL的知识,请参考:《ATL Internals》。
用VC6.0编写Word插件(Office2007篇)
作者:hjphy 下载源代码
简介
微软在06年底推出了两个重量级的产品:Vista和Office 2007。在Office2007中使用了新的UI方式。微软大力鼓吹这种叫做Ribbon的新的UI将会给使用Office的人带来更大的便利性。在Office插件方面,微软保持了对原有版本 (office 2K、XP、03) 的兼容性,都被放到一个叫做Add-In的Tab里面,如下图所示。
不过样子比较难看,到底有没有办法让界面做得更漂亮一点呢?答案是肯定的。本文假设读者已经阅读并理解了我的上一篇文章:
“用VC6.0编写Word插件(Office2K、XP、03)”
首先,引入新的Office的几个库文件,如果你的电脑安装了Office 2007,那么下面几个文件就会存在你的硬盘里面。
然后,原来的那个WordAddin需要继承一个名叫IRibbonExtensibility新的接口,并实现这个接口必须的函数raw_GetCustomUI。
然后,原来的那个WordAddin需要继承一个名叫IRibbonExtensibility新的接口,并实现这个接口必须的函数raw_GetCustomUI。
实现上边这个函数的目的是,因为Office2007在启动的时候就会询问插件的IRibbonExtensibility接口是否存在,如果该接口存在的话,就会调用这个接口的函数raw_GetCustomUI,在这个函数里面,我们必须把我们的UI通过XML的方式传给Office 2007,这样,在XML里面,我们可以自己定义我们需要的UI形式,样式非常丰富多彩,只有想不到,没有做不到。以下是一个简单的XML的例子:
我们可以把这个XML字符串放到我们的资源文件里面,并且在raw_GetCustomUI中传递给Office。以下是这个函数的简单实现。
到目前为止,在Word的UI上面我们应该可以看到我们的插件了。如下图所示。如果还不能看到的话,请检查前面的步骤是否有错误。
这时,当你点击按钮的时候,会发现没有任何反应。那么,该如何响应对应的按钮事件呢?这就需要我们修改两个地方。首先在XML字符串里面注明按钮的响应函数。
然后,在我们的组件里面新增这个接口函数。
一定要注意新增的这个接口函数必须与XML里面指定的相同。现在你再点击按钮的话,就会有一个对话框弹出来了。 总结
Office 2007系列的插件,跟以往的插件编写方法有了很大的不同。通过引入XML的UI安排方式,大大的增加了灵活性。本着与时俱进的精神,我们应该跟随微软的脚步,尽快掌握这个技术。
Office2000下内部COM插件的编程实现 选择自 snaill 的 Blog 译者:徐景周 下载示例源代码 简介
你也许曾在Office2000下的Word2000、Access2000、Excel2000、PowerPoint2000等软件中的工具条或菜单条资源中,看到一些其它软件加入的新的自定义工具条按钮或菜单条,当点击它们时,会有其不同的响应发生。下面,让我们也来实现这些功能,需要说明的是,在这里我们不用VB/VBA来实现它,而是用VC6中所带ATL(活动模板库)3.0来开发具有这种效果的Office2000内部COM插件。在Office2000中,不管是Word2000、Access2000、Excel120000、PowerPoint2000还是Outlook2000等,它们COM插件的编程方法及步骤都是极其相似的(除注册表中键值及导入相应类型库不同外)。
基础知识
一个Office2000下的内部COM插件必须实现一个_IDTExtensibility2派发接口,_IDTExtensibility2派发接口被定义在MSADDin Designer类型库(MSADDNDR.dll/MSADDNDR.tlb)中,通常位于<盘符>/Program Files/Common Files/Designer下。_IDTExtensibility2接口中必须实现下面五个接口函数(一般只需编写OnConnection和OnDisconnection中代码),分别如下:
1. OnConnection: 装载插件到内存时处理(可以通过自动化在程序启动时自动装载插件)。 2. OnDisconnection: 从内存中缷载插件时处理。 3. OnAddinsUpdate: COM插件改变时处理。
4. OnStartupComplete: 当应用程序启动时插件刚装载完成时处理。 5. OnBeginShutdown: 当应用程序关闭时插件刚缷载完成时处理。
注册插件
只有在正确注册了相应应用程序的内部COM插件时,才能被其应用程序加载上。需要在注册表中创建以下键值:
HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\
其中,TheOfficeApp表示相应程序名,如:Word、Outlook等,ProgID表示内部COM插件程序的唯一标识符的字符串表示形式,如:Outlook2000Addin.Addin等。 ProgID键值下主要创建以下四个键值:
1. FriendlyName: 字符串类型,插件的名称,将在相应程序的COM加载对话框中看到。 2. Description: 字符串类型,插件的描述信息。
3. LoadBehavior: DWORD类型,决定插件将以什么形式被装载。当其值为0x03时,为应用程序装载时被自动装载(一般使用此值)、当其值为0x08时,为用户控制激活装载。
4. CommandLineSafe: DWORD类型,命令行方式,可以设置为0x01(真)或0x00(假)。 其它键值的完整描述可参看最新MSDN。 具体实现
下面,我们将以创建一个Outlook2000的内部COM插件为示例,向你一步步的展现如何最小化的创建一个Office2000的内部插件的全过程。效果图如下所示:
打开VC6.0,在新建工程中选中ATL COM Appwizard,在右侧工程名中输入OutlAddin,点击下一步,接受默认选项Dynamic Link Library(DLL)不变,可以选中下面的Allow merging of proxy-stub code(允许合并代理/占位)复选框选项,点击Finish(完成)按钮完成工程创建。
接着,选取菜单Insert->New ATL Objec项,在弹出的ATL对象向导对话框中选中相应Objects对应右侧的Simple Object选项,点击下一步,在弹出的对话框中ShortName中输入OutlookAddin,如果需要的话,还可以在Attributes(属性页)中选中Support ISupportErrorInfo复选框选项,点OK完成插入ATL对象。 接着通过导入类型库来实现_IDTExtensibility2接口,编释好上面所建工程后,在ClassView中的COutlookAddin类上点鼠标右键,在弹出的右键菜单中选Implement Interface项。在弹出的实现接口对话框中点击Add Typelib,在弹出的Browse Type Libraries对话框中,向下滚动选取Microsoft Add-in Designer(1.0)子项,点OK按钮。在弹出的接口列表对话框中选中_IDTExtensibility2接口,点OK按钮完成导入。系统会自动为你生成空的上面所提到的五个所需接口函数。
接着注册编译好的插件,在FileView->Resource Files中,打开OutlookAddin.rgs注册文件,在该文件的最下面加入下面新的内部插件注册码:
// 新增Outlook2k内部插件注册键值 HKCU {
Software {
Microsoft { Office {
Outlook { Addins {
''OutlAddin.OutlookAddin'' {
val FriendlyName = s ''Outlook2000插件''
val Description = s ''使用ATL开发的Outlook2000的插件'' val LoadBehavior = d ''00000003'' val CommandLineSafe = d ''00000000'' }
} } } } } }
编译此工程,如果注册正确的话,将可以在Outlook中COM加载项的插件对话框中看到它的相应名称。在Office2000中加载或卸载COM插件,一般可以按下面步骤进行:
1. 如果已经将“COM 加载项”命令添加到了“工具”菜单,请跳到第 6 步。 2. 单击“工具”菜单中的“自定义”命令,然后单击“命令”选项卡。 3. 在“类别”框中,选择“工具”。
4. 将“COM 加载项”从“命令”框拖动到“工具”菜单。当“工具”菜单显示菜单命令时,将鼠标指针指向希望“COM 加载项”命令出现在菜单上的位置,然后释放鼠标按钮。 5. 单击“关闭”按钮。
6. 单击“工具”菜单中的“COM 加载项”命令,并执行下列操作之一:
● 要添加加载项,请选中“可使用的加载项”列表中加载项名称旁边的复选框。如果所需的加载项不在“可使用的加载项”列表中,请单击“添加”按钮,找到要添加的加载项,然后单击“确定”按钮。 ● 要从内存中卸载加载项,但希望在列表中保持其名称,请清除加载项名称旁边的复选框。
● 若要从列表中删除加载项的同时,将其从注册表文件中注册的加载项列表中删除,请选择加载项的名称,然后单击“删除”按钮。
在Office应用程序中,尽管菜单和工具栏按钮看上去不太一样,但实质上它们是相同类型的对象。CommandBars集合包含程序中的所有命令条,如:工具条和菜单条。每一个CommandBars集合都有一个CommandBar对象和它对应,CommandBar 对象可以包含其它的 CommandBar 对象,这些对象是作为按钮或菜单命令来用的。每一个CommandBar都将通过CommandBarControls 对象被引用,CommandBarControls又可以包含一组CommandBarControl对象。每一个CommandBarControl可以包含一个CommandBar对象,并可以通过它来存取控件属性。每一个CommandBarControl对象,实际是对应CommandBarControls中的控件集合。CommandBarControl可以有三种表现形式:
● 弹出式(CommandBarPopup):相当于菜单条的一个菜单项。
● 组合框(CommandBarComboBox):类似于工具条中组合框控件。它包括一个工具栏和紧接着工具栏的一个下拉箭头。单击该按钮,将显示出更多的带图标的菜单命令。
● 按钮(CommandBarButton):相当于标准的工具栏按钮,即带有图标的按钮。
在下面的示例程序中,我们将在Outlook2K中新建一个工具条并在其上添加二个按钮,并且在其菜单“工具”中新建一个菜单条,这些操作都可以在OnConnection接口函数中完成。
首先,我们需要在工程中导入Office和Outlook类型库,可以在Stdafx.h文件中加入下面语句(注意:其中路径可根据Office所装路径自行设定): // 导入工程所需Office2K及Outlook2K类型库
#import \Files\\Microsoft Office\\Office\\mso9.dll\rename_namespace(\named_guids
using namespace Office;
#import \raw_interfaces_only, named_guids
using namespace Outlook;
其次,让我们来在Outlook中新建一个工具条,并且在其上添加两个按钮。 代码如下: // 装卸插件时处理
STDMETHOD(OnConnection)(IDispatch * Application, {
// 新增一个工具条及其上两个位图按钮
CComVariant vName(\新增Outlook2K工具条插件\CComPtr
// 新增工具条位置 CComVariant vPos(1);
CComVariant vTemp(VARIANT_TRUE); // 临时
// 获取CommandBars接口
CComPtr
HRESULT hr = spExplorer->get_CommandBars(&spCmdBars); if(FAILED(hr))
return hr;
CComPtr < Office::_CommandBars> spCmdBars;
// Outlook应用接口_Application
CComQIPtr
ext_ConnectMode ConnectMode, IDispatch * AddInInst, SAFEARRAY * * custom)
ATLASSERT(spCmdBars);
CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
// 用Add方法在指定位置新增一工具条并让spNewCmdBar指向它 spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp);
// 获取新增工具条的CommandBarControls,从而在其上添加按钮 CComPtr < Office::CommandBarControls> spBarControls; spBarControls = spNewCmdBar->GetControls(); ATLASSERT(spBarControls);
//MsoControlType::msoControlButton = 1 CComVariant vToolBarType(1); //显示工具条
CComVariant vShow(VARIANT_TRUE);
CComPtr < Office::CommandBarControl> spNewBar; CComPtr < Office::CommandBarControl> spNewBar2;
// 用CommandBarControls中的Add方法新增第一个按钮,并让spNewBar指向它 spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar);
// 用CommandBarControls中的Add方法新增第二个按钮,并让spNewBar2指向它 spNewBar2 = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow); ATLASSERT(spNewBar2);
// 为每一个按钮指定_CommandBarButton接口,从面可以指定按钮的显示风格等 CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar); CComQIPtr < Office::_CommandBarButton> spCmdButton2(spNewBar2);
ATLASSERT(spCmdButton); ATLASSERT(spCmdButton2);
// 设置位图按钮风格,位图为32x32大小,将其放入剪切板中用PasteFace()贴在指定按钮上 HBITMAP hBmp =(HBITMAP)::LoadImage(_Module.GetResourceInstance(),
::OpenClipboard(NULL); ::EmptyClipboard();
::SetClipboardData(CF_BITMAP, (HANDLE)hBmp); ::CloseClipboard(); ::DeleteObject(hBmp); // 粘贴前设置显示风格
spCmdButton->PutStyle(Office::msoButtonIconAndCaption);
hr = spCmdButton->PasteFace(); if (FAILED(hr))
spCmdButton->PutVisible(VARIANT_TRUE); spCmdButton->PutCaption(OLESTR(\按钮1\spCmdButton->PutEnabled(VARIANT_TRUE);
spCmdButton->PutTooltipText(OLESTR(\按钮1提示信息\spCmdButton->PutTag(OLESTR(\按钮1标志\
// 显示新增工具条
spNewCmdBar->PutVisible(VARIANT_TRUE);
return hr;
MAKEINTRESOURCE(IDB_BITMAP),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
// 设置第二个工具条按钮风格
spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
// 第二个按钮指定位图为Outlook2K中预先定义的位图 spCmdButton2->PutFaceId(1760);
spCmdButton2->PutVisible(VARIANT_TRUE); spCmdButton2->PutCaption(OLESTR(\按钮2\spCmdButton2->PutEnabled(VARIANT_TRUE);
spCmdButton2->PutTooltipText(OLESTR(\按钮2提示信息\spCmdButton2->PutTag(OLESTR(\按钮2标志\spCmdButton2->PutVisible(VARIANT_TRUE);
m_spButton = spCmdButton; m_spButton2 = spCmdButton2; ??
接着,让我们在菜单\工具\中新建一个菜单条。 代码如下: _bstr_t bstrNewMenuText(OLESTR(\新增菜单条\CComPtr < Office::CommandBarControls> spCmdCtrls; CComPtr < Office::CommandBarControls> spCmdBarCtrls; CComPtr < Office::CommandBarPopup> spCmdPopup; CComPtr < Office::CommandBarControl> spCmdCtrl;
// 通过CommandBar获取Outlook主菜单
hr = spCmdBars->get_ActiveMenuBar(&spCmdBar); if (FAILED(hr))
// 获取菜单条的CommandBarControls spCmdCtrls = spCmdBar->GetControls(); ATLASSERT(spCmdCtrls);
// 在第5个\工具\菜单下新增一菜单条 CComVariant vItem(5);
spCmdCtrl= spCmdCtrls->GetItem(vItem); ATLASSERT(spCmdCtrl);
IDispatchPtr spDisp;
spDisp = spCmdCtrl->GetControl();
return hr;
CComPtr < Office::CommandBar> spCmdBar;
// 获取菜单条CommandBarPopup接口
CComQIPtr < Office::CommandBarPopup> ppCmdPopup(spDisp);
ATLASSERT(ppCmdPopup);
spCmdBarCtrls = ppCmdPopup->GetControls(); ATLASSERT(spCmdBarCtrls);
CComVariant vMenuType(1); // 控件类型 - menu CComVariant vMenuPos(6);
CComVariant vMenuEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); CComVariant vMenuShow(VARIANT_TRUE); // 菜单将显示 CComVariant vMenuTemp(VARIANT_TRUE); // 临时
CComPtr < Office::CommandBarControl> spNewMenu; // 用Add方法创建新的菜单条
spNewMenu = spCmdBarCtrls->Add(vMenuType,
vMenuEmpty, vMenuEmpty, vMenuEmpty, vMenuTemp);
ATLASSERT(spNewMenu);
spNewMenu->PutCaption(bstrNewMenuText); spNewMenu->PutEnabled(VARIANT_TRUE); spNewMenu->PutVisible(VARIANT_TRUE);
// 利用CommandBarButton来在菜单条前显示位图
CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu); ATLASSERT(spCmdMenuButton);
spCmdMenuButton->PutStyle(Office::msoButtonIconAndCaption);
// 同新增工具条第一个按钮位图相同方法 spCmdMenuButton->PasteFace();
// 显示菜单
spNewMenu->PutVisible(VARIANT_TRUE);
m_spMenu = spCmdMenuButton;
这样,通过在Outlook中通过上面提到的方法加载COM插件,就可以看到如图一所示的界面效果了,但是点击时没有响应,最后就让我们来解决这个问题。
工具条按钮CommandBarButton派发接口的响应事件是_CommandBarButtonEvents。ATL提供了二种模板类IDispEventImpl<>和IDispEventSimpleImpl<>来实现接口事件的接收,这里我们使用IDispEventSimpleImpl来实现(
)。它需要设置SINK(接收)映射,通过_ATL_SINK_INFO结构来回调参数信息,最终通过DispEventAdvise和DispEventUnadvise来与源接口连接或断开。实现方法如下:
1. 在COutlookAddin继承类中加入IDispEventSimpleImpl继承,代码如下: class ATL_NO_VTABLE COutlookAddin :
public CComObjectRootEx
public IDispEventSimpleImpl<1,COutlookAddin,&__uuidof(Office::_CommandBarButtonEvents)>
2. 声明_ATL_SINK_INFO结构回调参数信息。在OutlookAddin.h文件中加入下面语句: // 按钮事件响应信息声明
extern _ATL_FUNC_INFO OnClickButtonInfo; 在OutlookAddin.cpp文件中加入定义语句,如下: // 按钮事件响应信息定义
_ATL_FUNC_INFO OnClickButtonInfo ={CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}}; 3. 加入Sink映射,如下: EGIN_SINK_MAP(COutlookAddin)
SINK_ENTRY_INFO(1,
__uuidof(Office::_CommandBarButtonEvents),/*dispid*/
0x01,
OnClickButton1, &OnClickButtonInfo)
SINK_ENTRY_INFO(2,
__uuidof(Office::_CommandBarButtonEvents),/*dispid*/
0x01,
OnClickButton2, &OnClickButtonInfo)
SINK_ENTRY_INFO(3, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01, OnClickMenu, &OnClickButtonInfo) END_SINK_MAP()
4. 加入事件函数。在OutlookAddin.h中加入声明:
void __stdcall OnClickButton1(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
在OutlookAddin.cpp中加入实现: // 工具条按钮1点击事件响应函数
void __stdcall COutlookAddin::OnClickButton1(IDispatch* /*Office::_CommandBarButton* */ Ctrl, { }
5. 最后,打开或断开与接口的连接。方法如下
● 在OnConnection接口函数的最后部分,加入下面代码来打开连接: CommandButton1Events::DispEventAdvise((IDispatch*)m_spButton); ● 在OnDisconnection接口函数中,加入下面代码来断开连接: CommandButton1Events::DispEventUnadvise((IDispatch*)m_spButton);
到此就完成一个Office内部插件的最小需求了,大家可以编译后打开Outlook2000看看效果如何,详细代码可参看文章所带示例源码,内有详细注释。 参考文献:
Building an Office2K COM addin with VC++/ATL -- Amit Dey ATL开发指南(第二版) – Tom Armstrong & Ron Patton
USES_CONVERSION;
CComQIPtr
VARIANT_BOOL * CancelDefault)
HINSTANCE result=ShellExecute(NULL, _T(\
SW_SHOW);
作者Blog:http://blog.csdn.net/snaill/
编写插件锁住Word文档窗口 ——API和COM接口Hook综合演示
作者:Kruglinski Blog 下载源代码
关键字 API Hook,COM Hook,ATL,Office 文档安全,C++
现在的木马后门种类非常之多,其中有些木马专门以Office 文档为窃取目标,我最近做了一些Anti这些木马的工作,在工作中我基本上实现了阻止未知程序利用Office自动化接口窃取Word内容,但限于公司利益我只能写一些基本上已经比较成熟和公开的技术,当然其中了包含我的设计思想,我想多多少少会让你有所收获,你可任意转载文章,但请注明作者和出处,谢谢!
首先我们知道Ole拖放是由DoDragDrop启动的,为此你可以事先要准备一些参数给DoDragDrop使用,最开始我的想法是直接阻止DoDragDrop调用,后来我发现这样做会使Word文档窗口内部的拖放操作失效,这会给人一种很不好的感觉,好的保护软件应该在客户没有感觉到不方便的情况提供保护,而后我又仔细看了DoDragDrop的参数想发现有没有什么可以利用的地方,我发现它有四个参数: IDataObject * pDataObject; IDropSource * pDropSource; DWORD dwOKEffect; DWORD * pdwEffect;
后两个参数基本上没有什么利用价值,我想到Hook IDataObject的GetData函数,可是在Ole Drop客户端编程时,通常当我调用COleDataObject的相关成员函数时拖放已经差不多完成,鼠标已经在我的程序窗口上了。
而我现在的想法是在拖放操作刚离开Word的文档窗口,还没有到达外部程序窗口时就让它失效,这样Hook IDataObject的虚函数肯定不行,我只有再研究一下最后一个没有研究过的参数pDropSource,它是一个IDropSource类型指针,我发现它有一个虚函数QueryContinueDrag查看了一下MSDN发现它似乎就是我要找的。
我的理解是这个函数是让Ole拖放的服务端在调用DoDragDrop启动Ole拖放操作后,有机会取消拖放操作而设的一个CallBack,于是我写了一段代码Hook住DoDragDrop并从DoDragDrop中进一步Hook住IDropSource的QueryContinueDrag虚函数,我发现它会全程跟踪整个拖放操作,只要这个CallBack一返回DRAGDROP_S_CANCEL整个拖放操作就会被取消,这样我只要知道当前鼠标下的窗口是否是文档窗口就可以了,一旦离开了文档窗口我就让这个CallBack返回DRAGDROP_S_CANCEL取消整个拖放操作。
这样做就可以不影响文档窗口内部的拖放操作,又可以阻止将文档窗口内部的东西拖放到其它程序中,基本上不会让客户感到不方便,这很有意思!不是吗?
那么从鼠标位置得到窗口句柄可能吗?答案是肯定的! GetCursorPos函数可以返回一个POINT变量,它指示当前的鼠标位置,而WindowFromPoint则可以返回某一个POINT位置下的窗口句柄。写到这里我想差不多已经说完了我的思路,不知道你是否看得明白。
总结一下,用ATL向导生成一个COM框架,增加一个ATL简单对象,在这个对象上实现_IDTExtensibility2接口,在_IDTExtensibility2的OnConnection中Hook住DoDragDrop API,在Word
调用DoDragDrop时记录下当前的文档窗口句柄,并Hook住第二个参数pDropSource的QueryContinueDrag虚函数,在QueryContinueDrag里跟踪当前鼠下的窗口是否还是文档窗口(比较句柄是否等于DoDragDrop时记录下的文档窗口句柄),如果不是则返回DRAGDROP_S_CANCE取消拖放操作,并弹出一个警告信息的MessageBox,否则执行原有的操作(让拖放操作正常进行)。
最后,别忘了注册你的Word插件(用Regsvr32。exe),详细的键值和代码细节见源程序吧!
2005年10月15日夜
参考资料:
MSDN October 2001
Microsoft Office 2000/Visual Basic Programmer''s Guide ATL Internals
Inside C++ Object Model
在 C++ 程序中导出 Word 文档的简易方法
作者:胡金山 下载源代码
如果您要在应用程序中处理Word文档,可以参考MSDN. Lori Turner. Automating Microsoft Office 97 and Office 2000,该文内容详细全面,但是要在C++程序中导出Word文档,按照文中的方法来处理是很麻烦的,特别是需要填写的参数太多,所以我们考虑生成正确的VB脚本,然后执行生成Word文档的操作,这个方法的优点在于:一方面可以少填写参数;另一方面可以使用在Word中录制的宏脚本,而只需作少量的修改。我们给出了一些简单的函数来方便生成Word文档(主要是简单的表格)和直接运行内存中的VB脚本,此外,还附带了一个小小的例子。 //创建Word文档
std::string create_new(); //保存Word文档
std::string close_save(const char* filename); //selection 往下移,以继续生成下一元素 std::string move_down(); //插入分段符
std::string put_Paragraph(); //添加标题
std::string put_title(const char* title, const char* title_type=\标题 1\int align=ALIGN_LEFT); //添加“标题1”
std::string put_title1(const char* title, int align=ALIGN_CENTER); //添加“标题2”
std::string put_title2(const char* title, int align=ALIGN_LEFT); //添加“标题3”
std::string put_title3(const char* title, int align=ALIGN_LEFT); //添加红色警告信息
std::string add_warning_msg(const char* msg=\无数据\//添加表格的一行数据(不用此函数)
std::string add_grid_ln(const char* line); //添加表格
std::string put_grid(const char* content);
//运行脚本
extern \
下面是一个小例子,我们期望它在您的计算机上能够很好的运行,程序将生成一个Word文档,路径位于c:\\test.doc,计算机上需要安装Word XP。 int main(int argc, char* argv[]) {
ostringstream ostr; ostr< ostr< ostr< std::string str_buffer; read_file_as_grid_content(\ ostr< ostr< //输出到文件看看VB脚本的内容 /* std::ofstream ofile; ofile.open(\ ofile< //BeginWaitCursor(); RunScript( ostr.str().c_str() );//运行生成的脚本 //EndWaitCursor(); return 0; } 在VC中如何将数据导入 Word 中 作者:何鸿鹏 下载源代码 在给企业开发的项目中,客户经常要求将数据汇总显示并打印。站在程序员的立场上,简单数据的汇总打印可以采用VB自带报表,对复杂的数据用水晶报表或第三方打印控件。在企业中,汇总数据另一个目的是为了便于交流和共享资源,报表的格式通常固定,内容可以自己调整,他们对 Excel 和 Word 情有独钟。 刚完成的项目中,客户要求将汇总的数据以三种方式进行显示和打印:AutoCad、Excel 和 Word 文档中。前两种方式网上资源很多,对 Word 文档我找到例子不是很多。由于需要和 AutoCad 开发程序结合,不能将 Word 文档以 OLE 的方式 嵌入到程序中,文档只能在内存中形成,这将占用大量的CPU资源,另开一个线程进行处理,线程间以发送消息的方式进行通信。 首先在VC引入Word, 对格式相同的地方一定使用模版的方式进行处理,这可以大大的提高运行速度,Word中的模版为*.dot。加载模版的代码为: sDocs=sApp.GetDocuments(); sDocs.AttachDispatch(sApp.GetDocuments(),true); COleVariant vFalse((long)0),vTrue((long)1); vFalse,vFalse, sDoc.AttachDispatch(sDocs.Add(COleVariant(_variant_t(strFileName)), vTrue)); 需要注意的是,如果Word中使用到表格,VBA录制的全是Selection对象的处理方法,在VC中使用Selection对象的MoveUp和MoveDown函数时会出现不确定现象(如果你知道为什么的话请告诉我)。如果你需要绘制表格,不要用程序来动态形成,绘制表格的代码是很繁琐的,而且在页眉中绘制单元格是很难控制的。可以先在模版中绘出你要的表格格式,然后用程序复制单元格或者直接使用模版中的单元格。由于我做的项目表格的行数不确定,我采用的是复制单元格的方式(速度比较慢,谁有更好的方法?)。 剩下的处理流程,就是你想做什么操作,先在Word中录制一段宏,然后查看其VBA代码,并将其转化到Vc程序中。这就看你Word使用的熟练程度了,例如如何把不同页的页眉页脚设置不同,如何得到当前页的页数以及总的文档的页数,具体可以参考程序。 VC中也可以像Excel中调用模版中存在的宏,代码如下: VARIANT vtMissing ;vtMissing.vt =VT_ERROR; vtMissing.scode =DISP_E_PARAMNOTFOUND; sApp.Run(\ &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing, &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing, &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing, &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing); 我写本文的目的是想起到抛砖引玉的作用,看看大家在VC中使用Word有什么更好的方法。附属程序是我测试的一个工程,里面的代码没有经过加工,比较乱请大家见谅,但是基本的功能都基本上实现了。程序在Vc6.0,Office2000上测试通过。 获取本机通讯薄的内容 编译:徐景周 下载示例源代码 简介 如果你想获取本机通讯簿(Outlook Express和Outlook2000)的内容,如:联系人名字、联系人邮件地址等时,可以试试下面的方法。下面是把此方法用VC6编写的示例程序运行效果: 由于读取Outlook Express(系统自带)和Outlook2000(Office2000中所带)中通讯薄内容所采取的方法不同,下面将分开简述。 第一、读取系统自带Outlook Express中通讯薄方法 基本思路 通过载入Wab32.dll文件(此文件一般位于路径“<盘符>\\Program Files\\Common Files\\System\\”下面),再获取其内部涵数WABOpen的进程地址加以调用,来读出通讯薄中主要内容。 具体实现 一、 包含通讯薄头文件及声明内部函数 #include typedef HRESULT (WINAPI *fWABOpen)(LPADRBOOK*,LPWABOBJECT*,LPWAB_PARAM,DWORD); 二、 读取具体内容的详细代码 // 读取通讯薄内容(类型、呢称、名字、EMAIL) void CGetEmailDlg::OnOK() { HINSTANCE hinstLib; hinstLib = LoadLibrary(\fWABOpen procWABOpen; HRESULT hRes; LPADRBOOK lpAdrBook; LPWABOBJECT lpWABObject; LPWAB_PARAM lpWABParam = NULL; DWORD Reserved2 = NULL; ulFlags = NULL; if (ulObjType == MAPI_ABCONT) { IABContainer *lpContainer = static_cast hRes = lpContainer->GetContentsTable( ); _ASSERT(lpTable); ulFlags, &lpTable ULONG ulFlags = MAPI_BEST_ACCESS; ULONG ulObjType = NULL; LPUNKNOWN lpUnk = NULL; hRes = lpAdrBook->OpenEntry( ); lpcbEntryID, lpEntryID, NULL, ulFlags, &ulObjType, &lpUnk ULONG lpcbEntryID; ENTRYID *lpEntryID; hRes = lpAdrBook->GetPAB( ); _ASSERTE(hRes == S_OK); if (hRes != S_OK) exit(2); &lpcbEntryID, &lpEntryID if (procWABOpen != NULL) { hRes = (procWABOpen)(&lpAdrBook,&lpWABObject,NULL,Reserved2); _ASSERTE(hRes == S_OK); if (hRes != S_OK) exit(1); if (hinstLib != NULL) { // 获取\内部涵数WABOpen的进程地址 procWABOpen = (fWABOpen) GetProcAddress(hinstLib, \ } ULONG ulRows; hRes = lpTable->GetRowCount(0,&ulRows); _ASSERTE(hRes == S_OK); SRowSet *lpRows; hRes = lpTable->QueryRows( ); m_ListEmail.ResetContent(); for(ULONG i=0;i SRow *lpRow = &lpRows->aRow[i]; CString strTemp; for(ULONG j=0;j m_ListEmail.AddString(strTemp); SPropValue *lpProp = &lpRow->lpProps[j]; if (lpProp->ulPropTag == PR_DISPLAY_NAME_A) strTemp = strTemp + \名字: \strTemp = strTemp + \if (lpProp->ulPropTag == PR_EMAIL_ADDRESS_A) ulRows, 0, &lpRows // 获取所有行 if (lpProp->ulPropTag == PR_NICKNAME_A) strTemp = strTemp + \呢称: \strTemp = strTemp + \类型: \ if (lpProp->ulPropTag == PR_ADDRTYPE_A) } } } } lpWABObject->FreeBuffer(lpRow); lpWABObject->FreeBuffer(lpRows); FreeLibrary(hinstLib); // 读取成功后,置读取按钮无效 CButton* pBtn = (CButton*)GetDlgItem(IDOK); pBtn->EnableWindow(FALSE); 附注:在包含进头文件Wab.h进行编释时,有时会在WABTAGS.H等地方编释不通,可按示例源码中所带WABTAGS.H文件加以修改,主要是原安装文件的内容有部分损坏。 第二、读取Office2000中所带Outlook2K中通讯薄方法 基本思路 由于Outlook2000下支持内部COM接口,可以利用此接口来读取其内部通讯薄中主要内容。 具体实现 一、 导入Outlook2000的库文件 // 导入读取Outlook2000中通讯薄内容所需库 #import \#import \ no_namespace exclude(\二、 读取具体内容的详细代码 _ApplicationPtr pApp; _ItemsPtr pItems; MAPIFolderPtr pFolder; _ContactItemPtr pContact; HRESULT hr; try { // 获取默认Outlook中联系人文件夹 pFolder=pApp->GetNamespace(_bstr_t(\if (pFolder==NULL) { } else // 否则自行选择Outlook中一指定文件夹 { pFolder=pApp->GetNamespace(_bstr_t(\if (pFolder==NULL) return; MessageBox(\没有发现默认的Outlook联系人文件夹\错误!\return; hr=pApp.CreateInstance(__uuidof(Application)); if (FAILED(hr)) { } MessageBox(\实例创建失败\错误\return; } } if (pFolder->GetDefaultItemType()!=olContactItem) // 不是联系人 { } MessageBox(\选择不是联系人文件夹\错误\return; pItems=pFolder->GetItems(); if (pItems==NULL) { } pContact=pItems->GetFirst(); MessageBox(\不能得到联系人条目\错误\return; m_ListEmail.ResetContent(); while(1) { } if (pContact==NULL) break; CString strTemp; strTemp=(char *)pContact->GetFullName(); strTemp=strTemp + \ strTemp=strTemp + (char *)pContact->GetEmail1Address(); strTemp=strTemp + \ m_ListEmail.AddString(strTemp); pContact=pItems->GetNext(); catch(_com_error &e) { } 参考文献: Importing Contacts from Outlook -- Deepesh Dhapola Accessing the Windows Address Book – Code4Food 联系方式: 地址:陕西省西安市劳动路2号院六单元 邮编:710082 EMAIL:jingzhou_xu@163.net 未来工作室(Future Studio) MessageBox((char *)e.Description());
正在阅读:
VC6编写Office插件05-20
百家讲坛 《易经的奥秘》学习心得106-02
中国台地式电风扇行业市场调查研究报告(目录) - 图文01-17
课题一 低压电器的分类及常用术语09-01
针灸学治疗各论笔记整理06-27
(最新版)电脑艺术设计论室内设计中的人情味4毕业论文设计06-23
民俗博物馆项目可行性研究报告04-20
pH值对味精活性炭脱色效果的影响08-27
网络综合实验报告201001-16
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 插件
- 编写
- Office
- VC6