分享

Delphi控制Word编程手记

 饮冰E士 2013-02-01

  前几日对软件“文件整理助手”进行了完善。该软件有文本文件合并,文本文件内容的替换、插入、删除、为特定行首/尾添加字符、清理空行等,以及文件批量替换、改名等功能。
  一同事见后,希望能对Word文件进行合并。尽管Word的“插入文件”可以实现这个功能,但不能在插入文件时调整插入的顺序,也不能控制插入的新文件是否另起一页。Word虽然功能强大,但还是有一定的局限性。当然,通过VBA录入脚本、编写宏代码也许可以实现这些复杂的功能。但囿于其缺乏通用性和移植性,对于不善于编程的人来说,还是存在诸多不便。
  因此,打算做一个“Word文档合并器”。刚做出这个决定时,以为很简单,因为Delphi的Servers组件页有WordApplication、WordDocument等控件,通过它们来控制全不是那么回事!以前做过涉及到Excel的小程序,没觉得有多难。首次跟Word打交道,竟给我来了个大大的下马威。
  以前用过函数,用过过程,也用过带参数的函数、带参数的过程。见过参数多的,但没见过打开、保存Word时尽然要用多达15个、16个参数的函数、过程。而且这些参数青一色地被定义为OleVariant类型,哪些应该是字符串,哪些应该是布尔型,或者专门为Word程序和文档定义的变量类型,没有详细的、系统的资料,只好摸着石头过河,慢慢研究了。
  经过几翻碰壁、几翻查证、几翻试验,把要实现的功能一步步拆解,逐一进行调试,通过后再重新组合起来试验。经过拆解、调试、组装三步曲之后,总算是完成了“Word文档合并器”这样一个小小的软件。
  为避免下次还重复这种繁琐的基础工作,现将有关技术要点总结于下:
  (本程序在Word2003中调试通过,其他版本未进行测试。网上找的一些资料在过程调用、函数语句及参数个数上有出入,可能与Word版本不一样有关。)
说明:
  主窗体中放置以下三个与Word有关的控件:
    Word: TWordApplication;     //Word应用程序接口
    Document1: TWordDocument;   //Word文档
  ole_ShowDoc: TOleContainer; //用以显示Word文档
 
【一】相关Word组件
  这里仅整理Delphi通过自身所提供的Server组件连接Office(Word)的有关资料,其他方法暂不研究。Delphi中提供的与操作Word有关的组件有共有5个,常用的有4个:
  1、TWordApplication对象。对应于Microsoft Word应用程序,主要用来在Delphi程序中直接启动或关闭Word应用程序,建立或断开与Word程序的连接。
  2、TWordDocument对象。对应于Word文档,主要用来实现创建、销毁一个Word文档,文档的连接和断开,文档中的字符匹配查询,拼写和语法检查以及文档打印等功能。
  3、TWordFont对象。对应于Word的字体对象,用来设置Word文档中的字体属性。
  4、TWordParagraphFormat对象。对应于Word的段落对象,用来设置文档中的段落格式。
 
【二】启动Word程序
//建立与Word应用程序的连接  
  try
    Word:=TWordApplication.Create(nil); //必加此句,否则WORD.Quit后无法再启用
                                        //提示:“RPC服务器不可用”。
    word.Connect;
  except
    Application.MessageBox('无法连接Word。'+#13#10#13#10
      +'请确认已正确安装了Word,并关闭了Word中的对话框!','提醒:',Mb_Ok+
      MB_ICONSTOP);
    Exit;
  end;
  说明:不要“Word:=TWordApplication.Create(nil); ”也可建立与Word应用程序的连接,启动Word应用程序,但

如果用Word.Quit或Word.Destroy退出或注销Word后,便无法再次与Word建立连接,提示“RPC 服务器不可用”。添加

此句后,便可随心所欲地启动、退出Word应用程序。
 
【三】创建Word文件
新建空白文档的函数原型:
{//===========================================================================
Word.Documents.Add(var Template: OleVariant; var NewTemplate: OleVariant;
     var DocumentType: OleVariant; var Visible: OleVariant): WordDocument;
============================================================================//}
由于在程序中需要多次调用Add函数,而函数中又不能直接使用变量的值,必须通过OleVariant型的变量名进行传递,

为避免繁琐的变量定义和赋值,我将其简化为AddDoc过程:
//WordApplication建立空白文档过程
procedure AddDoc(word:TWordApplication;Dot:String;NewDot,DocVisible:Boolean);    //打开文件
var
  Template,NewTemplate,DocumentType,Visible: OleVariant;
begin
  Template:=Dot;           //使用模板的名称,
  NewTemplate:=NewDot;  //新建文档的类型,True表示为模板,False表示为文档
  DocumentType:=EmptyParam;       //文档类型,默认为空白文档
  Visible:=DocVisible;       //打捞的窗口是否可见
  Word.Documents.Add(Template,NewTemplate,DocumentType,Visible);
end;
 
【四】打开Word文件
打开文件的函数原型:
{//===========================================================================
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;
============================================================================//}
由于在程序中需要多次调用Open函数,我将其简化只有2个参数的OpenDoc过程:
//WordApplication打开文件过程
procedure OpenDoc(word:TWordApplication;sFileName:string);
var
  //打开文件的参数
  FileName,CfCversions,ReadOnly,AddToRctFiles,PswDocument,PswTemplate,Revert,
  WPswDocument,WPswTemplate,Format,Encoding,Visible,OpenAndRepair,
  DocumentDirection,NoEncodingDialog:OleVariant;
begin
// ===== 创建对象 =====
  try
    Word:=TWordApplication.Create(nil);
    word.Connect;
  except
    Application.MessageBox('本机可能没有正确安装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实例建立关联。
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
//保存文件的参数
  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;
如果在插入文件时,要另起一页,则可在插入文件前执行:
//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);
 
【八】关闭Word文件
//关闭打开的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;

【十】其他相关操作
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('本机可能没有装WORD!');
      Exit;
    end;
    itemindex:=1;
    WORDAPP.Connect;
    WORDAPP.Visible:=true;
    WORDAPP.Documents.AddOld(EmptyParam,EmptyParam) ;
    WORDdocument.ConnectTo(WORDAPP.Documents.Item(itemindex) as _document);
    //关闭拼写检查,因为这会浪费较多时间
    WORDAPP.Options.CheckSpellingAsYouType:= False;
    WORDAPP.Options.CheckGrammarAsYouType:= False;
    //页面设置
    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;
    //写标题
    curr_range:= WORDdocument.Range;
    curr_range.InsertAfter('插入标题文字'+#13#10);
    curr_range.Font.Size:=14;
    curr_range.Bold:=1;
    curr_range.ParagraphFormat.Alignment:=wdAlignPageNumberCenter;  //居中对齐
    //加入表格
    direction:=wdCollapseEnd;                                       //定位到标题的下一行加入表格
    curr_range.Collapse(direction);
    defaulttablebehvior:=wdWord10ListBehavior;        //画边框线
    autofitbehvior:=wdAutoFitWindow;
    col:=DBGridEh1.DataSource.DataSet.FieldCount-1;   //列数
    row:=DBGridEh1.DataSource.DataSet.RecordCount+1;  //行数
      //下面两种方式也能通过
      //myrange:=WORDdocument.Content.End_-1;         //定位到标题的下一行加入表格
      //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;

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多