前幾日對軟件“文件整理助手”進(jìn)行了完善。該軟件有文本文件合并,文本文件內(nèi)容的替換、插入、刪除、為特定行首/尾添加字符、清理空行等,以及文件批量替換、改名等功能。
一同事見后,希望能對Word文件進(jìn)行合并。盡管Word的“插入文件”可以實現(xiàn)這個功能,但不能在插入文件時調(diào)整插入的順序,也不能控制插入的新文件是否另起一頁。Word雖然功能強大,但還是有一定的局限性。當(dāng)然,通過VBA錄入腳本、編寫宏代碼也許可以實現(xiàn)這些復(fù)雜的功能。但囿于其缺乏通用性和移植性,對于不善于編程的人來說,還是存在諸多不便。
因此,打算做一個“Word文檔合并器”。剛做出這個決定時,以為很簡單,因為Delphi的Servers組件頁有WordApplication、WordDocument等控件,通過它們來控制全不是那么回事!以前做過涉及到Excel的小程序,沒覺得有多難。首次跟Word打交道,竟給我來了個大大的下馬威。
以前用過函數(shù),用過過程,也用過帶參數(shù)的函數(shù)、帶參數(shù)的過程。見過參數(shù)多的,但沒見過打開、保存Word時盡然要用多達(dá)15個、16個參數(shù)的函數(shù)、過程。而且這些參數(shù)青一色地被定義為OleVariant類型,哪些應(yīng)該是字符串,哪些應(yīng)該是布爾型,或者專門為Word程序和文檔定義的變量類型,沒有詳細(xì)的、系統(tǒng)的資料,只好摸著石頭過河,慢慢研究了。
經(jīng)過幾翻碰壁、幾翻查證、幾翻試驗,把要實現(xiàn)的功能一步步拆解,逐一進(jìn)行調(diào)試,通過后再重新組合起來試驗。經(jīng)過拆解、調(diào)試、組裝三步曲之后,總算是完成了“Word文檔合并器”這樣一個小小的軟件。
為避免下次還重復(fù)這種繁瑣的基礎(chǔ)工作,現(xiàn)將有關(guān)技術(shù)要點總結(jié)于下:
?。ū境绦蛟赪ord2003中調(diào)試通過,其他版本未進(jìn)行測試。網(wǎng)上找的一些資料在過程調(diào)用、函數(shù)語句及參數(shù)個數(shù)上有出入,可能與Word版本不一樣有關(guān)。)
說明:
主窗體中放置以下三個與Word有關(guān)的控件:
Word: TWordApplication; //Word應(yīng)用程序接口
Document1: TWordDocument; //Word文檔
ole_ShowDoc: TOleContainer; //用以顯示W(wǎng)ord文檔
【一】相關(guān)Word組件
這里僅整理Delphi通過自身所提供的Server組件連接Office(Word)的有關(guān)資料,其他方法暫不研究。Delphi中提供的與操作Word有關(guān)的組件有共有5個,常用的有4個:
1、TWordApplication對象。對應(yīng)于Microsoft Word應(yīng)用程序,主要用來在Delphi程序中直接啟動或關(guān)閉Word應(yīng)用程序,建立或斷開與Word程序的連接。
2、TWordDocument對象。對應(yīng)于Word文檔,主要用來實現(xiàn)創(chuàng)建、銷毀一個Word文檔,文檔的連接和斷開,文檔中的字符匹配查詢,拼寫和語法檢查以及文檔打印等功能。
3、TWordFont對象。對應(yīng)于Word的字體對象,用來設(shè)置Word文檔中的字體屬性。
4、TWordParagraphFormat對象。對應(yīng)于Word的段落對象,用來設(shè)置文檔中的段落格式。
【二】啟動Word程序
//建立與Word應(yīng)用程序的連接
try
Word:=TWordApplication.Create(nil); //必加此句,否則WORD.Quit后無法再啟用
//提示:“RPC服務(wù)器不可用”。
word.Connect;
except
Application.MessageBox('無法連接Word。'+#13#10#13#10
+'請確認(rèn)已正確安裝了Word,并關(guān)閉了Word中的對話框!','提醒:',Mb_Ok+
MB_ICONSTOP);
Exit;
end;
說明:不要“Word:=TWordApplication.Create(nil); ”也可建立與Word應(yīng)用程序的連接,啟動Word應(yīng)用程序,但
如果用Word.Quit或Word.Destroy退出或注銷Word后,便無法再次與Word建立連接,提示“RPC 服務(wù)器不可用”。添加
此句后,便可隨心所欲地啟動、退出Word應(yīng)用程序。
【三】創(chuàng)建Word文件
新建空白文檔的函數(shù)原型:
{//===========================================================================
Word.Documents.Add(var Template: OleVariant; var NewTemplate: OleVariant;
var DocumentType: OleVariant; var Visible: OleVariant): WordDocument;
============================================================================//}
由于在程序中需要多次調(diào)用Add函數(shù),而函數(shù)中又不能直接使用變量的值,必須通過OleVariant型的變量名進(jìn)行傳遞,
為避免繁瑣的變量定義和賦值,我將其簡化為AddDoc過程:
//WordApplication建立空白文檔過程
procedure AddDoc(word:TWordApplication;Dot:String;NewDot,DocVisible:Boolean); //打開文件
var
Template,NewTemplate,DocumentType,Visible: OleVariant;
begin
Template:=Dot; //使用模板的名稱,
NewTemplate:=NewDot; //新建文檔的類型,True表示為模板,F(xiàn)alse表示為文檔
DocumentType:=EmptyParam; //文檔類型,默認(rèn)為空白文檔
Visible:=DocVisible; //打撈的窗口是否可見
Word.Documents.Add(Template,NewTemplate,DocumentType,Visible);
end;
【四】打開Word文件
打開文件的函數(shù)原型:
{//===========================================================================
Word.Documents.Open(var FileName: OleVariant; var ConfirmConversions: OleVariant;
var ReadOnly: OleVariant; var AddToRecentFiles: OleVariant;
var PasswordDocument: OleVariant; var PasswordTemplate: OleVariant;
var Revert: OleVariant; var WritePasswordDocument: OleVariant;
var WritePasswordTemplate: OleVariant; var Format: OleVariant;
var Encoding: OleVariant; var Visible: OleVariant; var OpenAndRepair: OleVariant;
var DocumentDirection: OleVariant; var NoEncodingDialog: OleVariant): WordDocument;
============================================================================//}
由于在程序中需要多次調(diào)用Open函數(shù),我將其簡化只有2個參數(shù)的OpenDoc過程:
//WordApplication打開文件過程
procedure OpenDoc(word:TWordApplication;sFileName:string);
var
//打開文件的參數(shù)
FileName,CfCversions,ReadOnly,AddToRctFiles,PswDocument,PswTemplate,Revert,
WPswDocument,WPswTemplate,Format,Encoding,Visible,OpenAndRepair,
DocumentDirection,NoEncodingDialog:OleVariant;
begin
// ===== 創(chuàng)建對象 =====
try
Word:=TWordApplication.Create(nil);
word.Connect;
except
Application.MessageBox('本機(jī)可能沒有正確安裝WORD!','提醒:',Mb_Ok+MB_ICONSTOP);
Exit;
end;
// ===== 打開文件 =====
Word.Visible := false;
FileName:=sFileName;
CfCversions := false;
ReadOnly:=False;
AddToRctFiles:= false;
PswDocument:= '';
PswTemplate:= '';
Revert:=true;
WPswDocument:= '';//文檔密碼
WPswTemplate:= '';//模板密碼
Format:= EmptyParam;
Encoding:= '';
Visible:=False;
OpenAndRepair:= EmptyParam;
DocumentDirection:= EmptyParam;
NoEncodingDialog:= EmptyParam;
Word.Documents.open(FileName,CfCversions,ReadOnly,AddToRctFiles,PswDocument,
PswTemplate,Revert,WPswDocument,WPswTemplate,Format,Encoding,Visible,
OpenAndRepair,DocumentDirection,NoEncodingDialog);
end;
【五】連接Word文件
將新建的或打開的word文檔通過TWordDocument對象的ConnectTo方法與TWordapplication實例建立關(guān)聯(lián)。
var
DocInx: OleVariant;
begin
DocInx:=1;
// Document1.ConnectTo(Word.ActiveDocument);
Document1.ConnectTo(Word.Documents.Item(DocInx));
end;
【六】保存Word文件
Word保存文件過程的原型:
{//===========================================================================
Word.ActiveDocument.SaveAs(var FileName: OleVariant; var FileFormat: OleVariant;
var LockComments: OleVariant; var Password: OleVariant;
var AddToRecentFiles: OleVariant; var WritePassword: OleVariant;
var ReadOnlyRecommended: OleVariant; var EmbedTrueTypeFonts: OleVariant;
var SaveNativePictureFormat: OleVariant; var SaveFormsData: OleVariant;
var SaveAsAOCELetter: OleVariant; var Encoding: OleVariant;
var InsertLineBreaks: OleVariant; var AllowSubstitutions: OleVariant;
var LineEnding: OleVariant; var AddBiDiMarks: OleVariant);
safecall;
============================================================================//}
為避免繁瑣地使用保存文件過程,精簡為:
//WordApplication保存文件過程
procedure SaveDoc(word:TWordApplication;sFileName:string);
var
//保存文件的參數(shù)
FileName,FileFormat,LockComments,Password,AddToRecentFiles,
WritePassword, ReadOnlyRecommended,EmbedTrueTypeFonts,SaveNativePictureFormat,
SaveFormsData,SaveAsAOCELetter,Encoding,InsertLineBreaks,AllowSubstitutions,
LineEnding,AddBiDiMarks: OleVariant;
begin
FileName:=sFileName;
FileFormat:= EmptyParam;
LockComments:= EmptyParam;
Password:= EmptyParam;
AddToRecentFiles:= EmptyParam;
WritePassword:= EmptyParam;
ReadOnlyRecommended:= EmptyParam;
EmbedTrueTypeFonts:= EmptyParam;
SaveNativePictureFormat:= EmptyParam;
SaveFormsData:= EmptyParam;
SaveAsAOCELetter:= EmptyParam;
Encoding:= EmptyParam;
InsertLineBreaks:= EmptyParam;
AllowSubstitutions:= EmptyParam;
LineEnding:= EmptyParam;
AddBiDiMarks:= EmptyParam;
Word.ActiveDocument.SaveAs(FileName,FileFormat,LockComments,Password,
AddToRecentFiles,WritePassword,ReadOnlyRecommended,EmbedTrueTypeFonts,
SaveNativePictureFormat, SaveFormsData,SaveAsAOCELetter,Encoding,
InsertLineBreaks,AllowSubstitutions,LineEnding,AddBiDiMarks);
end;
如果通過TWordDocument對象保存文件,則比較簡單:
Document1.SaveAs(newFileName);
【七】插入Word文件
Word插入文件過程的原型:
{//===========================================================================
InsertFile(const FileName: WideString; var Range: OleVariant;
var ConfirmConversions: OleVariant; var Link: OleVariant;
var Attachment: OleVariant); safecall;
============================================================================//}
//向打開的Word文件中插入外部文件
var i:Integer;
myRange,CfCversions,Link,Attachment: OleVariant;
s:WideString;
begin
for i :=0 to List.Items.Count-1 do
begin
myRange:=EmptyParam;
CfCversions:=EmptyParam;
Link:=EmptyParam;
Attachment:=EmptyParam;
myType:=wdPageBreak;
if (chk_AddNewPage.Checked) and (i>0) then Word.Selection.InsertBreak(myType);
s:=List.Items[i];
Word.Selection.InsertFile(s,myRange,CfCversions,Link,Attachment);
end;
end;
如果在插入文件時,要另起一頁,則可在插入文件前執(zhí)行:
//var myType:OleVariant;
myType:=wdPageBreak;
Word.Selection.InsertBreak(myType);
插入“分隔符”的類型定義如下:
const
wdSectionBreakNextPage = $00000002;
wdSectionBreakContinuous = $00000003;
wdSectionBreakEvenPage = $00000004;
wdSectionBreakOddPage = $00000005;
wdLineBreak = $00000006;
wdPageBreak = $00000007;
wdColumnBreak = $00000008;
wdLineBreakClearLeft = $00000009;
wdLineBreakClearRight = $0000000A;
wdTextWrappingBreak = $0000000B;
如果要插入字符串,可以使用如下方法:
Document1.Characters.Last.Select;//選擇最后字符
Document1.Range.InseflAfter('要輸入的文字'+#13);
【八】關(guān)閉Word文件
//關(guān)閉打開的Word文件
{//===========================================================================
procedure Close(var SaveChanges: OleVariant; var OriginalFormat: OleVariant;
var RouteDocument: OleVariant); safecall;
============================================================================//}
var
SaveChanges,OriginalFormat,RouteDocument: OleVariant;
begin
Document1.Disconnect;
Document1.Close;
SaveChanges:=False;
OriginalFormat:=EmptyParam;
RouteDocument:=EmptyParam;
Word.Documents.Close(SaveChanges,OriginalFormat,RouteDocument);
end;
【九】退出Word程序
//退出Word
begin
Word.Disconnect;
// Word.Destroy;
WORD.Quit;
end;
【十】其他相關(guān)操作
var
WORDAPP:Twordapplication;
WORDdocument:TWordDocument;
itemindex:OleVariant;
template,newtemplate,documenttype,visible:olevariant;
curr_range:Range;
row,col:Integer;
direction:OleVariant;
defaulttablebehvior,autofitbehvior:OleVariant;
curr_table:Table;
myrange:OleVariant;
savefile:OleVariant;
begin
try
try
WORDAPP:=TWordApplication.Create(nil);
WORDdocument:=TWordDocument.Create(nil);
except
ShowMessage('本機(jī)可能沒有裝WORD!');
Exit;
end;
itemindex:=1;
WORDAPP.Connect;
WORDAPP.Visible:=true;
WORDAPP.Documents.AddOld(EmptyParam,EmptyParam) ;
WORDdocument.ConnectTo(WORDAPP.Documents.Item(itemindex) as _document);
//關(guān)閉拼寫檢查,因為這會浪費較多時間
WORDAPP.Options.CheckSpellingAsYouType:= False;
WORDAPP.Options.CheckGrammarAsYouType:= False;
//頁面設(shè)置
with WORDdocument.PageSetup do
begin
PaperSize:=wdPaperA4; //wdPaperA4 = $00000007;;
LeftMargin:= WORDAPP.CentimetersToPoints(2.0);
RightMargin:= WORDAPP.CentimetersToPoints(2.0);
TopMargin:= WORDAPP.CentimetersToPoints(2.0);
BottomMargin:=WORDAPP.CentimetersToPoints(2.0);
Orientation:= wdOrientLandscape; //橫向打印
//centerHorizontally:= True; //水平對齊方式
end;
//寫標(biāo)題
curr_range:= WORDdocument.Range;
curr_range.InsertAfter('插入標(biāo)題文字'+#13#10);
curr_range.Font.Size:=14;
curr_range.Bold:=1;
curr_range.ParagraphFormat.Alignment:=wdAlignPageNumberCenter; //居中對齊
//加入表格
direction:=wdCollapseEnd; //定位到標(biāo)題的下一行加入表格
curr_range.Collapse(direction);
defaulttablebehvior:=wdWord10ListBehavior; //畫邊框線
autofitbehvior:=wdAutoFitWindow;
col:=DBGridEh1.DataSource.DataSet.FieldCount-1; //列數(shù)
row:=DBGridEh1.DataSource.DataSet.RecordCount+1; //行數(shù)
//下面兩種方式也能通過
//myrange:=WORDdocument.Content.End_-1; //定位到標(biāo)題的下一行加入表格
//curr_table:=WORDdocument.Tables.AddOld(WORDdocument.Range(myrange),row,col);
curr_table:=WORDdocument.Tables.Add(curr_range,row,col,defaulttablebehvior,autofitbehvior);
//WORDdocument.Tables.AddOld(curr_range,row,col);
curr_table.Range.Paragraphs.Alignment:= wdAlignParagraphLeft; //對齊方式左對齊
//寫字段名及值
with DBGridEh1.DataSource.DataSet do
begin
for col:=1 to FieldCount-1 do //寫字段名
begin
curr_table.Cell(1,col).Range.Font.Name:='宋體';
curr_table.Cell(1,col).Range.Font.Size:=12;
curr_table.Cell(1,col).Range.Font.Bold:=Integer(False);
if col=1 then curr_table.Cell(1,col).Range.Text:='序號'
else curr_table.Cell(1,col).Range.Text:=Fields[col-1].FieldName;
//curr_table.Columns.AutoFit;
end;
row:=2;
First;
while not Eof do //寫值
begin
for col:=1 to FieldCount-1 do
begin
if col=1 then curr_table.Cell(row,col).Range.Text:=IntToStr(row-1)
else
begin
if ( Fields[COL-1].FieldName='開始時間' ) or ( Fields[COL-1].FieldName='終止時間' ) then
curr_table.Cell(row,col).Range.Text:=FormatDateTime('yyyy-MM-dd',Fields[COL-
1].AsDateTime)
else curr_table.Cell(row,col).Range.Text:=Fields[COL-1].AsString;
end;
curr_table.Cell(row,col).Range.Font.Size:=11;
curr_table.Cell(row,col).Range.Font.Name:='宋體';
curr_table.Cell(row,col).Range.Font.Bold:=Integer(False);
end;
inc(row);
Next;
end;
curr_table.Columns.AutoFit;
end;
finally
if Trim(savefilename)<>'' then
begin
savefile:=savefilename+'.doc';
WORDdocument.SaveAs(savefile);
end;
WORDdocument.Disconnect;
WORDAPP.Disconnect;
FreeAndNil(WORDdocument);
FreeAndNil(WORDAPP);
end;
end;