[轉]我們操縱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();
}