65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 VOLE 驱动 Microsoft Word

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (12投票s)

2007年8月10日

6分钟阅读

viewsIcon

68396

downloadIcon

910

另一种描述 VOLE 自动化库的方式

引言

本文是关于 VOLE C++/COM 自动化驱动程序库系列文章的第二篇。(第一篇 在这里。)它演示了如何使用 VOLE 重写(并大大简化)Microsoft 自己提供的代码示例,以说明如何从 C++ 驱动 Word 的自动化对象模型。

背景

最近,一位 VOLE 的新用户给我发电子邮件,询问 VOLE 是否能够处理驱动 Microsoft Word,以实现 Microsoft 自己提供的代码示例 中涉及的功能。他写道:

我刚刚在寻找一种相对合理的方法将一些 Excel 自动化添加到我的 C++ 程序中时发现了你的库。由于我并不熟悉 COM,我觉得这个示例 http://support.microsoft.com/kb/238393 太吓人了。然后我发现了你的封装库,它承诺让 COM 更容易使用。

他继续说道:

我仍然需要处理像这样的代码

    SAFEARRAYBOUND sab[2];
    sab[0].lLbound = 1;
    sab[0].cElements = 15;
    sab[1].lLbound = 1;
    sab[1].cElements = 15;
    arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab);

这并不令人鼓舞。我还没有太多地使用我从你的网站下载的代码,但你的库是否能够将这些东西隐藏在一个现代 C++ 接口后面呢?”"

简单的答案是:是的

在本文中,我将使用 VOLE 重写 Microsoft 示例,演示如何驱动具有复杂对象模型的 COM 自动化服务器。

场景

Microsoft 示例执行以下操作:

  1. 查找“Word.Application”的 CLSID
  2. 启动 Word 并获取应用程序对象的 IDispatch 指针
  3. 使 Word 可见
  4. 获取 Documents 集合
  5. 调用 Documents.Open() 打开“C:\Doc1.doc”
  6. 获取 BuiltinDocumentProperties 集合
  7. BuiltInDocumentProperties 集合中获取“Subject”属性
  8. 获取“Subject”属性的 Value
  9. 设置“SubjectDocumentPropertyValue
  10. 获取 CustomDocumentProperties 集合
  11. 添加一个名为“CurrentYear”的新属性
  12. 获取自定义属性“CurrentYear”并删除它
  13. 关闭文档而不保存更改
  14. 退出 Word

VOLE 版本

你可以在 Microsoft 示例 中看到用 C++ 实现此功能的完整手动方法的恐怖之处。现在让我们看看如何使用 VOLE 来实现。

// Step 2
object word = object::create("Word.Application", CLSCTX_LOCAL_SERVER);

// Step 3
word.put_property(L"Visible", true);

// Step 4
collection documents = word.get_property<collection>(L"Documents");

// Step 5
object document = documents.invoke_method<object>(L"Open", L"C:\\Doc1.doc");

// Step 6
collection builtInProps = document.get_property<collection>(
    L"BuiltinDocumentProperties");

// Step 7
object propSubject = builtInProps.get_property<object>(L"Item", L"Subject");

// Step 8
std::string subject = propSubject.get_property<std::string>(L"Value");

std::cout << "Subject property: \"" << subject << '"' << std::endl;

// Step 9
propSubject.put_property(L"Value", "This is my subject");

// Step 10
collection customProps = document.get_property<collection>(
    L"CustomDocumentProperties");

// Step 11
customProps.invoke_method<void>(L"Add", L"CurrentYear", false, 1, 1999);

// Step 12
object propCurrYear = customProps.get_property<object>(
    L"Item", L"CurrentYear");

propCurrYear.invoke_method<void>(L"Delete");

// Step 13
document.invoke_method<void>(L"Close", false);

// Step 14
word.invoke_method<void>(L"Quit");

