为了能起到作用,你的智能文档操作DLL一般需要访问(并且可能修改)下层的Office文档。在例子中,它是Excel项目进程表电子表格。为了这个目的,ISmartDocument的几个方法为你下层文档提供了一个IDispatch COM接口指针。在Office上编写COM的人知道,Idispatch提供了进入Office对象模型的通道。在Visual Basic中,使用Idispatch和类型库是相当自动化的,但是在C++中稍微复杂一些。
起先我准备使用#import指令,它允许Visual C++为类型库中的所有接口生成ATL智能指针包装。但是要让它正确的编译需要做大量的工作,它常常提示有东西出错了。果真,我找到了知识库文章“Office Application Remains in Memory After Program Finishes”,它描述了在Office类型库中使用#import指令所遇到的知名的问题以及相应的建议。
最后,我决定建立自己的类CexcelWorkbook来包装需要的Excel方法。这个类继承自ATL模版CcomDispatchDriver,这使它相对容易通过IDispatch调用Excel对象模型上的方法。使用CcomDispatchDriver的方法GetIDOfName,你可以得到一个给定的属性或方法(例如,Excel对象模型中的ActiveSheet的范围)的DISPID。你一旦有了DISPID(为了效率更高,我在类中对它进行了缓冲处理),就可以使用某个其它的CcomDispatch方法(例如GetProperty或InvokeN,此处的N是参数的数量)访问对象模型中的属性和方法。注意Exce Range值是作为VBA属性(而不是方法)暴露的,但是它也需要一个参数(范围地址)。因为CcomDispatchDriver没有提供为GetProperty调用传递的参数的途径,我在自定义类(CExcelWorkbook)中实现了一个新的方法(GetProperty1),它用于处理这种情况。
调试智能文档DLL
一切都在预料之中,在我尝试建立智能文档操作DLL的时候也没有出现异常。我试图在Excel中附加一个XML大纲的时候,收到了一个错误信息“XML扩展包逻辑丢失或无效”。为了调试这个错误,我首先使用ListDLLs(http://www.sysinternals.com上的一个方便的工具)确定Excel是否载入了我的库,这样清单才能看起来足够好,它可以让Excel找到该DLL的。我退出Excel,接着修改了项目的Debugging属性(右键点击解决方案并选择“属性”)告诉Visual Studio使用Excel作为该DLL的EXE容器。我浏览Excel.exe并选择它作为Command值。
接着,我按下Ctrl-B打开“New Breakpoints”对话框,在Function字段中输入DllMain,当出现“disambiguate symbol”窗口(显然有两个重载的DllMain函数)的时候选择了它们两个,通过这些操作在DllMain中设置了一个断点。我的目的是当Excel第一次调用该DLL的时候得到控制权。接着我按下F5,Excel启动了。Visual Studio警告没有Excel符号,但是我早就知道了。我打开智能文档,使用Data | XML | XML Expansion Packs试图再次附加XML大纲。Visual Studio同ATL生成的DllMain中的断点一起出现了。
这个时候我的目标是确定DLL中是否有方法、哪些方法被调用了。我在自己的IsmartDocument接口实现中的所有方法上设置了断点,以确定它们其中的哪些被调用了。实际上,有几个方法被调用了,并且通过逐步运行我找到了一个普通的索引问题,传递到get_SmartDocXmlTypeName的控件索引是从1开始的,但是C++代码把它处理为从0开始的,因此对最后一个元素的调用返回了E_INVALIDARG。后来我给所有的接口方法的入口点添加了ATLTRACE2宏,使自己更容易知道正在调用什么、什么时候调用。
查看本文来源