Rah*_*han 4 .net c# interop reference ms-office
我有一个WinForms应用程序,它使用COM Interop连接到Microsoft Office应用程序.我已经阅读了大量关于如何正确处理COM对象的资料,这里是我的应用程序使用Microsoft自己的文章(这里)中的技术的典型代码:
Excel.Application excel = new Excel.Application();
Excel.Workbook book = excel.Workbooks.Add();
Excel.Range range = null;
foreach (Excel.Worksheet sheet in book.Sheets)
{
range = sheet.Range["A2:Z2"];
// Process [range] here.
range.MergeCells();
System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
range = null;
}
// Release explicitly declared objects in hierarchical order.
System.Runtime.InteropServices.Marshal.ReleaseComObject(book);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
book = null;
excel = null;
// As taken from:
// http://msdn.microsoft.com/en-us/library/aa679807(v=office.11).aspx.
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
Run Code Online (Sandbox Code Playgroud)
已剥离所有异常处理,以使代码更清晰.
循环中的[sheet]对象会发生什么[foreach]?据推测,它不会被清理,也不能在枚举时篡改它.一种替代方法是使用索引循环,但这会产生丑陋的代码,Office对象库中的某些构造甚至不支持索引.
此外,[foreach]循环引用集合[book.Sheets].是否会导致孤儿RCW计数?
这里有两个问题:
[Sheets],[book.Sheets]因为它们没有明确声明或清除?更新:
Hans Passant的建议令我感到惊讶,并认为有必要提供一些背景信息.
这是客户端/服务器应用程序,其中客户端连接到许多不同的Office应用程序,包括Access,Excel,Outlook,PowerPoint和Word等.它有超过1,500个类(并且正在增长),用于测试最终用户执行的某些任务以及在训练模式下模拟它们.它用于培训和测试学生的办公室在学术环境中的熟练程度.由于有多个开发人员和大量的类,因此难以实施COM友好的编码实践.我最终使用反射和源代码解析的组合来创建自动化测试,以确保在代码前审查阶段这些类的完整性.
将汉斯的建议尝试并恢复原状.
枚举
你的sheet循环变量确实没有被释放.在为excel编写互操作代码时,您必须经常观看RCW.优先使用foreachenumertions,我倾向于使用,for因为它让我意识到每当我通过必须显式声明变量来获取引用时.如果必须枚举,那么在循环结束时(在离开循环之前)执行以下操作:
if (Marshal.IsComObject(sheet)) {
Marshal.ReleaseComObject(sheet);
}
Run Code Online (Sandbox Code Playgroud)
并且,在发布引用之前,请注意continue并break留下循环语句.
中间体
这取决于中间体是否实际上是一个COM对象(book.Sheets是),但如果是,那么你需要首先在一个字段中获取它的引用,然后枚举该引用,然后确保你处理该字段.否则你基本上是"双点"(见下文):
using xl = Microsoft.Office.Interop.Excel;
...
public void DoStuff () {
...
xl.Sheets sheets = book.Sheets;
bool sheetsReleased = false;
try {
...
foreach (xl.Sheet in sheets) { ... try, catch and dispose of sheet ... }
... release sheets using Marshal.ReleaseComObject ...
sheetsDisposed = true;
}
catch (blah) { ... if !sheetsDisposed , dispose of sheets ... }
}
Run Code Online (Sandbox Code Playgroud)
上面的代码是一般模式(如果你完全输入它会变得很长,所以我只专注于重要的部分)
错误怎么样?
要谨慎使用try ... catch ... finally.确保您非常小心地使用它.finally 并不总是在堆栈溢出,内存不足,安全异常等情况下调用,所以如果你想确保清理,并且如果代码崩溃就不要打开幻像excel实例,那么你必须有条件地执行抛出异常之前在catch中释放引用.
因此,在每个foreach或每个for循环中,您还需要使用try ... catch ... finally以确保释放枚举变量.
双点打点
也不要"双点"(仅在代码行中使用一个句点).这样做foreach是一个常见的错误,我们很容易做到.如果我暂时不使用非COM C#,我仍然会抓住自己这样做,因为由于LINQ样式表达式,链接周期越来越常见.
双点的例子:
item.property.propertyIWantitem.Subcollection[0] (在调用该子集合上的索引器属性之前调用SubCollection)foreach x in y.SubCollection(基本上你是在呼唤SubCollection.GetEnumerator,所以你再次"双点")幻影Excel
当然,最重要的考验是在程序退出后查看Excel是否在任务管理器中保持打开状态.如果是,那么你可能会打开一个COM引用.
参考
你说你已经对此进行了大量的研究,但是如果它有所帮助,那么我发现的一些参考资料是:
强大的解决方案
上述参考文献之一提到了他用于foreach循环的助手.就个人而言,如果我做的不仅仅是一个简单的"脚本"项目,那么我将首先花时间开发一个专门为我的场景包装COM对象的库.我现在有一套共同的类,我可以重复使用,而且我发现在做其他事情之前投入设置它的时间远远超过了以后不必追捕未关闭的引用.自动化测试对于帮助解决这个问题也是必不可少的,并且可以获得任何COM互操作的奖励,而不仅仅是Excel.
每个COM对象(例如Sheet)都将包装在一个实现的类中IDisposable.它将暴露属性,而Sheets这些属性又具有索引器.一直跟踪所有权,最后如果你只是处理主对象,比如WorkbookWrapper,那么其他一切都在内部处理掉.例如,跟踪添加工作表,以便处理新工作表.
虽然这不是一种防弹方法,但至少95%的用例可以依赖它,而另外5%的用户完全了解并处理代码.最重要的是,一旦你第一次完成它,它就会经过测试和重复使用.
| 归档时间: |
|
| 查看次数: |
1396 次 |
| 最近记录: |