[返回]
计算机世界1999年第10期

通过Automation控制Word 97

西安交通大学计算机系 刘明华

  Microsoft Word 97 是 功 能 强 大 的 文 字 处 理 软 件。 由 于 微 软 使 用 了 自 动 化 技 术( 即 以 前 的OLE 自 动 化), 并 且 把Word 中 自 动 化 对 象 的 属 性、 方 法 等 详 细 资 料 公 布 于 众, 这 就 使 得Word 表 现 出 强 大 的 可 扩 充 性。 用 户 或 者 第 三 方 的 开 发 人 员 可 以 使 用Word 提 供 的 自 动 化 服 务 来 扩 展Word 的 功 能 和 控 制Word。 为 了 充 分 利 用Word 的 自 动 化 功 能, 微 软 将Visual Basic 改 造 为Visual Basic for Applications, 并 将 其 集 成 到Word 中。 因 为VBA 的 引 入,Word 才 有 了" 宏"。 诚 然,VBA 有 许 多 优 势, 如 简 单 易 学、 与Word 的 结 合 紧 密 等, 它 的 一 个 明 显 的 限 制 便 是 它 的 运 行 必 须 在Word 环 境(Office 环 境) 中。 而 某 些 时 候, 我 们 希 望 在Word 环 境 之 外 来 控 制Word, 比 如, 要 把 文 字 发 送 到Word 中 去,VBA 就 无 能 为 力 了。

  Word 97 是 一 个 自 动 化 服 务 器 程 序, 它 是 依 靠 微 软 的COM 组 件 模 型 来 实 现 的。COM 组 件 模 型 是 一 种 基 于 客 户/ 服 务 器 结 构 的 技 术 规 范, 它 使 得 软 件 部 件 与 应 用 程 序 的 交 互 成 为 可 能。 除 了 基 本 的COM 服 务 外, 微 软 对 这 项 技 术 进 行 了 如 下 扩 充:Automation servers,Automation controllers( 即 自 动 化 对 象 的 客 户 程 序),ActiveX controls,Type libraries,Active Documents,Visual cross -process objects。COM 的 实 现 与 编 程 语 言 无 关。 要 使 用Word 97 的 自 动 化, 其 实 就 是 要 编 写 自 动 化 控 制 程 序( 即 客 户 程 序)。 本 文 中 笔 者 想 以Delphi 为 例, 谈 谈 如 何 有 效 地 使 用Word 97 提 供 的 自 动 化 功 能。 读 者 也 可 根 据 本 文 的 介 绍, 用 其 他 的 编 程 语 言 或 者 工 具 来 实 现。

  Delphi 提 供 了variant 类 型 的 变 量, 我 们 可 以 用 一 个variant 变 量 来 指 代 一 个 自 动 化 对 象。 通 过variant 变 量, 我 们 可 以 访 问 自 动 化 对 象 的 属 性 或 者 方 法。 由 于 自 动 化 对 象 的 方 法 调 用 和 属 性 读 写 的 合 法 性 都 是 在 程 序 运 行 时 进 行 动 态 检 查 的, 而 编 译 期 间 并 不 检 查 它 们 的 合 法 性, 所 以, 我 们 不 需 要 声 明 它 们。 但 是 如 果 某 个 自 动 化 对 象 不 提 供 某 一 个 属 性 或 者 方 法, 而 其 他 的 程 序 却 引 用 了 它, 那 么 就 会 引 起 运 行 期 的 错 误。 故 而, 在 编 写 自 动 化 服 务 器 的 客 户 程 序 之 前, 我 们 必 须 了 解 自 动 化 对 象 的 方 法 和 属 性。 下 面 笔 者 简 单 地 介 绍 一 下Word 97 中 的 自 动 化 对 象 及 其 常 用 方 法、 属 性。

  Word 97 的 自 动 化 对 象 有 两 个:Word.Application 和Word.Basic。

  Word 97 提 供 了Application 自 动 化 对 象( 即Word.Application), 该 对 象 代 表 整 个Word 97 应 用 程 序。Application 对 象 包 括 可 返 回 最 高 一 级 对 象 的 属 性 和 方 法。 例 如,ActiveDocument 属 性 可 返 回 Document 对 象。Application 对 象 包 含 有 许 多 属 性, 如ActiveDocumnet( 返 回 当 前 的 活 动 文 档), CapsLock( 大 写 锁 定 键 的 当 前 状 态), Caption(Word 97 的 标 题); 以 及 方 法, 如,Activate, CheckSpelling, CheckGrammar 等。 每 个 子 对 象 下 面 又 定 义 了 属 性、 方 法。 如 果 你 以 前 用VBA 编 写 过Word 97 宏 命 令 的 话, 那 么 你 对 这 些 对 象、 属 性 及 方 法 一 定 比 较 熟 悉 了。 如 果 你 还 不 具 备 这 些 信 息 的 话, 那 也 没 什 么 关 系, 你 可 以 在Office 97 光 盘 中 的Vbawrd8.hlp 帮 助 文 件 中 获 取 详 细 的 资 料。 在 本 文 中, 笔 者 的 重 点 放 在 如 何 编 写 客 户 程 序 上。

  为 了 使 用Word 97 的 服 务, 首 先 我 们 需 要 创 建 一 个Word.Application 自 动 化 对 象。 使 用CreateOleObject 函 数 我 们 可 以 创 建 一 个 自 动 化 对 象, 此 函 数 的 原 型 为:



   function CreateOleObject(const ClassName: string): IDispatch;

  当 我 们 使 用CreateOleObject 时, 它 所 进 行 的 操 作 便 是 先 启 动 一 份Word 97 拷 贝( 不 管Word 97 是 不 是 正 在 运 行), 然 后 返 回 一 个Word.Application。 如 果 系 统 中 已 经 有Word 97 在 运 行, 再 启 动 一 份 显 然 是 浪 费 内 存 ! 这 种 情 况 下, 我 们 希 望 获 取 当 前 这 份Word 97 的Word.Application 对 象。 为 了 解 决 这 个 问 题, 笔 者 通 过 查 阅Delphi Help 和Win32 OLE Help 及 自 己 的 实 践, 找 到 了 一 种 比 较 简 便 的 方 法:



