访问智能文档内容
为了能起到作用,你的智能文档操作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宏,使自己更轻易知道正在调用什么、什么时候调用。
改变通知
对于示例解决方案,我需要知道用户什么时候选择了电子表格的数据项区域中的某行,这意味着他希望编辑该事务的相关信息。接着我从当前行中抓取信息并填充事务面板,答应他输入本周的工作和下一周的计划工作的相关信息。当他改变了事务面板中的某些东西的时候,我将使用Excel对象模型把新的信息复制回原工作表行。但是我如何知道什么时候选择了新的行?有一种比较复杂的解决方案,即使用Excel事件(在Visual Basic中轻易,但是在C++中不是太轻易),但是我发现这是没有必要的。在Excel中无论选择什么时候发生了改变,都会调用IsmartDocument接口方法,因此无论什么事情,你仅仅需要改变通知。通过更新每次改变后的进度表事务内部视图,我能够忽略行选择的变化。
安全性
因为宏病毒和其它的脚本技术的恶意使用变得很普遍,Office在两个途径做了修改:通过提高默认的安全性设置,防止运行大多数没有签名的、潜在的恶意代码;通过增加安全性设置的数量,为即使没有数字签名的解决方案的运行提供了更多的治理权限。这考虑了现实情况:很多Office解决方案(嵌入Word或Excel文档中的宏)都已经广泛的布署在大型组织中,要让它们完全安全将花费一定时间。假如新版本Office忽然要求所有的解决方案必须有数组签名(理想的解决方案),这将给很多组织带来布署方面的障碍。但是,Office安全性设置的增加也使安全性更加复杂,在本文中我没有谈到这个主题。
应用于宏和插件程序的相同的Office安全性设置也可应用于智能文档。这些设置包括:
·用户宏的安全性设置(高、中或低,在Tools Macro Security中设置;默认的是高)。
·智能文档是否从可信(trusted)位置载入:可信的文件系统目录(例如每个用户的或工作组模版目录)、公司局域网上的Web服务器或可信的Internet站点。
·智能文档组件是否是数字签名的,假如是,发行人是否在Tools Macro Security Trusted Publishers设置为可信发行人。
·是否在Tools Macro Security Trusted Publishers中选择了“相信所有安装了的插件程序和模版”。
默认情况下,Tools Macro Security中的安全性层次被设置高,Office阻止任何没有数字签名的插件程序DLL(包含智能文档操作DLL)的载入。上面列举的安全性设置的其它组合可以答应智能文档被载入(可能给用户显示一个安全性提示),但是保证组织中的安全性的最好途径仍然是使用从VeriSign或GTE CyberTrust得到的数字证书对所有的已布署的解决方案(包括智能文档组件)进行数字签名。微软2003 智能文档SDK中的XMLSign.exe工具可以用于对XML智能文档清单文件进行数字签名。
此外,假如某个Office智能文档解决方案是从Web服务器上运行的,微软Internet Explorer和Office安全性设置都会影响解决方案是否能运行。假如服务器上的XML扩展包清单文件既不在Internet Explorer可信站点中,也不在局域网区域,就不会试图检索它,也不会给用户提示把该站点添加到Internet Explorer可信列表站点列表中。假如XML扩展包清单文件位于可信的服务器或局域网上,清单是否被载入依靠于它是否签名了。假如它是签名了的,并答应运行,它仍然受到用户Office安全性设置的约束。
在智能文档解决方案开发过程中,你可以通过编辑注册表的下述键下面的REG_DWORD值“DisableManifestSecurityCheck”激活或禁止XML扩展包清单文件安全性检查:
HKEY_LOCAL_MACHINE\Software\Microsoft\Office\Common\Smart Tag
假如它的值为1将禁止XML扩展包清单文件安全性检查,假如值为0就激活了它。当你试图引用某个XML扩展包清单文件的时候,假如这个注册表设置是1(禁止安全性检查),将出现一个对话框警告用户禁止安全性是危险的。它同时提供一个“确定”按钮,这个按钮答应你立即重新激活XML扩展包安全性检查。你应该仅仅在开发解决方案的时候把开发计算机的注册表的这个值设置为1(这个时候每次编译每个组件后进行数字签名可能不方便),但是要确保在签名后的组件的最后测试中激活它。在用户计算机上禁止XML扩展包清单文件安全性检查是非常不可取的。
尽管本文讨论了在C++中智能文档操作DLL的建立,但是用Visual Basic .NET或C#建立受控操作DLL也是可行的。在那种情况下,将应用.NET框架组件安全性模型,但是这超出了本文讨论的范围。
生成状态报告
我的示例依据Excel对象模型使用Idispatch访问来自进度表智能文档工作表的信息以生成状态报告。我也可以选择开发一些C++代码来驱动Word对象模型建立状态文档,但是我选择了另一种途径,它也利用了Word 2003中的新XML特性的优点。我把状态报告生成为XML文件,并使用XSLT在Word中打开它以提供良好的格式化。这种方法利用了使用XSLT很轻易把几个XML文件(可能是整个小组的状态报告)合并成一个大的报告的优点,假如它们作为Word文档保存就很难合并了。
在花费一段时间研究如何实现XML保存后,我决定定义一个类来治理将显示在状态报告上的数据项列表。当开发者更新进度表中的信息的时候,智能文档操作DLL中的代码就会建立并更新这个事务列表,这与文档操作工作面板中放置的控件对应。最后,开发者点击工作面板中的控件生成XML状态报告。每个类负责使用MSXML DOM正确地保持存储在XML中的信息。处理这种情行的处理方法是CScheduleTaskList和CScheduleTask。我用于格式化它的Word XSL样式表是ScheduleReport.xsl,它在示例代码中提供了。当你安装智能文档解决方案的时候,这个XLST文件就会被安装好,并让Word知道它,这样当你在Word中打开XML状态报告的时候,它将自动应用这个样式表。你也可以使用Word中的XML数据视图事务面板来选择这个样式表。
在我生成的XML中有一个指令值得提起: