使用 VOLE 驱动 Microsoft Word






4.95/5 (12投票s)
2007年8月10日
6分钟阅读

68396

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 示例执行以下操作:
- 查找“Word.Application”的 CLSID
- 启动 Word 并获取应用程序对象的
IDispatch
指针 - 使 Word 可见
- 获取
Documents
集合 - 调用
Documents.Open()
打开“C:\Doc1.doc” - 获取
BuiltinDocumentProperties
集合 - 从
BuiltInDocumentProperties
集合中获取“Subject
”属性 - 获取“
Subject
”属性的Value
- 设置“
Subject
”DocumentProperty
的Value
- 获取
CustomDocumentProperties
集合 - 添加一个名为“
CurrentYear
”的新属性 - 获取自定义属性“
CurrentYear
”并删除它 - 关闭文档而不保存更改
- 退出 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);
步骤 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日:初版