浅谈Excel开发:九 Excel 开发中遇到的常见问题及解决方法
Excel开发过程中有时候会遇到各种奇怪的问题,下面就列出一些本人在开发中遇到的一些比较典型的问题,并给出了解决方法,希望对大家有所帮助。
一 插件调试不了以及错误导致崩溃的问题
在开发机器上,有时可能会装有多个版本的.NET运行时,有时候也可能装有多个版本的Visual Studio,本人的开发机器上就安装了3个版本的Visual Studio,在最新的Visual Studio 2013出来之后,就迫不及待的把工程文件迁移到2013上来了,但是在设置了启动程序之后,调试的时候,断点根本不能命中,也调试不进去。即使使用Attached to process方式也不行。
该问题是由于在安装有多个.NET版本的机器上,由于某些原因,会导致Excel不知道为Com Add-in加载哪个版本的Framework导致的。注:这里仅针对是用.NET技术开发Shared Add-in插件的情况,由于VSTO 中的安装部署文件.vsto中注明了程序集的版本,所以不存在该问题。
解决方法是新建一个名为EXCEL.EXE.config的配置文件,放到和EXCEL.EXE同级的目录下,配置如下,在startup节点下新建supprotedRuntime节点,指定运行时的版本号,即可解决该问题:
在Shared Add-in插件开发的初期,在测试机器上测试的时候,有时会由于代码错误,导致Excel直接崩溃掉的问题。在开发机器上,我们只需要将在该配置文件中,将runtime节点下的legacyUnhandledExceptionPolicy的Enable属性设置为true,这样,Excel在出现问题的时候就会弹出提示框,有助于帮助查找错误出现的原因。
这其实也是相较于VSTO,Shared Add-in应用程序的缺点之一,VSTO的一个插件是加载到各自的App Domain中的,出现问题不会影响其他的插件。
二 若干Excel方法调用会导致出错
由于需要兼容不同版本的Excel,但是随着版本的变化,Excel暴漏出来的API方法的参数也会发生变化。有时候采用直接调用的方式在遇到版本不同时会抛出异常,这种COM类型的异常一般很难处理和恢复。在有些时候,通过采用“晚绑定”的方式,采用反射调用方法就可以解决这一问题。
以条件着色功能为例。在使用插件获取并输出数据的时候,通常需要根据条件对单元格进行着色,比如,输出股票的涨跌和涨跌幅的时候,人们一般习惯将涨的标注为红色,跌的标注为绿色。Excel中对Range进行单元格条件可以使用FormatCoondition对象,该对象有如下方法,以添加涨跌幅的条件着色为例,代码如下。
object missing = Type.Missing; FormatCondition conditionFall = (FormatCondition)tmpRange.FormatConditions.Add(XlFormatConditionType.xlCellValue, XlFormatConditionOperator.xlLess, "=0", missing); conditionFall.Font.ColorIndex = 10;//GreenFormatCondition conditionRise = (FormatCondition)tmpRange.FormatConditions.Add(XlFormatConditionType.xlCellValue, XlFormatConditionOperator.xlGreater, "=0", missing); conditionRise.Font.ColorIndex = 3;//Green
该方法在Excel07及以上版本中没有问题,但是在03下就会抛出异常。由于个版本的API中,参数个数不一样。所以采用反射的方法,将参数以数组的方式传入即可解决该问题。
下面这个方法封装了FoormationCoondition的Add方法。
/// <summary> ///兼容Excel 2003-Excel2010的代码条件着色,如果直接使用FormatConditions.Add方法,则不能兼容 ///详情可以参考:http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/8a91d154-f766-427a-963c-16dfa39e154a ///使用方法如: tempRange.AddConditionValue(XlFormatConditionOperator.xlLess, "=0"); /// </summary> /// <param name="R">待进行条件着色的Range区域</param> /// <param name="ConditionOperator">找色条件,如,大于,小于</param> /// <param name="Formula">具体的值</param> /// <returns></returns> public static FormatCondition AddConditionValue(Range R, XlFormatConditionOperator ConditionOperator, string Formula) {return (FormatCondition)R.FormatConditions.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, (object)R.FormatConditions, new object[] { XlFormatConditionType.xlCellValue, ConditionOperator, Formula }); }
使用该方法,上面的代码改写为:
FormatCondition conditionFall = AddConditionValue(tempRange, XlFormatConditionOperator.xlLess, "=0"); conditionFall.Font.ColorIndex = 10;//GreenFormatCondition conditionRise = AddConditionValue(tempRange, XlFormatConditionOperator.xlGreater, "=0"); conditionRise.Font.ColorIndex = 3;//Red
Excel中遇到同一方法由于不同API版本参数不同导致出现问题,都可以采取上面的采用反射方法调用来解决问题。
三 Excel自定义函数中包含.xla或者.xll文件路径的问题
一般地通常的插件都会开发相应的自定义函数 (UDF),除了使用C#类库编写自定义函数,大部分都是将自定义函数写在Excel的.xla或者xll文件中,有时候也会使用自定义函数编写一些标准的模板供用户调用。当这些模板中包含有自定义函数,当用户将该模板保存到其他位置之后,再次打开,这些单元格中的函数就会包含.xla或者.xll的路径。比如"C:\Program Files\Installation folder\MyUDFs.xla!MyUDF"等,原因是Excel在内部会包含自定义函数文件的完整路径。
这会导致一些问题,比如说,改自定义函数不能够正确的执行,不能求值等等。
就解决方法就是在vba中,在SheetOpen事件中,将所有包含函数的路径进行重新定向到系统指标的自定义函数类库的目录下即可。VBA代码如下,主要的方法为_Workbook.ChangeLink 方法,VBA的代码如下。将该部分代码写到vba类库中即可。
Private Sub ExcelApp_WorkbookOpen(ByVal Wb As Workbook)ReLink Wb End SubPublic Sub ReLink(ByVal oBook As Workbook)If IsEmpty(oBook.LinkSources(xlLinkTypeExcelLinks)) Then Exit SubDim lk As VariantFor Each lk In oBook.LinkSources(xlLinkTypeExcelLinks)If lk Like "*" & ThisWorkbook.Name And lk <> ThisWorkbook.FullName ThenoBook.ChangeLink lk, ThisWorkbook.FullName, xlLinkTypeExcelLinksEnd IfNext lk End Sub
四 自定义函数加载的问题
还是UDF加载的问题,如果是采用.xla或者xll承载自定义函数的话,在插件启动的时候需要加载改文件到Excel中,这样Excel才能够使用这里面的自定义函数,并在Excel 2007及以上版本中会给出函数的智能提示。
一般地,在Shared Add-ins中加载自定义函数我们可以在OnConnection方法中,调用如下方法:
MSExcel.AddIn excelFunc = null; try {excelFunc = applicationObject.AddIns.Add(“your .xla file path“, true); } catch (System.IO.IOException exception) {LogHelper.Error("IOException:未发现xla文件在所设置路径", exception); } catch (Exception exception) {LogHelper.Error(exception); }
在大多数情况下,如果用户直接打开Excel应用程序,插件加载就会执行OnConnect方法,就会执行加载.xla函数的逻辑。
但是,更多的情况是,用户会直接双击一个之前保存过的,包含有我们的.xla文件中的自定义函数的excel文件,通过这种方式打开excel,插件的加载.xla在某些情况下回出现加载不了的情况。出现该问题的原因在于,我们再加载.xla文件的时候,必须要确保当前Excel中有一个空白的Sheet页处于打开状态。在用户通过双击现有的excel文件的时候,在文档打开之前,并没有可用的Sheet页处于打开状态,这是我们的插件加载.xla文件的时候就会出现加载不上的问题。
解决方法就是在OnConnect中调用RegisterOpenEvent方法注册WorkbookActivate事件,在该事件回调方法中去注册.xla文件。这样在文件被打开的时候就可以加载.xla或者.xll函数了,在加载完函数之后,我们需要注销该事件以防止重复加载。如下
private void RegisterOpenEvent() {applicationObject.WorkbookActivate += new MSExcel.AppEvents_WorkbookActivateEventHandler(applicationObject_WorkbookActivate); }/// <summary> /// This active evevt was designed to be trigged only once to load xla file /// </summary> /// <param name="Wb"></param> void applicationObject_WorkbookActivate(MSExcel.Workbook Wb) {MSExcel.AddIn excelFunc = null;try{excelFunc = applicationObject.AddIns.Add(RuntimeHelper.MapRootPath(EmOfficeCore.Helper.PathHelper.Instance.EMFuncPath), true);}catch (System.IO.IOException exception){LogHelper.Error("IOException:未发现xla文件在所设置路径", exception);}catch (Exception exception){LogHelper.Error(exception);}if (excelFunc.Installed == true){applicationObject.WorkbookActivate -= new MSExcel.AppEvents_WorkbookActivateEventHandler(applicationObject_WorkbookActivate);}else{applicationObject.WorkbookActivate -= new MSExcel.AppEvents_WorkbookActivateEventHandler(applicationObject_WorkbookActivate);excelFunc.Installed = true;} }
这样就能解决加载不上的问题。
五 资源释放不掉的问题
在使用Shared Add-ins开发插件的时候,通常如果资源释放处理不好,会存在关闭Excel后,任务管理器中的Excel.exe这个进程没有关闭。这是由于我们在.NET中引用了非托管代码 ,但是有没有手动释放,导致资源没有被回收导致的。该问题在StackOverflow上有很详细的讨论,按照msdn上的解决方法。我们只需要在OnDisConnection 方法中对资源进行释放即可:
/// <summary> /// Implements the OnDisconnection method of the IDTExtensibility2 interface. /// Receives notification that the Add-in is being unloaded. /// </summary> /// <param term='disconnectMode'> /// Describes how the Add-in is being unloaded. /// </param> /// <param term='custom'> /// Array of parameters that are host application specific. /// </param> /// <seealso class='IDTExtensibility2' /> public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom) {try{GC.Collect();Marshal.FinalReleaseComObject(applicationObject);GC.Collect();}catch (Exception ex){LogHelper.Error(ex);} }
六 正确的创建和使用Excel实例
在应用程序开发中,通常我们需要在Winform中导出到Excel或者对Excel文件进行读写,如果您选择使用Excel API的话(当然更好的方式是直接使用OpenXML或者相似的技术直接生成Excel文件),通常的做法是直接创建一个Excel实例:
Microsoft.Office.Interop.Excel.Application m_objExcel = new Microsoft.Office.Interop.Excel.Application();
但是这种方式过于简单粗暴, 他相当于直接在后台创建一个Excel的实例。当您导出多次的时候会创建多个这样的应用程序,并且在关闭程序的时候很可能导致这些资源不能释放,最明显的是您会发现在资源管理器中会出现很多个Excel的进程没有被关闭。
还有一个场景就是插件的自动升级。 通常我们的插件需要升级,如果发现新的版本,则回去下载,然后对现有的dll进行覆盖。通常,改升级程序时一个独立于excel的插件,覆盖之前通常需要关闭当前打开的Excel才能进行覆盖。在检测到升级并下载完新的升级包时,需要提示用户关闭Excel,点击确定的时候,一般的做法是强制终止任务管理器中的Excel进行,再执行覆盖,这样做的问题在于:Excel会认为插件导致了Excel的意外关闭,在下次打开的时候,会直接提示改插件存在严重问题,是否禁用。通常一般的用户会去点击禁用。这样显然不好。
打开或者获取Office产品实例的方法有很9种,比较友好的方式是,如果当前已经有Excel的实例在运行,则不要去new新的实例,而是通过一些方法直接从现有的实例中去获取和创建,比如可以通过Marshal.GetActiveObject方法传入想要获取实例的ProgID即可。对于上面的方法,更友好的版本是:
public Microsoft.Office.Interop.Excel.Application StartExcel() {Microsoft.Office.Interop.Excel.Application instance = null;try{instance = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");}catch (System.Runtime.InteropServices.COMException ex){instance = new Microsoft.Office.Interop.Excel.Application();}return instance; }
该方法首先尝试使用GetActiveObject从当前活动的Excel实例获取实例,如果没有活动的Excel,则再新建一个Excel实例。这样就避免了肯能的资源浪费。
在升级程序中,我们可以直接调用该方法返回的实例,然后调用它的Quit方法就可以。
七 Excel 2003上安装部署不上的问题
使用Shared Com Add-in创建的程序在Office 2003上有可能安装不上, 该问题在前一篇有关Excel安装部署中 提到过,需要安装两个针对Office 2003的补丁。这篇文章Deploying a Shared COM add-in for Office 2003 (Visual Studio 2008 SP1) [and how to work around a known issue which causes the add-in to fail to load if KB908002 is not installed.] 有详细介绍该补丁如何在部署时打包及安装,这里就不在赘述了。
浅谈Excel开发:九 Excel 开发中遇到的常见问题及解决方法相关推荐
- 开发微信小程序 中遇到的坑 及解决方法
开发微信小程序 中遇到的坑 及解决方法 参考文章: (1)开发微信小程序 中遇到的坑 及解决方法 (2)https://www.cnblogs.com/feiye512/p/6070052.html ...
- 报表开发工具FastReport.NET的十大常见问题及解决方法
Fastreport是目前世界上主流的图表控件,具有超高性价比,以更具成本优势的价格,便能提供功能齐全的报表解决方案,连续三年蝉联全球文档创建组件和库的" Top 50 Publishers ...
- 浅谈运维工程师的开发能力的培养
写在前面 本文已获得作者授权,作者的博客地址:https://www.cuiliangblog.cn/ 原文链接:浅谈运维工程师的开发能力的培养 一.运维工程师发展路线 1. 传统运维 侧重点是解决具 ...
- catia三维轴承_浅谈基于CATIA二次开发的单排四点接触球轴承三维设计论文
浅谈基于CATIA二次开发的单排四点接触球轴承三维设计论文 一.概述 单排四点接触球转盘轴承是一种能够同时承受较大轴向负荷.径向负荷和倾覆力矩等综合载荷,集支承.旋转.传动.固定等多种功能于一身的特殊 ...
- 浅谈桌面应用程序的开发
浅谈桌面应用程序的开发 常用的桌面的应用程序的开发有这几个技术(难度递减) windows api > MFC > Qt > WinForm = VB = DevExpress &g ...
- 计算机技术在测绘专业应用,浅谈计算机技术在测绘工程制图中的应用(原稿)
<浅谈计算机技术在测绘工程制图中的应用(原稿).doc>由会员分享,可免费在线阅读全文,更多与<浅谈计算机技术在测绘工程制图中的应用(原稿)>相关文档资源请在帮帮文库(www. ...
- 浅谈金达莱花在朝鲜族家居装饰中的传承发展
浅谈金达莱花在朝鲜族家居装饰中的传承发展 摘要:金达莱花历史悠久.风格独特,已经成为韩国传统文化的标志之一.然而金达莱花的诞生距今已经有几百年的历史,朝鲜民族将金达莱看成是春天的使者,坚贞.美好.吉祥 ...
- 计算机在食品安全的应用,浅谈计算机在农产品食品安全中应用.doc
浅谈计算机在农产品食品安全中应用 浅谈计算机在农产品食品安全中应用 摘 要:随着人们健康意识的不断提高,农产品食品的安全问题越来越受到人们的关注,因此农产品.食品质量的监督.产品溯源等工作的重要性更加 ...
- 计算机室在初中英语教学中的应用,浅谈信息技术在初中英语教学中的运用
浅谈信息技术在初中英语教学中的运用 关键词:初中 英语 信息技术 摘要:如今信息技术高速发展,其应用范围愈加广泛.信息技术在教学中的应用,为学生接受知识提供了更为便捷的途径.在初中的英语教学中,结合信 ...
最新文章
- linux进程中对信号的屏蔽,linux进程中的信号屏蔽
- Scrapy框架的学习(11.scrapy框架中的下载中间件的使用(DownloaderMiddlewares))
- 32 | 浅谈容器网络
- lintcode-【简单题】快乐数
- 大数据实效_全流程监督 让大数据督查取得实效
- 在制造业中推进机器人技术的五种方法
- pycharm自定义代码段
- DDoS booter滥用 DTLS 服务器放大攻击
- 开张第一天,一年之际在于春
- Bootstrap3 表单-水平排列的表单
- 稳压二极管和TVS管知识点总结
- Django相关文档地址
- Android内部存储和外部存储以及缓存清理和内存清理!
- 京东2022双11预售期,商家该做些什么?
- 2018年10月自考计算机应用基础真题,2018年10月全国自考《计算机应用基础》真题与答案...
- “硬核”刘强东是怎么炼成的?
- 一般树与二叉树的相互转换
- java vbs_一键定位配置JAVA SDK 环境变量 VBS脚本全自动操作正式开源
- ubuntu上安装spark详细步骤
- 南京数字孪生智能工厂,3d可视化工业建模,数字工厂智能车间建设
热门文章
- 分享一个基于 Shiro 的权限管理系统,亮点是支持 restful 风格 URL.
- How to enable coredump on centos 7.6
- 超级队长VR线下体验店落地上海,让娱乐突破想象
- 企业分布式微服务云SpringCloud SpringBoot mybatis (二)Spring Boot属性配置文件详解...
- 架构之道(4) - 最後的最後,让大家都知道自己在做什麽
- python3精简笔记——开篇
- angular.js 嵌套路由
- 004_URL 路由 - 对磁盘文件的请求进行路由
- android:AlertDialog控件
- The number of Oracle redo threads (2) is not the same as the number of checkpoint threads (1)