摘要
本文解答了有关从 Visual C++ 自动化到 Microsoft Office 的常见问题。
更多信息
目录
-
什么是自动化?
-
我不熟悉自动化,在哪里可以找到要了解详细信息的好资源?
-
是否有其他方法可以使用自动化?
-
什么是 COM?
-
如何实现附加到正在运行的 Office 应用程序实例?
-
如何实现传递可选参数?
-
如何实现捕获 Office 应用程序公开的事件?
-
我的自动化代码太慢了。 如何加快速度?
-
这些巨大的错误值(如 -2147352573 或 0x80030002)是什么意思?
-
什么是类型库?
-
我的自动化代码适用于 Microsoft Excel 95,但 Microsoft Excel 97 失败。 为什么?
-
为什么在程序完成后,我自动执行的应用程序会保留在内存中?
-
我知道作为 Microsoft Office 应用程序用户我想做什么,但如何使用自动化以编程方式执行此操作?
-
是否可以自动执行嵌入式 Microsoft Office 应用程序?
-
如何实现 Microsoft Office 文档中访问我的文档属性?
问题和解答
-
什么是自动化?
自动化 (以前为 OLE 自动化) 是一种技术,可用于利用现有程序的功能并将其合并到自己的应用程序中。 例如,可以在应用程序中利用 Microsoft Word 拼写和语法检查功能,而用户看不到 Microsoft Word。 甚至可以使用所有 Microsoft Excel 图表、打印和数据分析工具。 这项技术可以大大简化和加快开发速度。
-
我不熟悉自动化,在哪里可以找到要了解详细信息的好资源? David Kruglinski 的“Visual C++内部” (ISBN:1-57231-565-2) 提供了一般概述和一些很好的示例。 此外,Microsoft 知识库是一个很好的信息来源。
如果你更喜欢通过示例学习,请参阅 Microsoft 知识库中的以下文章:
179706 如何使用 MFC 自动执行 Excel &创建新工作簿/设置其格式
-
是否有其他方法可以使用自动化?
可以使用自动化的三种基本方法:MFC、#import和 C/C++:
-
使用 MFC 时,使用 Visual C++ ClassWizard 从 Microsoft Office 类型库生成“包装类”。 这些类以及其他 MFC 类(如 COleVariant、COleSafeArray、COleException)简化了自动化的任务。 通常建议使用此方法,而其他方法,并且大多数 Microsoft 知识库示例都使用 MFC。
-
#import是 Visual C++ 5.0 中提供的一个新指令,它从指定的类型库创建 VC++“智能指针”。 它非常强大,但通常不建议使用,因为与 Microsoft Office 应用程序一起使用时通常会发生引用计数问题。
-
C/C++ 自动化要困难得多,但有时需要避免 MFC 开销或#import问题。 基本上,可以使用 CoCreateInstance () 等 API 以及 IDispatch 和 IUnknown 等 COM 接口。
请务必注意,与纯 C 相比,C++ 中的自动化之间存在一些细微差别,因为 COM 是围绕 C++ 类设计的。
-
-
什么是 COM?
自动化基于组件对象模型 (COM) 。 COM 是基于接口的标准软件体系结构,旨在将代码分隔成独立的对象。 将其视为面向对象的编程 (OOP) 范例的扩展,但适用于单独的应用程序。 每个对象公开一组接口,与对象的所有通信(如初始化、通知和数据传输)都通过这些接口进行。
COM 也是动态链接库提供的一组服务, (DLL) 随操作系统一起安装。 自动化使用其中许多服务。 一个示例是“封送”服务,该服务打包客户端应用程序对服务器应用程序接口成员函数的调用,并将这些调用及其参数传递给服务器应用程序。 它使服务器的接口似乎在客户端的内存空间中公开,当客户端是在其自己的进程空间中运行的.exe时,情况并非如此。 封送处理还会跨进程边界从服务器的方法获取返回值,并安全地交到客户端调用手中。 还有许多其他对自动化至关重要的服务,这些服务由各种 COM 库提供。 有关这些信息的来源包括 Kraig Brockschmidt 的“Inside Ole - 第二版”、ISBN 1-55615-843-2、戴尔·罗杰森的“Inside COM”-ISBN 1-57231-349-8 和“自动化程序员参考”,ISBN 1-57231-584-9。 -
如何实现附加到 Office 应用程序的正在运行的实例?
使用 GetActiveObject () API。 自动化服务器通过 RegisterActiveObject () API 在 ROT (Running Object Table) 中注册自己。 自动化客户端可以使用以下代码访问正在运行的实例:
// Translate server ProgID into a CLSID. ClsidFromProgID // gets this information from the registry. CLSID clsid; CLSIDFromProgID(L"Excel.Application", &clsid); // Get an interface to the running instance, if any.. IUnknown *pUnk; HRESULT hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk); ASSERT(!FAILED(hr)); // Get IDispatch interface for Automation... IDispatch *pDisp; hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDisp); ASSERT(!FAILED(hr)); // Release the no-longer-needed IUnknown... pUnk->Release();
注意:如果要附加的 Office 应用程序运行多个实例,则只能附加到使用 GetActiveObject () API 启动的第一个实例。
从理论上讲,可以循环访问每个单独实例的 ROT,但如果另一个实例已在 ROT 中,Office 应用不会自行注册,因为本身的名字对象始终是相同的 (它无论如何都无法区分) 。 这意味着不能附加到除第一个实例之外的任何实例。 但是,由于 Office 应用也在 ROT 中注册其文档,因此可以通过循环访问 ROT 查找特定文档、附加该文档,然后从中获取 Application 对象来成功附加到其他实例。
无需为 PowerPoint 执行此操作,因为它是单实例应用程序;只能运行它的一个实例。 -
如何实现传递可选参数?
某些方法具有“可选”参数。 在 Visual Basic 中,可以在调用 方法时随意省略它们。 但是,在使用 Visual C++ 调用时,必须传递一个特殊 VARIANT,该变量的 .vt 字段VT_ERROR,而 .scode 字段DISP_E_PARAMNOTFOUND。 那是:
// VARIANT used in place of optional-parameters. VARIANT varOpt; varOpt.vt = VT_ERROR; varOpt.scode = DISP_E_PARAMNOTFOUND;
这确实是 Visual Basic 在幕后执行的操作。
-
如何实现捕获 Office 应用程序公开的事件?
基本上,实现要捕获 (“接收器”) 的事件接口,并 (“源”) 与应用程序建立咨询连接。
通常,若要设置咨询连接,请获取服务器的 IConnectionPointContainer,并使用事件接口的 IID 调用 FindConnectionPoint () 。 这为你提供了一个 IConnectionPoint 接口,剩下的就是使用事件接口的实例调用 Advise () 。 然后,当发生这些事件时,服务器将通过此接口回调。 -
我的自动化代码太慢了。 如何加快速度?
自动化速度问题的一个常见原因是重复读取和写入数据。 这是 Excel 自动化客户端的典型应用。 但是,大多数人不知道此数据通常可以使用 SAFEARRAY 一次性写入或读取。 有关详细信息和信息性示例,请参阅以下 Microsoft 知识库文章:179706 HOWTO:使用 MFC 自动执行 Excel 和创建新工作簿/设置新工作簿
的格式 此外,请务必指出,使用剪贴板有时可以提高性能。 例如,法律时刻可以将数据复制到剪贴板,然后使用自动化来告知服务器进行粘贴。 反之亦然;告知服务器复制到剪贴板,并粘贴到应用程序中。 -
这些巨大的错误值(如 -2147352573 或 0x80030002)是什么意思?
这些值称为 HRESULT,在 winerror.h 中定义。 数字如此之大,因为第一个位表示它是否是错误结果。 可以使用 Visual C++ 附带的 ErrLook.Exe 实用工具将这些数字转换为有意义的说明。
如果要以编程方式获取错误说明,可以使用 FormatMessage () API。注意:如果使用 Visual C++ 6.0 并在调试监视窗口中有一个包含此值的变量,请追加“, hr” (不带引号) ,以便 Visual C++ 为你翻译它!
-
什么是类型库?
类型库类似于 C/C++ 头文件。 它包含服务器正在发布的接口、方法和属性。 可以使用 Visual C++ 附带的 OLE/COM 对象查看器 (Oleview.exe) 查看类型库。 下面是 Microsoft Office 95、97 和 2000 的类型库文件名列表:
Office 应用程序|类型库
------------------------+----------------
Word 95 和以前的|wb70en32.tlb
Excel 95 及更早版本的|xl5en32.olb
Powerpoint 95 和以前的|Powerpoint.tlb
Access 95 及更早|msaccess.tlb
Binder 95 |binder.tlb
Schedule+ |sp7en32.olb
项目|pj4en32.olb
团队经理|mstmgr1.olb
Word 97 |msword8.olb
Excel 97 |excel8.olb
Powerpoint 97 |msppt8.olb
Access 97 |msacc8.olb
Binder 97 |msbdr8.olb
图 97 |graph8.olb
Outlook 97 |msoutl8.olb
Outlook 98 |msoutl85.olb
Word 2000 |msword9.olb
Excel 2000 |excel9.olb
Powerpoint 2000 |msppt9.olb
Access 2000 |msacc9.olb
Outlook 2000 |msoutl9.olb
Word 2002 |msword.olb
Excel 2002 |
excel.exe Powerpoint 2002 |msppt.olb
Access 2002 |msacc.olb
Outlook 2002 |msoutl.olb
-
我的自动化代码适用于 Excel 95,但 Excel 97 失败。 发生了什么事情?
Excel 的对象模型进行了从版本 95 到 97 的重大更改。 Excel 95 在 IDispatch 的单个实现中实现了其所有方法和属性。 这意味着,通常可以从对象 Y 调用用于对象 X 的方法。这不是一个很好的设计,因此在 Office 97 中,每个对象都有其自己的独立 Idispatch 实现。 这意味着,如果从单独的对象 Y 从对象 X 请求方法或属性,则会收到错误0x80020003 -2147352573,“找不到成员”。 若要避免此错误,需要确保调用的基础 IDispatch 接口在语义上正确。
-
程序完成后,我自动执行的应用程序将保留在内存中。 发生了什么事情?
很可能是因为你忘记了发布获取的接口,需要跟踪它。 下面是一些常规建议和要查找的事项:
-
如果使用 #import,则很可能遇到与之关联的引用计数 bug 之一。 通常,可以解决 bug,但通常首选使用其他自动化方法之一。 #import不太适用于 Office 应用程序,因为它的类型库和用法相当复杂。 此外,此类引用计数问题很难跟踪,因为使用 #import 时,许多接口级 COM 调用都是幕后调用。
-
检查是否调用任何方法(如 Open 或 New),这些方法返回 IDispatch * (LPDISPATCH) ,并忽略返回值。 如果是,则放弃此返回的接口,并且需要更改代码,以便在不再需要时将其释放。
-
逐步注释掉代码的各部分,直到问题消失,然后明智地将其添加,以跟踪问题开始的位置。
-
请注意,如果用户已“触摸”应用程序,某些应用程序将保持运行。 如果在自动化时发生这种情况,则应用程序可能会在之后继续运行。 Office 应用程序在 Application 对象上具有“UserControl”属性,可读写该属性来更改此行为。
-
此外,如果发生了足够的用户界面“操作”,某些应用程序将决定继续运行。 如果打算退出应用程序,请在 Application 对象上调用其 Quit () 方法。 调用 Quit 时,无论 Word 的引用计数如何,Word 都将关闭。 这不是预期的 COM 行为。 但是,Excel 将正确隐藏自身,但会一直运行,直到释放所有未完成的接口。 通常,应释放所有未完成的引用,并且仅当打算退出应用程序时调用 Quit () 。
-
-
我知道作为 Office 应用程序用户我想做什么,但如何通过自动化以编程方式执行此操作?
你感兴趣的是需要使用哪些对象、方法和属性。 若要了解如何根据用户需要执行的操作导航 Word、Excel 和 Powerpoint 的对象模型,最佳方法是使用宏录制器。 只需从“工具”菜单中选择“宏”“录制新宏”,执行感兴趣的任务,然后选择“宏”“停止录制”。 录制完成后,从“工具”菜单中选择“宏\宏”,选择录制的宏,然后单击“编辑”。 这会将你带到生成的 VBA 代码,该代码将完成你记录的任务。 请记住,在大多数情况下,录制的宏不是最好的代码,但对于快速示例来说,它非常有用。 -
是否可以自动执行嵌入式 Office 应用程序?
绝对。 技巧在于获取 IDispatch 指针:Visual C++ 技术说明 39 (TN039) 中提供了此指针。
-
如何实现 Office 文档中访问我的文档属性?
文档属性可通过自动化或直接通过 IPropertyStorage 进行访问。