浅谈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;//Green
- FormatCondition 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;//Green
- FormatCondition 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 Sub
- Public Sub ReLink(ByVal oBook As Workbook)
- If IsEmpty(oBook.LinkSources(xlLinkTypeExcelLinks)) Then Exit Sub
- Dim lk As Variant
- For Each lk In oBook.LinkSources(xlLinkTypeExcelLinks)
- If lk Like "*" & ThisWorkbook.Name And lk <> ThisWorkbook.FullName Then
- oBook.ChangeLink lk, ThisWorkbook.FullName, xlLinkTypeExcelLinks
- End If
- Next 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.wx.request 只能访问 https 解决: 新建项目 不填appid 即可访问 localhost 2.页面中多重三元表达式 解析有问题 解决: <!--{{index}} { ...
- 示例浅谈PHP与手机APP开发,即API接口开发
示例浅谈PHP与手机APP开发,即API接口开发 API(Application Programming Interface,应用程序接口)架构,已经成为目前互联网产品开发中常见的软件架构模式,并且诞 ...
- 【ASP.NET MVC系列】浅谈jqGrid 在ASP.NET MVC中增删改查
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- java开发中遇到的问题及解决方法(持续更新)
摘自 http://blog.csdn.net/pony12/article/details/38456261 java开发中遇到的问题及解决方法(持续更新) 工作中,以C/C++开发为主,难免与其他 ...
- 【开发总结】—— BABYLON 3D开发常见问题及解决方法
前言:组内同事们根据长时间的Babylon.js开发实践,一起将项目开发中遇到的问题及解决方法做了一个梳理. ios [最好] 关闭离线缓存—— 解决添加了反射的mesh 丢失的问题 不要使用 pos ...
- IOS开发证书变成“此证书的签发者无效”解决方法
IOS开发证书全部变成无效,如下图 打包提示错误 解决方法: 1. 下载https://developer.apple.com/certificationauthority/AppleWWDRCA ...
- 使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法
使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法 是xls文件有问题,另存为新的xls文件,然后导入就不会有这个问题
- C#用ado.net访问EXCEL的常见问题及解决方法
C#用ado.net访问EXCEL的常见问题及解决方法,除了像sql server,access常见的数据库,其实Excel文件也可以做为数据库访问. ado.net访问excel的实例: OleDb ...
- 关于Excel 2016 总要点击两次才能打开的解决方法
1.快捷键:win+r,输入:regedit2.定位到如下项目“HKEY_CLASSES_ROOT\Excel.Sheet.12\shell\Open\command”:3.编辑原有的内容,在最后添加 ...
随机推荐
- ue4 重新生成ide project文件的命令行
有时候换了机器,工程文件没了,通常是在编辑器里有个菜单项可以生成 但是有时编辑器自身都编不过,没法运行 这时需要调试代码,可以用命令行生成相应的工程文件: ../UnrealEngine/Genera ...
- js数组方法push pop shift unshift的返回值
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度. pop() 方法用于删除并返回数组的最后一个元素. unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度. s ...
- arch 安装图形界面
图形界面的安装.--- 引导: .显卡驱动,这里我们是集成显卡,根据wiki提示安装xf86-video-intel .安装xorg-server,xorg-server-utils,xorg-xin ...
- linux bash 笔记
Bash的简单使用笔记: 1- 命令行参数(调用脚本后面跟的参数) 2- 命令行选项(修改命令行为的单字符串) 3- 获取键盘输入 4- 读文件 5- 函数 1.命令行参数(调用脚本后面跟的参数) x ...
- Java自定义表单、自定义字段
最近想实现用户自定义数据库中的字段,我想大部分人第一想到的就是EAV(Entity-Attribute-Value),这种方式对于写一个小的毕业设计应该还可以使用,当然也有很多CMS系统采用这种方式, ...
- 关于MYSQL四种引擎
你能用的数据库引擎取决于mysql在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型INNO ...
- Hibernate+jsp+struts+spring做增删该查,
同样还是web项目,这里只做了一张表,做一个测试,例子.主要是建Hibernate 的时候要非常注意,有时间了整理一下建Hiberbnate 的时候需要注意的事项 这里我是建了5个包,其实只要四个就好 ...
- yii使用createCommand()增删改查
查询单条数据$sql = "SELECT `name` FROM `table` WHERE id='7'";$users=Yii::$app->db->createC ...
- 通过硬编码获取dubbo服务对象
运维进行监控dubbo服务的时候可能会调用dubbo服务对象,并且定期去执行,这时候如果需要添加新的服务,可能需要修改监控dubbo服务的配置,即dubbo-producer.xml或是dubbo-c ...
- 什么是SQL注入
一种简单的web应用架构,: 表示层(web浏览器或呈现引擎)———>逻辑层(如C#,ASP,.NET,PHP,JSP等编程语言)———>存储层(如Microsoft SQL Server ...