VC_数据库_Word的VBA编程实现技巧.pdf
http:/-1-VC_数据库数据库_Word 的的 VBA 编程实现技巧编程实现技巧 蔡世伟,杨正球 北京邮电大学计算机科学技术系,北京(100876)E-mail: 摘摘 要:要:本文主要介绍了用 VC6.0 作为程序开发语言,用 Kingbase 作数据库,以 word2003中的书签作为媒介,从数据库中提取所需信息,自动替换到 Word 中的对应位置上,并支持在同一 Word 文件里面批量进行。关键词:关键词:Word,bookmark,VBA,COM,OLE 1.引言引言 在实际工作中,常常需要将数据库中的数据读取到 Office,实现诸如:Word 文件打印、传送数据到 Word 文档、发送 E-MAIL、自动产生表格、Excel 数据统计、圆饼图,直方图显示、自动报表生成、播放幻灯、doc、txt、HTML、rtf 文件转换、中文简繁体转换、拼音或笔画排序通常的实现方法是在 Word 中手工输入,或用数据库软件编写一个专门的模块来完成此功能。本文采用 VC6.0 作为开发语言,用宏命令的方法实现了从 Kingbase 数据库中获取数据,在 Word 中自动替换,并支持批量进行。下面详细介绍实现的基本过程。2.VBA编程概念编程概念 VBA(Visual Basic for Application)是微软公司将 VB 的一部分代码结合到 OFFICE 中,并可以嵌入 OFFICE 使用的一种编程语言,它的很多语法继承了“VB”,可以像编写 VB 语言那样来编写 VBA 程序,以实现某个功能。Microsoft 的 Office 产品中,都提供了 OLE Automation 自动化程序的接口,可以直接支持使用 VB,VBA 和 Script 脚本来调用 Office的功能。比如在 WORD 中,调出菜单“工具(T)宏(M)录制新宏(R)”,这时候它开始记录你在 WORD 中任何菜单和键盘的操作,把你的操作过程保存起来,以便再次重复调用。而保存这些操作的记录,其实就是使用了 VBA 程序。但是,在实际工作中,又常常需要在 VC中调用 Office 的功能。3.Word逻辑层次结构逻辑层次结构 为了更有逻辑,更有层次地操作 Office,Microsoft 把应用(Application)按逻辑功能划分为如下的树形结构:1 http:/-2-图 1word 逻辑层次结构 由此可知,Word 对象是按层次顺序排列的,层次结构顶端的两个主类是 Application 和Document 类。Word 对象模型严格遵循用户界面。Application 对象表示整个应用程序,每个Document 对象表示单个 Word 文档,Paragraph 对象对应于单个段,以此类推。这些对象各自都有很多方法和属性,您可以使用这些方法和属性操作对象或与对象交互。Word 对象模型还存在许多重叠。例如,Document 和 Selection 对象都是 Application 对象的成员,但是 Document 对象还是 Selection 对象的成员。Document 和 Selection 对象都包含 Bookmark 和 Range 对象。存在重叠使得可以通过多种方式来访问相同类型的对象。4.Word各部分对象的描述及彼此间的交互各部分对象的描述及彼此间的交互 4.1 Application 对象简介对象简介 Application 对象表示 Word 应用程序,是其他所有对象的父级。它的所有成员通常作为一个整体应用于 Word。可以使用该对象的属性和方法来控制 Word 环境。4.2 Document 对象简介对象简介 通过 VBA 使用 Word 时,Document 对象处于中心位置,几乎所有的操作都要调用Document 对象本身或其内容。Document 对象表示一个打开的文档,从根本上来看,文档不过是一个巨大的字符流。人们倾向于认为文档是一个由字符、单词、句子和段落组成的集合,字符组成单词,单词组成句子,句子组成段落,等等。但实际上,文档就是一些字符。每个字符都有一定的作用。某些字符是字母、空格或制表符,另一些字符是段落标记或分页符。因此,每一个 Document 对象都具有 Characters 对象、Words 对象、Sentences 对象和 Paragraghs对象四个集合。此外,每个文档具有一个包含一个或多个节的 Sections 集合,每一个节都有一个包含该节页眉和页脚的 HeadersFooters 集合。当你打开已有的文档或创建一个新文档时,都要创建新的 Document 对象,每个打开或新创建的文档均被添加至 Documents 集合,因此,所有的 Document 对象都是 Application 对象的 Documents 集合的成员。Document 对象作为 Documents 集合中的一个成员,您可以通过使用 Document 对象的索引值(Document 对象在 Documents 集合中的位置,1 是集合中的第一个文档)或名称来引用它。一般情况下不要使用 Documents 集合中的索引值来引用文档,http:/-3-因为当其它文档打开或关闭时,某个特定文档的索引值可能会随之改变。通常,您可以通过使用ActiveDocument属性或使用Documents集合的Add方法或Open方法来创建的Document对象变量。另外,具有焦点的文档称为活动文档,可以使用 ActiveDocument 属性来引用它。通过使用Open方法打开的文档,或者通过使用Add方法创建的文档,都将成为用ActiveDocument属性表示的当前活动文档。如果您想使 Documents 集合里的其它文档成为活动文档,可使用Document 对象的 Active 方法。4.3 Selection 对象简介对象简介 Selection 对象表示当前选择的区域。在 Word 用户界面中执行某项操作(例如,对文本进行加粗)时,应首先选择或突出显示文本,然后应用格式设置。Selection 对象始终存在于文档中。如果未选中任何对象,它表示插入点。此外,它也可以是不连续的多个文本块。4.4 Bookmark 对象简介对象简介 文档中的Microsoft.Office.Interop.Word.Bookmark是控制文档中的文本的最容易的方法,在这一点上它类似于 Windows 窗体上的文本框控件。Microsoft.Office.Interop.Word.Bookmark 对象表示文档中同时具有起始位置和结束位置的连续区域。书签用于在文档中标记一个位置,或者用作文档中的文本容器。Microsoft.Office.Interop.Word.Bookmark 对象可以小到只有一个插入点,也可以大到整篇文档。Microsoft.Office.Interop.Word.Bookmark 与 Range 对象的不同之处在于它具有以下特点:可以在设计时命名书签。Microsoft.Office.Interop.Word.Bookmark 对象随文档一起保存,因此当代码停止运行或文档关闭时,它不会被删除。书签可以隐藏或变得可见,方法是将 View 对象的 ShowBookmarks 属性设置为 True 或False。4.5 Range 对象简介对象简介 Range 对象表示文档中的一个连续范围,由一个起始字符位置和一个终止字符位置定义。这个连续范围可以小到一个插入点,大到整个文档。它也可能是(而非必须是)由当前节表示的范围。您也可以定义一个 Range 对象,表示和当前节不同的范围。也可以在同一个文档中定义多个 Range 对象。Range 对象中的字符包含非打印字符,例如,空格、回车符和段落标记。创建Range对象的典型方法为:声明一个Range类型的对象变量,然后用Document对象的 Range 方法或另一个对象(例如 Character、Word、Sentence 或 Selection 对象)的Range 属性来实例化该变量。当您使用 Range 方法来指定文档的特定范围时,您必须使用此方法的 Start 参数指定这个范围开始的位置,使用 End 参数指定结束的位置。文档的第一个字符的字符位置为 0。最后一个字符的位置和文档的字符总数相等。您可以通过使用 Characters 集合的 Count 属性确定文档中的字符数。您也可以使用 Bookmark、Selection 或 Range 对象的 Start 和 End 属性来指定 Range 方法的 Start 和 End 参数。您可以将 Start 和 End 参数设置为同一个数字,这将创建一个不包含任何字符的范围。您可以使用对象的 SetRange 方法设置或重新定义 Range 对象的内容,还可以通过使用http:/-4-Range 对象的 Start 属性或 MoveStart 方法指定或重新定义范围开始的位置。同样地,也可以通过使用 Range 对象的 End 属性或它的 MoveEnd 方法指定或重新定义范围结束的位置,可以通过使用对象的 Find 属性返回 Find 对象,重新定义 Range 对象。许多 Word 对象具有可返回 Range 对象的 Range 属性。在您需要使用 Range 对象的属性和方法进行操作,而对象本身又不提供这些属性和方法的情况下,您可以使用对象的 Range 属性返回 Range 对象。定义 Range 对象后,就可以应用此对象的方法和属性修改所指定范围的内容或获取有关信息了。处理 Range 对象中的文本使用 Range 对象的 Text 属性可以指定或确定该范围包含的文本,也可以通过 GetText 和 SetText 方法,来获得、设置文本。使用 Range 对象的 StoryType 属性可以确定范围在文档中的位置。文档构成部分是指文档中包含文本的特定范围。在一个文档中最多可以有 11 种文档构成部分,表示正文、页眉、页脚、批注等不同范围。您可以使用 StoryRanges 属性返回 StoryRanges 集合。StoryRanges 集合包含 Range 对象,表示文档中的每一个文档构成部分。使用 Range 对象的 InsertBefore 或 InsertAfter 方法,可将文本添加至现有 Range 对象。事实上,有一整类方法,名称以“Insert”开头,可以用于操作 Range 对象。2 了解清楚了以上逻辑层次,才能正确的操纵 Office。举例来讲,如果给出一个 VBScript语句是:application.ActiveDocument.SaveAs c:abc.doc;那么,我们就知道了,这个操作的过程是:第一步,取得 Application;第二步,从 Application 中取得 ActiveDocument;第三步,调用 Document 的函数 SaveAs,参数是一个字符串型的文件名。5.宏录制与转换宏录制与转换 5.1 VARIANT 结构和结构和 COleVariant 类简介类简介 在 OLE、ActiveX 和 COM 中,VARIANT 数据类型提供了一种非常有效的机制,由于它既包含了数据本身,也包含了数据的类型,因而它可以实现各种不同的自动化数据的传输。查看在 OAIDL.H 文件中定义的 VARIANT 结构如下:struct tagVARIANT VARTYPE vt;union short iVal;/VT_I2.long lVal;/VT_I4.float fltVal;/VT_R4.double dblVal;/VT_R8.DATE date;/VT_DATE.BSTR bstrVal;/VT_BSTR.short*piVal;/VT_BYREF|VT_I2.long*plVal;/VT_BYREF|VT_I4.float*pfltVal;/VT_BYREF|VT_R4.double*pdblVal;/VT_BYREF|VT_R8.DATE*pdate;/VT_BYREF|VT_DATE.BSTR*pbstrVal;/VT_BYREF|VT_BSTR.;http:/-5-显然,VARIANT 类型是一个 C 结构,它包含了一个类型成员 vt、一些保留字节以及一个大的 union 类型。例如,如果 vt 为 VT_I2,那么我们可以从 iVal 中读出 VARIANT 的值。同样,当给一个 VARIANT 变量赋值时,也要先指明其类型。为了方便处理 VARIANT 类型的变量,Windows 还提供了这样一些非常有用的函数:VariantInit 将变量初始化为 VT_EMPTY;VariantClear 消除并初始化 VARIANT;VariantChangeType 改变 VARIANT 的类型;VariantCopy 释放与目标 VARIANT 相连的内存并复制源 VARIANT。COleVariant 类是对 VARIANT 结构的封装。它的构造函数具有极为强大大的功能,当对象构造时首先调用 VariantInit 进行初始化,然后根据参数中的标准类型调用相应的构造函数,并使用 VariantCopy 进行转换赋值操作,当 VARIANT 对象不在有效范围时,它的析构函数就会被自动调用,由于析构函数调用了 VariantClear,因而相应的内存就会被自动清除。除此之外,COleVariant 类重载了“=”操作符,所以常用类型可以直接转换;COleVariant 的两个操作在与 VARIANT 类型转换中为我们提供极大的方便,operator LPCVARIANT Converts a COleVariant value into an LPCVARIANT.operator LPVARIANT Converts a COleVariant object into an LPVARIANT.可以得到 VARIANT 结构。其区别是,前者是个拷贝操作,将 VARIANT 结构的内容拷贝到目标中去,后者仅仅返回 VARIANT 结构的指针。5.2 宏录制方法宏录制方法 在书写调用 Office 函数的过程中,最困难的是确定函数的参数,一般情况下,参数都是 VARIANT 类型的变量指针。那么到底具体我们应该怎么写呢?推荐两个方法,其一是阅读有关 VBA 的书籍;其二是使用 Office 中自带的“宏”功能。本文使用第二个方法,把要完成的功能,在 Office操作环境中,用宏录制下来,然后观察分析录制后的函数和参数,就可以在 VC 中使用了。启动 Word,连续点击“工具”,“宏”,“录制新宏.”,在弹出的对话框中填写宏名,选择宏要保存的位置(可以保存在模板中,也可以保存在当前文档中),单击“确定”按钮,屏幕上将出现一个“录制宏”工具栏。接下来,你可以在 word 里面进行诸如插入换页分隔符,选中一片区域进行编辑,保存,关闭等操作,最后,单击停止按钮停止录制。再依次选择“工具”、“宏”、“宏”,在对话框中选择刚才的宏名,单击“编辑”按钮,打开 vba 编程窗口,就看到与刚才那些操作所对应的函数和参数了。3 举个例子,以下是一个在 Word 中录制的另存文件的宏:Sub Macro1()Macro1 Macro 宏在 2007-12-9 由 csw 录制 ChangeFileOpenDirectory C:Documents and SettingsAdministrator桌面 ActiveDocument.SaveAs FileName:=CSW.doc,FileFormat:=wdFormatDocument,_ LockComments:=False,Password:=,AddToRecentFiles:=True,WritePassword _ :=,ReadOnlyRecommended:=False,EmbedTrueTypeFonts:=False,_ http:/-6-SaveNativePictureFormat:=False,SaveFormsData:=False,SaveAsAOCELetter:=_ False End Sub 5.3 转换成可以在转换成可以在 VC 中使用的函数中使用的函数 仍以上面的宏为例,在 VC 中对应的函数原型为 void _Document:SaveAs(VARIANT*FileName,VARIANT*FileFormat,VARIANT*LockComments,VARIANT*Password,VARIANT*AddToRecentFiles,VARIANT*WritePassword,VARIANT*ReadOnlyRecommended,VARIANT*EmbedTrueTypeFonts,VARIANT*SaveNativePictureFormat,VARIANT*SaveFormsData,VARIANT*SaveAsAOCELetter)分析对照后,我们就能看出,参数 FileName 是字符串 VARIANT(VT_BSTR),参数 LockComments 是布尔 VARIANT(VT_BOOL),等等。参数 FileFormat:=wdFormatDocument 是什么类型那?其实这是一个表示保存的时候指定文件类型的常量,而且显然是 DWORD 类型 VARIANT(VT_I4),由匈牙利命名可以知道 wd 其实是 DWORD 的意思。这些常量的具体数值是多少,可以通过把光标定位到常量所在位置,从浮出的 tip 中获得,也可以另写一个宏,调用函数 MsgBox 显示输出它的值!6.程序设计实现程序设计实现 6.1 基本步骤基本步骤 准备.dot 模板文件:先在 word 中建立一个模板,将固定的文字输入到该模板中,将变动的文字部分用带下划线的空格表示,并设置成标签,为了编程方便,把标签依次命名为:INDEX0,INDEX1,INDEX2,创建(或打开已有的)一个 MFC 的程序工程,Ctrl+W 执行 ClassWizard,Add Class.From a type Library在 Office 目录中,找到你想使用的类型库。(我使用的是 Office2003,其Word 的类型库文件,保存在 C:Program FilesMicrosoft OfficeOfficeMSWORD.OLB)根据你 Office 的版本,查询相关资料确定需要使用的类型库文件,比如:Word 97 使用Msword8.olb,Word 2000 使用 Msword9.olb,Word 2002 使用 MSWord.olb。6.2 初始化初始化 COM 库库 方法一,找到 App 的 InitInstance()函数,在其中添加 AfxOleInit()函数的调用 方 法 二,在 需 要 调 用COM功 能 的 地 方:CoInitialize(NULL),调 用 完 毕后:CoUninitialize()。从本质上讲,使用 COM 组件的过程,就是“New”一个 COM,Query一个接口,调用此接口的一个函数。CoInitialize 并不装载 com 库,这个函数只是用来初始化当前线程使用什么样的套间,套间是 com 中用来解决并发调用冲突的很有效的办法。当使用这个函数以后,线程就和一个套间建立了对应关系。线程的套间模式决定了该线程如何调用 com 对象,使用不同套间之间对象接口是通过列集来完成的。现代 COM 的基本原则之一,就是每个使用 COM 的线程都应该先调用 CoInitialize 或 CoInitializeEx 来初始化 COM。4 6.3 在需要调用在需要调用 Office 功能函数的功能函数的 cpp 文件中添加文件中添加#include /为了方便操作 VARIANT 类型变量,使用 CComVariant 模板类 http:/-7-/在定义的时候直接调用带参数的构造函数,比 VARIANT 使用简单多了吧/使用 CComVariant 的不带参数的构造函数,默认就是使用 VT_EMPTY,设置为空类型/另外,除了 CComVariant,你还可以使用 COleVariant 和_variant_t,但我个人最喜欢前者#include 头文件.h /具体的头文件名,是由装载类型库的文件名决定的。(鼠标双点包装/类的文件,就可以看到);比如使用 msword.olb 类型库,那么头文件是 msword.h 6.4 在要操作在要操作 Word 的地方,按如下步骤写代码的地方,按如下步骤写代码 _Application*app=new _Application;/定义一个 WORD 的应用对象 if(!app-CreateDispatch(_T(Word.Application)/启动 WORD AfxMessageBox(创建 MS-WORD 失败);flag=false;return flag;app-SetVisible(TRUE);/设置 WORD 可见。/当然,如果你想要悄悄地调用 WORD 的功能,则注释掉这条语句 Documents docs=app-GetDocuments();/根据逻辑结构层次,需先获取 Documents COleVariant covOptional1(long)DISP_E_PARAMNOTFOUND,VT_ERROR);COleVariant Dot(strDotPath,VT_BSTR);/strDotPath 是 CString,代表文档模板路径 CComVariant Visible(false);doc2=docs.Add(&Dot,&covOptional1,&covOptional1,&Visible);/隐示打开该.dot 模板文件 doc2.Activate();/激活该文档对象 sel2=app-GetSelection();/根据逻辑结构层次,应先获取当前文档对象的 Selection 对象,表/示输入点,即光标闪烁的那个地方 sel2.WholeStory();/选中当前文档对象的整个区域 sel2.Copy();/将模板内容拷贝入剪贴板中,以实现在一个 Word 文件中保存 N 个用户信息 doc1=docs.Add(&Dot,&covOptional,&covOptional,&covOptional);marks=doc1.GetBookmarks();/根据逻辑结构层次,此时应先获取当前文档对象的 Marks 对象 marksCount=marks.GetCount();/获取文档中的标签数目 /此处是实现从 Kingbase 数据库中,查询获得所需信息,并保存到 CStringArray 对象中的代/码;我是使用自实现的 CGeneralSet 类,通过 ODBC(Open DataBase Connector 开放数据库/互连接口)数据源,可以对各种类型的数据库进行统一访问。限于篇幅,不再赘述。读者可/以根据自己的开发环境、习惯、爱好,自己实现这部分代码。for(int i=0;iGetSelection();sel1.EndKey(&uit,&etd);/定位到当前文档的末尾,以便换页和添加原模板的内容 sel1.InsertBreak(&FileFormat);/换页。以便每个用户信息都能从新的一页开始 sel1.Paste();/从剪贴板中拷回原模板内容,实现一个 Word 中存放 N 个用户的信息 range.ReleaseDispatch();/Range 不用了,一定要释放 mark.ReleaseDispatch();/Mark 不用了,一定要释放 marks.ReleaseDispatch();/Marks 不用了,一定要释放 sel1.ReleaseDispatch();/Selection 不用了,一定要释放 sel2.ReleaseDispatch();CComVariant SaveChanges(false),OriginalFormat,RouteDocument;doc2.Close(&SaveChanges,&OriginalFormat,&RouteDocument);doc1.ReleaseDispatch();/Document 不用了,一定要释放 doc2.ReleaseDispatch();docs.ReleaseDispatch();/Documents 不用了,一定要释放 app-ReleaseDispatch();/释放对象指针。切记,必须调用 delete app;7.总结总结 VBA(Visual Basic for Applications,Visual Basic 应用程序设计语言)是 Visual Basic 和许多可编程软件应用程序(特别是 Microsoft Office 套件)所采用的语言,特别适合需要对数据进行批量处理的情况。4通过以上实例,说明了如何利用宏记录,在 VC 编程环境里调用 Office 所能完成的功能。读者通过借鉴文中介绍的方法,根据自己的实际需要定制,必极大地提高处理办公文档的效率。http:/-9-参考文献参考文献 1 Visual C+中操纵 MS Word123杨老师VC 知识库2006(7)2 Office 中的 WordDavid ShankMSDN2000(7)3 Word 中利用 VBA 编程制作作文稿纸石斌天极软件2006(12)4 来自 COM 经验的八个教训Jeff Prosise微软中国社区2004(5)5 VB 与 VBA 技术手册 Paul Lomax清华大学2002(5)The VBA of Word and Database programming skills with VC language Cai Shiwei,Yang Zhengqiu Institute of Computer Science and Technology,Beijing University of Posts and Telecommunications,Beijing(100876)Abstract This article mainly explains how to use VC6.0 as program development language,a Kingbase database and bookmarks of word2003 as a medium,automatically replace the corresponding bookmark_position with extracted_information from the database in the Word,and support a batch in the same Word document.Keywords:Word,bookmark,VBA,COM,OLE 作者简介:作者简介:蔡世伟,男,1980 年 9 月出生,硕士研究生,主要研究方向为多媒体与网络信息管理;杨正球,男,1967 年 7 月生,副教授,主要研究方向为网络管理、通信软件等。