我们操纵Word需要通过类型库中的MFC类。而这些类,应该都是基于一个叫COleDispatchDriver的类。至少我所了解到的都是这样。
COleDispatchDriver没有基类。COleDispatchDriver类实现OLE自动化中的客户方。OLE调度接口为访问一个对 象的方法和属性提供了途径。COleDispatchDriver的成员函数连接,分离,创建和释放一个IDispatch类型的调度连接。其它的成员函 数使用变量参数列表来简化调用IDispatch::Invoke。
学习如何自动化控制 Word、Excel 和 Powerpoint 的对象模型的最佳方法是使用这些 Office 应用程序中的宏录制器:
- 从 工具 菜单上的 宏 选项中选择 录制新宏 ,然后执行您感兴趣的任务。
- 从 工具 菜单上的 宏 选项中选择 停止录制 。
- 完成录制后,从 工具 菜单上的 宏 选项中选择 宏 ,选择您录制的宏,然后单击 编辑 。
您将看到生成的 VBA 代码,该代码可完成您所录制的任务。记住,录制的宏在大多数情况下并 不 是最佳代码,但它可以提供快捷可用的示例。
Application :代表 Microsoft Word 应用程序。Application 对象包含可返回最高级对象的属性和方法。例如,ActiveDocument 属性可返回当前活动的Document 对象。
Documents :由 Word 当前打开的所有 Document(文档) 对象所组成的集合。
Document :代表一篇文档。Document 对象是 Documents 集合中的一个元素。Documents 集合包含 Word 当前打开的所有 Document 对象。
Selection: 该对象代表窗口或窗格中的当前所选内容。所选内容代表文档中被选定(或突出显示的)的区域,若文档中没有所选内容,则代表插入点。每个文档窗格只能有一个活动的 Selection 对象,并且整个应用程序中只能有一个活动的 Selection 对象。
例子1 :
#include " msword9.h " // 为了使代码集中,方便阅读,所以把头文件放到了这里 void CStep1Dlg::OnOK() { _Application app; // 定义一个WORD的应用对象 if (!app.CreateDispatch(_T( " Word.Application " ))) // 启动WORD { AfxMessageBox(_T( " 居然你连OFFICE都没有安装吗? " )); return ; } AfxMessageBox(_T( " WORD 已经运行启动啦,你可以用Ctrl+Alt+Del查看 " )); app.SetVisible(TRUE); // 设置WORD可见。 // 当然,如果你想要悄悄地调用WORD的功能,则注释掉这条语句 AfxMessageBox(_T( " 现在你已经看到WORD的程序界面了吧 " )); AfxMessageBox(_T( " WORD准备要退出啦 " )); VARIANT SaveChanges,OriginalFormat,RouteDocument; // 定义调用QUIT时使用的参数 SaveChanges.vt=VT_BOOL; // 设置退出WORD时候的保存参数 SaveChanges.boolVal=VARIANT_FALSE; // 为不保存任何文档,模板及设置 ::VariantInit( &OriginalFormat); // 清空变量 RouteDocument.vt=VT_EMPTY; // 清空变量的另一种方法 // 调用Quit退出WORD应用程序。当然不调用也可以,那样的话WORD还在运行着那 app.Quit(&SaveChanges,&OriginalFormat,& RouteDocument); app.ReleaseDispatch(); // 释放对象指针。切记,必须调用 AfxMessageBox(_T( " Step1执行完成。接着请学习Setp2 " )); }
例子2:
#include " msword9.h " #include <AtlBase.h> // 新增加了一个头文件,为使用CComVariant替代VARIANT做准备 void CStep2Dlg::OnOK() { // 以下3行代码,同Step1。就不解释啦 _Application app; // 为了简单,没有判断返回值。如果没有成功,记得检查你有没有AfxOleInit()呀? app.CreateDispatch(_T( " Word.Application " )); app.SetVisible(TRUE); AfxMessageBox(_T( " WORD已经启动,现在要退出啦 " )); AfxMessageBox(_T( " 怎么和Step1没有什么区别呀? " )); AfxMessageBox(_T( " 嘿嘿,是没什么区别,但是使用方式简单了很多呀。看看源程序吧 " )); // 准备调用_Application::Quit函数了,需要定义3个参数。 // 但是,这次我们使用CComVariant,这是一个模板类。 // 在定义的时候直接调用带参数的构造函数,比VARIANT使用简单多了吧 CComVariant SaveChanges( false ),OriginalFormat,RouteDocument; // 使用 CComVariant 的不带参数的构造函数,默认就是使用VT_EMPTY,设置为空类型 // 另外,除了CComVariant,你还可以使用COleVariant和_variant_t,但我个人最喜欢前者 app.Quit(&SaveChanges,&OriginalFormat,& RouteDocument); app.ReleaseDispatch(); AfxMessageBox(_T( " 下面该学习Setp3了 " )); }
例子3:
#include " msword9.h " #include <AtlBase.h> void CStep3Dlg::OnOK() { //////////// // 这次,我们要控制在WORD中输入一些字符了 ///////////////////// /* ************ WORD 录制的宏,新建一个空文档,然后输入一些文字 ************ Documents.Add Template:= _ "C:/Documents and Settings/Administrator/Application Data/Microsoft/Templates/Normal.dot" _ , NewTemplate:=False, DocumentType:=0 Selection.TypeText Text:="HELLO" Selection.TypeParagraph Selection.TypeText Text:="大家好" ************************************************************************** */ _Application app; app.CreateDispatch(_T( " Word.Application " )); app.SetVisible(TRUE); AfxMessageBox(_T( " 看好了,就要新建一个空白文档了 " )); // 通过WORD宏可以知道,由于要使用Documents,于是我们定义一个并从app中取得 Documents docs= app.GetDocuments(); // 准备调用Documents::Add函数了,需要定义4个参数。 // 从WORD宏可以看出来3个参数的类型为: // Template字符,NewTemplate布尔,DocumentType数值 // 但Add函数还需要一个参数是Visible,傻子也能看出来这个值表示是否显示出新文档 // 并且可以给默认值(VT_EMPTY) CComVariant Template(_T( "" )); // 为了简单,没有使用WORD的文档模板 CComVariant NewTemplate( false ),DocumentType( 0 ),Visible; docs.Add( &Template,&NewTemplate,&DocumentType,& Visible); AfxMessageBox(_T( " 下面,程序要向WORD发送字符啦 " )); // 通过WORD宏可以知道,由于要使用Selection,于是我们定义一个并从app中取得 // Selection表示输入点,即光标闪烁的那个地方 Selection sel= app.GetSelection(); // 调用函数Selection::TypeText 向WORD发送字符 sel.TypeText(_T( " HELLO/r/n大家好呀 " )); AfxMessageBox(_T( " 看见了吗?我要退出啦 " )); sel.ReleaseDispatch(); // Selection 不用了,一定要释放 docs.ReleaseDispatch(); // Documents 也不用了 CComVariant SaveChanges( false ),OriginalFormat,RouteDocument; app.Quit( &SaveChanges,&OriginalFormat,& RouteDocument); app.ReleaseDispatch(); AfxMessageBox(_T( " 下面该学习Setp4了 " )); }
例子4:
#include " msword9.h " #include <AtlBase.h> void CStep4Dlg::OnOK() { _Application app; app.CreateDispatch(_T( " Word.Application " )); app.SetVisible(TRUE); Documents docs = app.GetDocuments(); CComVariant Template(_T( "" )); CComVariant NewTemplate( false ),DocumentType( 0 ),Visible; docs.Add( &Template,&NewTemplate,&DocumentType,& Visible); Selection sel = app.GetSelection(); COleVariant varstrRange( "" ); COleVariant varConfirmConversions( short ( 0 ),VT_BOOL); COleVariant varLink( short ( 0 ),VT_BOOL); COleVariant varAttachment( short ( 0 ),VT_BOOL); sel.InsertFile( " C://My Project//WordOperator//doc//fjjb.doc " ,varstrRange,varConfirmConversions,varLink,varAttachment); sel.MoveUp(COleVariant(( short ) 5 ),COleVariant(( short ) 2 ),COleVariant(( short ) 0 )); sel.TypeText( " 123456789 " ); sel.MoveRight(COleVariant(( short ) 12 ),COleVariant(( short ) 1 ),COleVariant(( short ) 0 )); sel.TypeText(_T( " HELLO " )); sel.MoveRight(COleVariant(( short ) 1 ),COleVariant(( short ) 1 ),COleVariant(( short ) 0 )); sel.TypeText( " 123456789 " ); AfxMessageBox(_T( " 好了,我要保存到c://hello.doc中了 " )); /* *************** 这是在WORD中录制的新建文档直到另存的宏 ************* Documents.Add Template:= _ "C:/Documents and Settings/Administrator/Application Data/Microsoft/Templates/Normal.dot" _ , NewTemplate:=False, DocumentType:=0 Selection.TypeText Text:="Hello" Selection.TypeParagraph Selection.TypeText Text:="大家好" ChangeFileOpenDirectory "C:/" ActiveDocument.SaveAs FileName:="Hello.doc", FileFormat:=wdFormatDocument _ , LockComments:=False, Password:="", AddToRecentFiles:=True, _ WritePassword:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, _ SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:= _ False ******************************************************************** */ /* *************** 程序思路 ****************************************** 另存为的函数是ActiveDocument.SaveAs,显然表示的是对当前活跃的文档进行保存, 在我们的类中没有ActiveDocument,其实它对应的是_Document,而这个可以由 _Application 的GetActiveDocument()得到。你一定会提问:“你怎么知道的?” 呵呵,怎么说那,我怎么知道的那?答案是:猜。其实如果使用的多了,分析、猜 查找都是办法。如果想得到确切的方法,其实可以在VBA的书或微软的网站中搜索 ******************************************************************** */ _Document doc =app.GetActiveDocument(); // 得到ActiveDocument CComVariant FileName(_T( " c://doc.wps " )); // 文件名 CComVariant FileFormat( 101 ); // 重点,看下面的说明 CComVariant LockComments( false ),Password(_T( "" )); CComVariant AddToRecentFiles( true ),WritePassword(_T( "" )); CComVariant ReadOnlyRecommended( false ),EmbedTrueTypeFonts( false ); CComVariant SaveNativePictureFormat( false ),SaveFormsData( false ); CComVariant SaveAsAOCELetter( false ); /* ************** FileFormat 文件格式说明 **************************** 参数FileFormat,在WORD的宏中,使用的是 wdFormatDocument,这是什么那? 其实这是WORD宏中所使用的常量,由匈牙利命名可以知道wd其实是DWORD的意思 知道了是一个正数,那么它到底是多少那?其实有一个办法可以知道,那就是在 WORD宏程序中,加一条语句:MsgBox wdFormatDocument 这样你再运行宏程序, 就能看到这个常量是多少了。呵呵,这个常量是0,我够聪明吧^_^ ******************************************************************** */ doc.SaveAs( &FileName,&FileFormat,&LockComments,& Password, &AddToRecentFiles,&WritePassword,& ReadOnlyRecommended, &EmbedTrueTypeFonts,&SaveNativePictureFormat,& SaveFormsData, & SaveAsAOCELetter); sel.ReleaseDispatch(); doc.ReleaseDispatch(); docs.ReleaseDispatch(); CComVariant SaveChanges( false ),OriginalFormat,RouteDocument; app.Quit( &SaveChanges,&OriginalFormat,& RouteDocument); app.ReleaseDispatch(); AfxMessageBox(_T( " 请检查c://hello.doc是否正常产生了。下面该学习Setp5了 " )); }
例子5:
/* **************** WORD 中录制的宏,按笔画排序(全部选择,菜单表格/排序) ******** MsgBox wdSortFieldStroke '5 使用了常量,所以使用MsgBox得到具体的数值 MsgBox wdSortOrderAscending '0 MsgBox wdSortFieldSyllable '3 MsgBox wdSeparateByTabs '1 MsgBox wdSimplifiedChinese '2052 Selection.WholeStory '全选 Selection.Sort ExcludeHeader:=False, FieldNumber:="段落数", SortFieldType:= _ wdSortFieldStroke, SortOrder:=wdSortOrderAscending, FieldNumber2:="", _ SortFieldType2:=wdSortFieldSyllable, SortOrder2:=wdSortOrderAscending, _ FieldNumber3:="", SortFieldType3:=wdSortFieldSyllable, SortOrder3:= _ wdSortOrderAscending, Separator:=wdSortSeparateByTabs, SortColumn:=False, _ CaseSensitive:=False, LanguageID:=wdSimplifiedChinese Selection.Copy '把排序后的结果,复制到剪贴板 ******************************************************************************** */ #include " msword9.h " #include <AtlBase.h> void CStep5Dlg::OnOK() { CString str; GetDlgItemText(IDC_EDIT1,str); str.TrimLeft(); str.TrimRight(); if (str.IsEmpty()) return ; ::CoInitialize(NULL); _Application app; app.CreateDispatch(_T( " Word.Application " )); // app.SetVisible(FALSE); // 这次不调用显示,因为我们要偷偷摸摸的转换:) Documents docs= app.GetDocuments(); CComVariant Template( "" ),NewTemplate( false ),DocumentType( 0 ),Visible; docs.Add( &Template,&NewTemplate,&DocumentType,& Visible); Selection sel = app.GetSelection(); for ( int i= 0 ;i<str.GetLength()/ 2 ;i++ ) { // 这里只考虑了输入为纯汉字的情况,你自己修改为可以支持中英文混合的情况 sel.TypeText(str.Mid(i* 2 , 2 )+ " /r/n " ); // 2个字符表示一个汉字,用回车换行分隔 } sel.WholeStory(); // 全部选择 CComVariant ExcludeHeader( false ); CComVariant FieldNumber(_T( " 段落数 " )),SortFieldType( 5 ),SortOrder( 0 ); CComVariant FieldNumber2(_T( "" )),SortFieldType2( 3 ),SortOrder2( 0 ); CComVariant FieldNumber3(_T( "" )),SortFieldtype3( 3 ),SortOrder3( 0 ); CComVariant SortColumn( false ),Separator( 1 ),LanguageID( 2052 ); CComVariant CaseSensitive( false ),BidiSort,IgnoreThe; CComVariant IgnoreKashida,IgnoreDiacritics,IgnoreHe; // 排序 sel.Sort(&ExcludeHeader,&FieldNumber,&SortFieldType,& SortOrder, &FieldNumber2,&SortFieldType2,&SortOrder2,& FieldNumber3, &SortFieldtype3,&SortOrder3,&SortColumn,& Separator, &CaseSensitive,&BidiSort,&IgnoreThe,& IgnoreKashida, &IgnoreDiacritics,&IgnoreHe,& LanguageID); // 其实,这里可以直接调用sel.GetText()取得文本。 // 但现在选择复制到剪贴板的方式。 sel.Copy(); // 复制到剪贴板 if (OpenClipboard()) { // 从剪贴板取出排序后的文字 HGLOBAL hMem= ::GetClipboardData(CF_TEXT); LPCTSTR lp = (LPCTSTR)::GlobalLock(hMem); str = lp; ::GlobalUnlock(hMem); CloseClipboard(); str.Replace( " /r/n " , "" ); // 删除回车换行 SetDlgItemText(IDC_EDIT2,str); } sel.ReleaseDispatch(); docs.ReleaseDispatch(); CComVariant SaveChanges( false ),OriginalFormat,RouteDocument; app.Quit( &SaveChanges,&OriginalFormat,& RouteDocument); app.ReleaseDispatch(); ::CoUninitialize(); }
例子6:
#include " msword9.h " void CStep6Dlg::OnOK() { CLSID clsid; HRESULT hr; hr =::CLSIDFromProgID(L " Word.Application " ,&clsid); // 通过ProgID取得CLSID if (FAILED(hr)) { AfxMessageBox(_T( " 不会吧,竟然没有安装OFFICE " )); return ; } IUnknown *pUnknown= NULL; IDispatch *pDispatch= NULL; _Application app = NULL; Selection sel = NULL; hr =::GetActiveObject(clsid,NULL,&pUnknown); // 查找是否有WORD程序在运行 if (FAILED(hr)) { AfxMessageBox(_T( " 没有正在运行中的WORD应用程序 " )); return ; } try { hr =pUnknown->QueryInterface(IID_IDispatch,(LPVOID *)& app); if (FAILED(hr)) throw (_T( " 没有取得IDispatchPtr " )); pUnknown ->Release(); pUnknown= NULL; sel = app.GetSelection(); if (!sel) throw (_T( " 没有正在编辑的文档 " )); sel.WholeStory(); // 全部选择 CString str=sel.GetText(); // 取得文本 SetDlgItemText(IDC_EDIT1,str); // 显示到编辑窗中 } catch (LPCTSTR lpErr) { AfxMessageBox(lpErr); } if (pUnknown) pUnknown-> Release(); if (sel) sel.ReleaseDispatch(); if (app) sel.ReleaseDispatch(); }
7、图片查找的demo(无选中功能)
vc操纵word问题:查找到图片后如何得到该图片的长宽信息
一个Word文档中已经有一张BMP图片。VC打开该文档,并Find查找到该图片。
COleVariant covTrue(( short )TRUE),covFalse(( short )FALSE),covOptional(( long )DISP_E_PARAMNOTFOUND,VT_ERROR); _Application app; // 定义一个WORD的应用对象 app.CreateDispatch(_T( " Word.Application " ))) // 启动WORD app.SetVisible( true ); // 设置用户可见 Documents docs= app.GetDocuments(); docs.Open( // 打开文档 COleVariant((LPCSTR)m_csPath,VT_BSTR), covFalse, // 确定转换 covTrue, // 只读打开 covFalse, // 添加到最近使用文件列表 covOptional, // PasswordDocument. covOptional, // PasswordTemplate. covFalse, // Revert. covOptional, // WritePasswordDocument. covOptional, // WritePasswordTemplate. COleVariant( long ( 1 )), // Format. 如果是Word97,这便是最好一个参数 covOptional, // 编码 下面是Word 2000/2002专用的参数 covTrue) // 可见 Selection sel = app.GetSelection(); Find find = sel.GetFind(); find.ClearFormatting(); find.Execute( // 开始查找图片 COleVariant(_T( " ^g " ),VT_BSTR), covFalse, covFalse, covFalse, covFalse, covFalse, covTrue, COleVariant( long ( 1 )), covFalse, COleVariant( "" ,VT_BSTR), covFalse, covFalse, covFalse, covFalse, covFalse);
8、文字查找(有选中功能)
COleVariant covTrue(( short )TRUE),covFalse(( short )FALSE),covOptional(( long )DISP_E_PARAMNOTFOUND,VT_ERROR); Find t_find = sel.GetFind(); t_find.ClearFormatting(); t_find.Execute(COleVariant(_T( " 的 " ),VT_BSTR), covFalse, covFalse, covFalse, covFalse, covFalse, covTrue, COleVariant( long ( 1 )), covFalse, COleVariant( "" ,VT_BSTR), covFalse, covFalse, covFalse, covFalse, covFalse);
9、带选中功能的
void DoSelctionPicture() {
sel.MoveRight(COleVariant( short ( 1 )),COleVariant( short ( 1 )),COleVariant( short ( 0 ))); // 撤销当前的选中状态。 sel.MoveLeft(COleVariant( short ( 1 )),COleVariant( short ( 1 )),COleVariant( short ( 0 ))); // 撤销当前的选中状态。 COleVariant covTrue(( short )TRUE),covFalse(( short )FALSE),covOptional(( long )DISP_E_PARAMNOTFOUND,VT_ERROR); Find t_find = sel.GetFind(); t_find.ClearFormatting(); t_find.Execute(COleVariant(_T( " ^g " ),VT_BSTR), covFalse, covFalse, covFalse, covFalse, covFalse, covTrue, COleVariant( long ( 1 )), covFalse, COleVariant( "" ,VT_BSTR),//要替换的字符串。空 covFalse, covFalse, covFalse, covFalse, covFalse); sel.MoveRight(COleVariant( short ( 1 )),COleVariant( short ( 1 )),COleVariant( short ( 1 ))); // 选中。bug:多选中了一个右边的字符。 }
10、扩展
// sel.MoveRight(COleVariant((short)1),COleVariant((short)1),COleVariant((short)1)); // 左移,以字为单位。 // m_Sel.MoveDown(COleVariant((short)4),COleVariant((short)1),COleVariant((short)0)); // 下移,以行为单位。 // m_Sel.MoveLeft(COleVariant((short)2),COleVariant((short)1),COleVariant((short)0)); // 左移,以词组为单位。 // m_Sel.MoveLeft(COleVariant((short)1),COleVariant((short)1),COleVariant((short)0)); // 左移,以字为单位。 // m_Sel.MoveStart(COleVariant((short)2),COleVariant((short)1)); // m_Sel.MoveDown(COleVariant((short)4),COleVariant((short)1),COleVariant((short)1)); // 选中下移,以行为单位。 // m_Sel.MoveLeft(COleVariant((short)2),COleVariant((short)1),COleVariant((short)1)); // 选中左移,以词组为单位。 // m_Sel.MoveLeft(COleVariant((short)1),COleVariant((short)1),COleVariant((short)1)); // 选中左移,以字为单位。 // m_Sel.TypeBackspace(); // 删除选中内容。
11、插入图片【此部分来源: http://blog.csdn.net/halin1983/article/details/4533112 】
VC++6.0向Word文件的指定位置上插入一张图片,需要用到nlineShapes类型的AddPicture方法。
在MSDN中,该方法的声明如下:
**************************************************************************
在文档中添加一幅图片。返回一个 Shape 对象,该对象代表图片,并将其添加至 InlineShapes 集合。
expression.AddPicture(FileName, LinkToFile, SaveWithDocument, Range)
expression 必需。该表达式返回一个 InlineShapes 对象。
FileName String 类型,必需。图片的路径和文件名。
LinkToFile Variant 类型,可选。如果为 True,则将图片链接到创建该对象的文件;如果为 False,则将图片作为该文件的独立副本。默认值是 False。
SaveWithDocument Variant 类型,可选。如果为 True,则将链接的图片与文档一起保存。默认值是 False。
Range Variant 类型,可选。图片置于文本中的位置。如果该区域未折叠,那么图片将覆盖此区域,否则插入图片。如果忽略此参数,则自动放置图片
**************************************************************************
根据该说明的定义,第四个参数Range是用来设置图片位于文本中的位置的。因此,我们可以利用该参数向某一指定位置插入图片。具体方法如下:
Selection sel=m_app.GetSelection(); // 获取文档的selection InlineShapes inlineshapes = sel.GetInlineShapes(); inlineshapes.AddPicture( " D:\\abc.bmp " ,COleVariant(( short )FALSE),COleVariant(( short )TRUE),&_variant_t(sel.GetRange())); // 添加图片,并制定其位置为当前光标位置 inlineshapes.ReleaseDispatch(); sel.ReleaseDispatch(); // 注意上面第四个参数的转换方法,用到了_variant_t变量,使用此种方法可以将LPDISPATCH转换成VARIANT*类型
如此就可将图片直接插入到光标当前所在位置
Document对象的保存功能:
LPCWSTR pathstr=L"D:\\abc.doc"; VARIANT path; path.vt=VT_BSTR; path.bstrVal=SysAllocString(pathstr); VARIANT overw; overw.vt=VT_BOOL; overw.boolVal=VARIANT_TRUE; VARIANT name; name.vt=VT_BSTR; name.bstrVal=NULL; m_FramerControl.Save( path,overw,path,path);