完整程序包含在下载文件中;这里我将只展示关键部分。为了清晰起见,我们将假定已为 VOLE 类使用了 using 声明。

步骤 1. 查找“Word.Application”的 CLSID

这一步很简单:我们可以完全省略它。

因为(静态)vole::create() 方法允许用户通过 CLSID({000209FF-0000-0000-C000-000000000046})、程序化 ID("Word.Application")或 CLSID 的字符串形式("{000209FF-0000-0000-C000-000000000046}")来指定要创建的类,我们可以直接传递程序化 ID。

步骤 2. 启动 Word 并获取应用程序对象的 IDispatch 指针

要启动 Word 自动化服务器,我们将 "Word.Application" 传递给 vole::object::create() 方法。此方法返回一个 vole::object 实例,该实例拥有服务器关联的底层 COM 接口。

  object word = object::create("Word.Application", CLSCTX_LOCAL_SERVER);

请注意,就像在 Microsoft 示例 中一样,我们必须指定 CLSCTX_LOCAL_SERVER 以将 Word 作为本地服务器调用。

步骤 3. 使 Word 可见

为了使 Word 应用程序对象可见,我们将它的 Visible 属性设置为 true。这是通过使用 vole::object::put_property() 方法完成的,如下所示:

word.put_property(L"Visible", true);

步骤 4. 获取 Documents 集合

为此,我们需要通过 vole::object::get_property() 方法,获取应用程序对象的 Documents 属性,并将其放入 vole::collection 的实例中,如下所示:

collection documents = word.get_property<collection>(L"Documents");

请注意 vole::object::get_property() 成员函数模板的显式特化。所有现代编译器都能处理这种适度的复杂性,但有些(尤其是 VC++ 6)则不能。我将在本文 末尾 为每个步骤展示替代代码(如果需要)。

步骤 5. 调用 Documents.Open() 打开“C:\Doc1.doc”

要调用自动化服务器的方法,我们使用 vole::object::invoke_method()

object document = documents.invoke_method<object>(L"Open", L"C:\\Doc1.doc");

这将返回一个 vole::object 实例,其中包含相应的文档对象。

注意:就像在 Microsoft 示例 中一样,您需要确保文件 C:\Doc1.doc 存在,否则将抛出异常。

步骤 6. 获取 BuiltinDocumentProperties 集合

我们通过 步骤 4 中所示的相同方式,获取文档的 BuiltinDocumentProperties 属性并将其放入一个集合中。

collection builtInProps = document.get_property<collection>(
   L"BuiltinDocumentProperties");

步骤 7. 从 BuiltInDocumentProperties 集合中获取“Subject”属性

这里也是一样:

object propSubject = builtInProps.get_property<object>(L"Item", L"Subject");

步骤 8. 获取“Subject”属性的值

这次我们获取的是字符串类型的属性值,而不是对象。为此,我们将其作为 std::string 实例请求。

std::string subject = propSubject.get_property<std::string>(L"Value");

请注意,VOLE 同样支持将字符串值作为 std::wstring 实例来获取,因此我们也可以这样写:

  std::wstring subject = propSubject.get_property<std::wstring>(L"Value");

步骤 9. 设置“Subject”DocumentProperty 的 Value

这是通过 vole::object::put_property() 方法实现的:

  propSubject.put_property(L"Value", "This is my subject");

请注意,VOLE 会自动处理方法/属性参数之间的 ANSI/多字节和宽/Unicode 字符串之间的转换。因此,我们也可以这样写:

propSubject.put_property(L"Value", L"This is my subject");

它们之间唯一的区别是,前者效率略低,因为字符串 "This is my subject" 需要转换为宽格式。

注意:这种灵活性不适用于方法/属性本身的名称——这些名称必须指定为 DISPID(也称为 long)或宽字符串。

步骤 10. 获取 CustomDocumentProperties 集合

这与 步骤 6 基本相同。

collection customProps = document.get_property<collection>(
    L"CustomDocumentProperties");

