通过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.