function GetWordObject (const ClassName: string): IDispatch;

var

  ClassID: TGUID;

  Unknown: IUnknown;

begin

  ClassID := ProgIDToClassID (ClassName);

  if Succeeded (GetActiveObject (ClassID, nil, Unknown)) then

     OleCheck (Unknown.QueryInterface (IDispatch, Result))

  else

    Result := CreateOleObject (ClassName);

end;

  许 多 自 动 化 对 象 支 持 两 种 参 数 传 递 方 式: 按 顺 序 或 者 按 名 字。 综 合 以 上 的 叙 述, 笔 者 用Delphi 编 写 了 一 个 比 较 简 单 的 例 子, 它 的 功 能 是 在Word 97 中 创 建 一 个 新 文 档, 并 且 向 这 个 文 档 中 发 送 文 字。 程 序 如 下:



unit Unit1;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics,

  Controls, Forms, Dialogs, StdCtrls,

  ComObj, ActiveX;

  { 一 定 要 包 含ComObj 和ActiveX 两 个 单 元}



type

  TForm1 = class(TForm)

    Button1: TButton;

    SaveDialog1: TSaveDialog;

    Memo1: TMemo;

    procedure FormCreate(Sender: TObject);

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{ $R *.DFM}



var  Word97: Variant;



function GetWordObject 

   (const ClassName: string): IDispatch;

{ 如 果Word 97 没 有 运 行, 则 启 动 它,

  并 返 回 一 个 自 动 化 对 象;

 如 果Word 97 已 经 启 动, 就 返 回 正 在 

   运 行 的 实 例 的 自 动 化 对 象。}

var

  ClassID: TGUID;

  Unknown: IUnknown;

begin

  ClassID := ProgIDToClassID (ClassName);

  if Succeeded (GetActiveObject 

    (ClassID, nil, Unknown)) then

OleCheck (Unknown.QueryInterface 

 (IDispatch, Result))

  else

    Result := CreateOleObject (ClassName);

end;



procedure TForm1.FormCreate(Sender: TObject);

begin

{ 获 取( 创 建)Word.Application 自 动 化 对 象,

 如 果Word 97 尚 未 运 行,Word 97 将 会 被 启 动}

  Word97 := GetWordObject ('Word.Application');

end;



procedure TForm1.Button1Click(Sender: TObject);

var

   NewDocument, TheRange: Variant;

begin

 // 让Word 97 可 见

  Word97.Visible := True;

  Word97.UserName:=' 刘 明 华';

  Word97.Caption:='Delphi is Great!';



 // 增 加 一 个 新 的Word 文 档 对 象

  NewDocument:=Word 97.Documents.Add;

 // 设 置 第 一 段 的 内 容、 字 体、

    字 体 粗 细、 字 体 的 颜 色 及 大 小

  NewDocument.Paragraphs.Item(1).

    Range.Text:='Hello, My Fellow Chinese!';

  NewDocument.Paragraphs.Item(1).

    Range.Bold :=True;

  NewDocument.Paragraphs.Item(1).

    Range.Font.Size :=30;

  NewDocument.Paragraphs.Item(1).

    Range.Font.Name:='Comic Sans MS';

  NewDocument.Paragraphs.Item(1).

    Range.Font.ColorIndex:=6;//Red

  // 增 加 一 段

  NewDocument.Paragraphs.Add;

  { 通 过 下 面 的 赋 值 语 句, 

    使 得Variant 变 量TheRange 等 同 于

  NewDocument.Paragraphs.Item(2).Range 对 象}

  TheRange:=NewDocument.Paragraphs.Item(2).Range;

 // 获 取 本 份Word 97 的 合 法 用 户 名, 

   并 将 此 信 息 作 为 文 档 第 二 段 的 内 容

  TheRange.Text:=String('This copy of Word 97

     is licensed to ' +Word97.Application.UserName);

  TheRange.Font.Name:='Times New Roman';

  TheRange.Font.Italic:=TRUE;

  TheRange.Font.Size:=18;

  TheRange.Font.ColorIndex:=4;

  // 把Memo1 中 的 内 容 插 入 文 档

  NewDocument.Paragraphs.Add;

  TheRange:=NewDocument.Paragraphs.Item(3).Range;

  TheRange.Font.Italic:=False;

  TheRange.Font.Size:=16;

  TheRange.Text:=Memo1.Text;

  TheRange.Font.ColorIndex:=5;

  {

  // 注 释 中 的 这 段 代 码 将 

     提 示 用 户 保 存 这 个 新 文 档

  if SaveDialog1.Execute then

    NewDocument.SaveAs (

      FileName := WideString (SaveDialog1.Filename),

      FileFormat := 0,

      SaveNativePictureFormat := 1);

  // 注 意

  }

 end;

end.