在All-In-One Code Framework 上下载了一个使用.NET操作Office的例程。文中详细介绍了如何使用Microsoft Excel Primary Interop Assembly (PIA)生成Excel及释放Com资源。这篇文章把代码提出来,简单翻译加上我的理解。
使用Office PIA之前需要安装,如果没有安装Office,需要先下载安装:
Office 2007 Primary Interop Assemblies
Office 2003 Primary Interop Assemblies
PIA会安装到Visual Studio的安装目录,引用的时候需要注意,不是引用Com对象。

??? 当使用这个解决方案时,应当尽量避免通过通道调用访问器,因为这些对象会将堆中的Runtime Callable Wrapper (RCW)孤立,从而不能调用Marshal.ReleaseComObject释放这些资源。
RCW是在运行时通过CLR从Interop Assembly的元数据中获取相关信息动态的实例化而得到的。可以理解为介乎于COM和.Net应用程序之间的一个代理,.Net应用程序对COM组件的调用请求都是通过RCW中转。
例如:
Excel.Workbook oWB = oXL.Workbooks.Add(missing);
通过oXL.Workbooks.Add这样一个通道调用访问器,将会在GC堆中为Workbooks创建一个RCW,但是引用会被放到栈的范围之内,然后很快被丢弃。
这样将没有办法为这个RCW调用MarshalFinalReleaseComObject。
解决方案1:
如果明确的为每一个访问器分配一个变量,然后就可以释放它们了,例如:
可以通过下边的代码创建一个WorkBook:
Excel.Workbooks oWBs = oXL.Workbooks; Excel.Workbook oWB = oWBs.Add(missing);
然后通过变量释放堆中的资源:
Marshal.FinalReleaseComObject(oWBs); oWBs = null; Marshal.FinalReleaseComObject(oWB); oWB = null;
Solution1.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
namespace CSAutomateExcel
{
static class Solution1
{
public static void AutomateExcel()
{
object missing = Type.Missing;
Excel.Application oXL = null;
Excel.Workbooks oWBs = null;
Excel.Workbook oWB = null;
Excel.Worksheet oSheet = null;
Excel.Range oCells = null;
Excel.Range oRng1 = null;
Excel.Range oRng2 = null;
try
{
//创建一个Microsoft Excel的实例,并设置为隐藏界面
oXL = new Excel.Application();
oXL.Visible = false;
//创建一个新的工作表Workbook
oWBs = oXL.Workbooks;
oWB = oWBs.Add(missing);
//获取工作表激活的Worksheet,并设置sheet的名称
oSheet = oWB.ActiveSheet as Excel.Worksheet;
oSheet.Name = "Report";
#region 填充数据
// 设置标题行
oCells = oSheet.Cells;
oCells[1, 1] = "First Name";
oCells[1, 2] = "Last Name";
oCells[1, 3] = "Full Name";
// 创建一个数据数组
string[,] saNames = new string[,] {
{"John", "Smith"},
{"Tom", "Brown"},
{"Sue", "Thomas"},
{"Jane", "Jones"},
{"Adam", "Johnson"}};
// 使用数组中的数据填充 A2:B6
oRng1 = oSheet.get_Range("A2", "B6");
oRng1.Value2 = saNames;
// 使用公式(=A2 & " " & B2)填充C2:C6
oRng2 = oSheet.get_Range("C2", "C6");
oRng2.Formula = "=A2 & \" \" & B2";
#endregion
// 保存工作表,然后关闭
string fileName = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location) + "\\Sample1.xlsx";
oWB.SaveAs(fileName, Excel.XlFileFormat.xlOpenXMLWorkbook,
missing, missing, missing, missing,
Excel.XlSaveAsAccessMode.xlNoChange,
missing, missing, missing, missing, missing);
oWB.Close(missing, missing, missing);
//当Excel或通过程序调用启动,并且Application.Visible = false时,Application.UserControl 的值是false。
//如果UserControl为false,且还有未完成的调用时,Excel进程将会继续保留。
//可以设置UserControl为true,强迫Quit方法调用时,应用程序被终止,而不管未完成的调用。
oXL.UserControl = true;
oXL.Quit();
}
catch (Exception ex)
{
Console.WriteLine("Solution1.AutomateExcel throws the error: {0}",
ex.Message);
}
finally
{
// 明确的释放非托管的COM资源
// 调用Marshal.FinalReleaseComObject对所有的访问其对象.
if (oRng2 != null)
{
Marshal.FinalReleaseComObject(oRng2);
oRng2 = null;
}
if (oRng1 != null)
{
Marshal.FinalReleaseComObject(oRng1);
oRng1 = null;
}
if (oCells != null)
{
Marshal.FinalReleaseComObject(oCells);
oCells = null;
}
if (oSheet != null)
{
Marshal.FinalReleaseComObject(oSheet);
oSheet = null;
}
if (oWB != null)
{
Marshal.FinalReleaseComObject(oWB);
oWB = null;
}
if (oWBs != null)
{
Marshal.FinalReleaseComObject(oWBs);
oWBs = null;
}
if (oXL != null)
{
Marshal.FinalReleaseComObject(oXL);
oXL = null;
}
}
}
}
}
解决方案2:
强制调用GC回收。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;
namespace CSAutomateExcel
{
static class Solution2
{
public static void AutomateExcel()
{
AutomateExcelImpl();
//当调用函数从栈中移除时,立即强制垃圾回收释放非托管的Excel Com资源
GC.Collect();
GC.WaitForPendingFinalizers();
//GC需要被调用两次
//第一次 产生将要结束的资源列表
//第二次 实际执行释放资源
GC.Collect();
GC.WaitForPendingFinalizers();
}
private static void AutomateExcelImpl()
{
object missing = Type.Missing;
try
{
// 创建一个Excel实例并隐藏
Excel.Application oXL = new Excel.Application();
oXL.Visible = false;
// 创建一个Workbook
Excel.Workbook oWB = oXL.Workbooks.Add(missing);
Console.WriteLine("A new workbook is created");
//创建一个Worksheet
Excel.Worksheet oSheet = oWB.ActiveSheet as Excel.Worksheet;
oSheet.Name = "Report";
#region 填充数据
// Set the column header
oSheet.Cells[1, 1] = "First Name";
oSheet.Cells[1, 2] = "Last Name";
oSheet.Cells[1, 3] = "Full Name";
// Construct an array of user names
string[,] saNames = new string[,] {
{"John", "Smith"},
{"Tom", "Brown"},
{"Sue", "Thomas"},
{"Jane", "Jones"},
{"Adam", "Johnson"}};
// Fill A2:B6 with an array of values (First and Last Names).
oSheet.get_Range("A2", "B6").Value2 = saNames;
// Fill C2:C6 with a relative formula (=A2 & " " & B2).
oSheet.get_Range("C2", "C6").Formula = "=A2 & \" \" & B2";
#endregion
// 保存文件并关闭对象
string fileName = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location) + "\\Sample2.xlsx";
oWB.SaveAs(fileName, Excel.XlFileFormat.xlOpenXMLWorkbook,
missing, missing, missing, missing,
Excel.XlSaveAsAccessMode.xlNoChange,
missing, missing, missing, missing, missing);
oWB.Close(missing, missing, missing);
//当Excel或通过程序调用启动,并且Application.Visible = false时,Application.UserControl 的值是false。
//如果UserControl为false,且还有未完成的调用时,Excel进程将会继续保留。
//可以设置UserControl为true,当Quit方法调用时,强制应用程序终止,而不管未完成的调用。
oXL.UserControl = true;
//调用Quit退出
oXL.Quit();
}
catch (Exception ex)
{
Console.WriteLine("Solution2.AutomateExcel throws the error: {0}", ex.Message);
}
}
}
}
发表评论
相关文章
国内AI资源汇总,AI聊天、AI绘画、AI写作、AI视频、AI设计、AI编程、AI音乐等,国内顺畅访问,无需科学上网。
扫码或点击进入:萤火AI大全
文章分类
最新评论