浅谈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.编辑原有的内容,在最后添加 ...
随机推荐
- Linux中的工作管理(Job Control )
以前使用Linux老是会不小心按下Ctrl + z,然后就出现看不懂的情况,以为程序突然就没了,今天专门研究了下Linux下的几个快捷键和工作管理. 其中找到一篇很不错的文章,大部分是里面转载的. 原 ...
- Maemo平台上如何使用Openvpn
Maemo是一个开源的智能手机软件平台社区,它是基于Debia的LInux发行版本,Maemo的大多是开源的,并已经制定了Maemo和诺基亚内部的设备与许多开源项目,例如,Debian的Linux内核 ...
- js常用的地址栏参数获取
用JS获取地址栏参数的方法(超级简单) 方法一:采用正则表达式获取地址栏参数:( 强烈推荐,既实用又方便!) function GetQueryString(name) { var reg ...
- 【温故Delphi】Win32API之CreateMutex
解决问题 如何让一个软件在一台机器上只能运行一个实例呢?这个问题用专业术语就是进程互斥.这个问题可以通过CreateMutex来解决. 进程互斥的核心思想:进程在启动时首先检查是否存在此进程实例,如果 ...
- Codeigniter 在Active Record中限制批量更新数目
今天手头电商项目有个需求是:将订单中的优惠券自动发放给买家,所以要只更新优惠券表中的某几行数据,查了手册和网络都没有解决办法. 一开始用循环和遍历来做都是错的,因为update语句一下就更新掉所有符合 ...
- C++中未初始化的bool值的问题
原创文件,欢迎阅读,禁止转载. 问题描述 你见过一个这样的bool值吗,判断 var 和 !var 都是成立的,今天被我遇到了,是在一个坑里遇到的.今天调试了一个程序,发送一个网络消息,结果总是得不到 ...
- XAF ListView 移除顶部工具栏
此方法适用于C/S及B/S,无需分别写在web和win中. Module下新建ViewController,代码如下: public partial class GongZuoJiaoShen_Yin ...
- 【算法与数据结构】二叉搜索树的Java实现
为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨. 首先,二叉搜索树是啥?它有什么用呢? 二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指 ...
- Bootstrap库之Modals
Bootstrap库之Modals. Bootstrap是Twitter推出的一个开发工具包,包含了一些比较常用的CSS,JavaScript代码.使用Bootstrap可以加快前端开发的速度.本站( ...
- XAF Spreadsheet property Editor
https://www.devexpress.com/Support/Center/Question/Details/T371232