步骤 11. 添加一个名为“CurrentYear”的新属性

我们使用 vole::object::invoke_method 调用新属性的 Add() 方法,如下所示:

customProps.invoke_method<void>(L"Add", L"CurrentYear", false, 1, 1999);

因为我们不返回任何内容,所以我们特化为 void

步骤 12. 获取自定义属性“CurrentYear”并删除它

要删除我们刚刚创建的属性,我们需要先检索它,然后指示检索到的属性对象自行删除,如下所示:

object propCurrYear = customProps.get_property<object>(
    L"Item", L"CurrentYear");
propCurrYear.invoke_method<void>(L"Delete");

或者,我们可以在一个语句中完成此操作,如下所示:

customProps.get_property<object>(
    L"Item", L"CurrentYear").invoke_method<void>(L"Delete");

但这不太清晰。显然,VOLE 虽然与直接 C++ 操作 COM 自动化服务器相比非常直观,但仍然具有一定的冗余性。因此,我倾向于尝试将每个 COM 属性/方法调用保持在一个 C++ 语句中。

步骤 13. 关闭文档而不保存更改

由于我们修改了文档对象,尝试退出 Word 将会失败,因为它会询问我们是否要保存更改。因此,我们首先调用文档方法 Close,传递 false 以指示我们希望放弃更改。

document.invoke_method<void>(L"Close", false);

步骤 14. 退出 Word

最后,我们指示 Word 退出,这会导致 Word 应用程序关闭。

word.invoke_method<void>(L"Quit");

就这样。与 Microsoft 示例 中大约 220 行的代码相比,它在表达能力上取得了巨大的进步。再加上在处理不同基本类型和字符编码方面的灵活性、所有服务器资源都以异常安全的方式处理的健壮性,以及它 100% 是纯头文件且与编译器无关的事实,很明显 VOLE 代表了从 C++ 驱动 COM 自动化服务器的最佳解决方案。但我也这么说,不是吗?所以,我邀请您亲自尝试一下。

与旧编译器的兼容性

正如 本系列的第一篇文章中所述,一些旧编译器(例如 VC++ 6)在处理 VOLE 的模板语法时存在问题,因此也支持另一种形式。替代形式与以下步骤相关:

// Step 4
collection documents = word.get_property(of_type<collection>(), L"Documents");

// Step 5
object document = documents.invoke_method(
    of_type<object>(), L"Open", L"C:\\Doc1.doc");

// Step 6
collection builtInProps = document.get_property(
    of_type<collection>(), L"BuiltinDocumentProperties");

// Step 7
object propSubject = builtInProps.get_property(of_type<object>(), 
    L"Item", L"Subject");

// Step 8
std::string subject = propSubject.get_property(of_type<std::string>(), 
    L"Value");

// Step 10
collection customProps = document.get_property(of_type<collection>(), 
    L"CustomDocumentProperties");

// Step 11
customProps.invoke_method_v(L"Add", L"CurrentYear", false, 1, 1999);

// Step 12
object propCurrYear = customProps.get_property(of_type<object>(), 
    L"Item", L"CurrentYear");
propCurrYear.invoke_method_v(L"Delete");

// Step 13
document.invoke_method_v(L"Close", false);

// Step 14
word.invoke_method_v(L"Quit");

更多内容即将推出...

我计划很快写另一篇关于 VOLE 的文章,介绍 vole::collection 类通过 IEnumXXXX 协议为 COM 集合的元素提供 STL 迭代器的能力。

欢迎通过 VOLE 项目主页 提出您对 VOLE 的意见/批评/功能请求。

欢迎通过 STLSoft 新闻组(由 Digital Mars 提供,他们提供免费高质量的 C/C++/D 编译器) 在此处 提出您对 STLSoft 的意见/批评/功能请求。

历史

2007年8月5日:初版

使用 VOLE 驱动 Microsoft Word - CodeProject - 代码之家
© . All rights reserved.