来源:http://www.cnblogs.com/ahui/archive/2013/03/05/2944441.html

有一个项目用到Excel组件产生报表,本以为这个通用功能是个很简单的case,没想到结果却花了不少时间

本人开发环境: Win7 64bit + IIS7.5 + VS2012

最开始碰到的问题是NetworkService无法访问com组件,需要对帐户进行授权,这个相信很多人都碰到过,也很好解决

1.运行:mmc comexp.msc /32,找到我的电脑 -> DCom配置中的Microsoft Excel Application
2.在Microsoft Excel Application上点击右键,选择"属性"
3.点击"标识"标签,选择"交互式用户"
4.点击"安全"标签,在"启动和激活权限"上点击"自定义",然后点击对应的"编辑"按钮,在弹出的"安全性"对话框中填加一个"NETWORK SERVICE"用户(注意要选择本计算机名),并给它赋予"本地启动"和"本地激活"权限.
5.依然是"安全"标签,在"访问权限"上点击"自定义",然后点击"编辑",在弹出的"安全性"对话框中也填加一个"NETWORK SERVICE"用户,然后赋予"本地访问"权限.

之后程序能正常运行了,但这个项目需要并发处理,可能有多个ApplicationClass实例,这时问题就来了

调用_Application.Quit()之后,Excel.exe进程仍然存在,搜索之后,解决方案来了:

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int proecessId); public static void KillExcel(Excel.Application excel)
{
var t = new IntPtr(excel.Hwnd);
int proecessId = 0;
GetWindowThreadProcessId(t, out proecessId);
Process.GetProcessById(proecessId).Kill();
}

用VS调试正常工作,在Console应用里也OK,但如果用IIS启动就不行了,断点调用发现GetWindowThreadProcessId取得的processId一直为0。

搜索了一下网站,大概意思是运行在不同的Session级别,Excel是以"交互式用户"启动,即本地Administrator,所以取不到对应的进程Id,

于是又在Microsoft Excel Application的属性->标识中将启动用户这项选中,发现这回Excel进程是以NetworkService启动,测试代码如下:

Application excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
excel.Quit();
KillExcel(excel);

也能正常退出,好像问题已经解决了,OK,来编写业务代码了

Workbook workbook = excel.Workbooks.Add(true);

一调试,马上报异常,继续Google和看微软官方网站,发现Office自动化必须以交互式用户方式来启动,而NetworkService是虚拟的,所以这条路显然走不通。

即使你想到给NetworkService提升权限,也不清楚正常运行Excel倒底要那些权限,搜索后也无果,大部分推荐使用第三方组件,或者建立一个专用的帐户,或者建立一个Service项目来处理Http请求。

这些方式各有各的不足,将建立专用域帐户做一备选方案后,继续寻求解决办法,最先找到的代码是:

Application excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
Workbook workbook = excel.Workbooks.Add(true);
Worksheet worksheet = (Worksheet)workbook.ActiveSheet;
// do sth
excel.Quit();
Marshal.ReleaseComObject(worksheet);
workbook.Close(false, null, null);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(excel);

这段代码相信遇到这个问题的人都试过吧,结果还是不行,在IIS下面启动的Excel进程不一定按你的要求及时退出

后来一位高手同事给了个链接 http://support.microsoft.com/kb/317109

测试代码:

Application excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
Workbooks workbooks = excel.Workbooks;
Workbook workbook = workbooks.Add(true);
Worksheet worksheet = (Worksheet)workbook.ActiveSheet;
// do sth
excel.Quit();
Marshal.ReleaseComObject(worksheet);
workbook.Close(false, null, null);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);

这段代码测试正常,看到希望了,继续Coding,然后测试。

服务器运行一段时间后,仍然存在大量的Excel进程没有退出,有心的人估计从两段稍有差别的代码看到问题的所在了,问题就在于:

调用com+对象后,必须要及时释放资源,那怕你只是请求了某个属性,只要这个属性是com+资源,也得显示释放资源

代码如下:

 public class ExcelApp : IDisposable
{
private Application _excel;
private Workbooks _workbooks;
private Workbook _workbook;
private Worksheet _worksheet; public ExcelApp()
{
_excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
_workbooks = _excel.Workbooks;
} public int ColCount { get; set; } public int RowCount { get; set; } public string WorksheetName
{
get
{
return _worksheet != null ? _worksheet.Name : null;
}
set
{
if (_worksheet != null)
{
_worksheet.Name = value;
}
}
} #region Get Excel Range
public Range GetCell(int rowIndex, int cellIndex)
{
Range cells = null;
Range range = null; try
{
cells = _excel.Cells;
range = (Range)cells[ + rowIndex, + cellIndex];
}
finally
{
Marshal.ReleaseComObject(cells);
} return range;
} public Range GetColumn(int cellIndex)
{
Range range = null;
Range cells = null;
object rangeX = null;
object rangeY = null; try
{
cells = _excel.Cells;
rangeX = cells[, + cellIndex];
rangeY = cells[RowCount, + cellIndex];
range = _worksheet.get_Range(rangeX, rangeY);
}
finally
{
Marshal.ReleaseComObject(rangeX);
Marshal.ReleaseComObject(rangeY);
Marshal.ReleaseComObject(cells);
} return range;
} public Range GetRange(int xRowIndex, int xCellIndex, int yRowIndex, int yCellIndex)
{
Range range = null;
Range cells = null;
object rangeX = null;
object rangeY = null; try
{
cells = _excel.Cells;
rangeX = cells[ + xRowIndex, + xCellIndex];
rangeY = cells[yRowIndex + , yCellIndex + ];
range = _worksheet.get_Range(rangeX, rangeY);
}
finally
{
Marshal.ReleaseComObject(rangeX);
Marshal.ReleaseComObject(rangeY);
Marshal.ReleaseComObject(cells);
} return range;
}
#endregion public void Save(string fullFilePath)
{
if (string.IsNullOrEmpty(fullFilePath))
{
throw new ArgumentNullException("fullFilePath");
} string directory = Path.GetDirectoryName(fullFilePath); if (string.IsNullOrEmpty(directory))
{
throw new ArgumentException("fullFilePath is not a valid file path.");
} if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
} _workbook.SaveCopyAs(fullFilePath);
} public void Open(string fullFilePath)
{
_workbook = _workbooks._Open(fullFilePath,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value); _worksheet = (Worksheet)_workbook.ActiveSheet; ColCount = ;
RowCount = ;
} public void AddWorkbook()
{
_workbook = _workbooks.Add(true);
_worksheet = (Worksheet)_workbook.ActiveSheet; ColCount = ;
RowCount = ;
} public void Reset()
{
Close();
AddWorkbook();
} private void Close()
{
if (_worksheet != null)
{
Marshal.ReleaseComObject(_worksheet);
}
if (_workbook != null)
{
_workbook.Close(false, null, null);
Marshal.ReleaseComObject(_workbook);
}
_worksheet = null;
_workbook = null;
} #region IDisposable Members public void Dispose()
{
try
{
Close(); if (_workbooks != null)
{
Marshal.ReleaseComObject(_workbooks);
}
if (_excel != null)
{
_excel.Quit();
Marshal.ReleaseComObject(_excel);
}
}
catch (Exception ex)
{
Console.WriteLine("dispose ExcelApp object failed", ex);
} _workbooks = null;
_excel = null;
} #endregion
} public class Disposable
{
public static Disposable<T> Create<T>(T o) where T : class
{
return new Disposable<T>(o);
}
} public class Disposable<T> : IDisposable where T : class
{
public T Value; internal Disposable(T o)
{
Value = o;
} public void Dispose()
{
if (Value != null)
{
Marshal.ReleaseComObject(Value);
}
}
}
public class ExcelApp : IDisposable
{
private Application _excel;
private Workbooks _workbooks;
private Workbook _workbook;
private Worksheet _worksheet; public ExcelApp()
{
_excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
_workbooks = _excel.Workbooks;
} public int ColCount { get; set; } public int RowCount { get; set; } public string WorksheetName
{
get
{
return _worksheet != null ? _worksheet.Name : null;
}
set
{
if (_worksheet != null)
{
_worksheet.Name = value;
}
}
} #region Get Excel Range
public Range GetCell(int rowIndex, int cellIndex)
{
Range cells = null;
Range range = null; try
{
cells = _excel.Cells;
range = (Range)cells[1 + rowIndex, 1 + cellIndex];
}
finally
{
Marshal.ReleaseComObject(cells);
} return range;
} public Range GetColumn(int cellIndex)
{
Range range = null;
Range cells = null;
object rangeX = null;
object rangeY = null; try
{
cells = _excel.Cells;
rangeX = cells[1, 1 + cellIndex];
rangeY = cells[RowCount, 1 + cellIndex];
range = _worksheet.get_Range(rangeX, rangeY);
}
finally
{
Marshal.ReleaseComObject(rangeX);
Marshal.ReleaseComObject(rangeY);
Marshal.ReleaseComObject(cells);
} return range;
} public Range GetRange(int xRowIndex, int xCellIndex, int yRowIndex, int yCellIndex)
{
Range range = null;
Range cells = null;
object rangeX = null;
object rangeY = null; try
{
cells = _excel.Cells;
rangeX = cells[1 + xRowIndex, 1 + xCellIndex];
rangeY = cells[yRowIndex + 1, yCellIndex + 1];
range = _worksheet.get_Range(rangeX, rangeY);
}
finally
{
Marshal.ReleaseComObject(rangeX);
Marshal.ReleaseComObject(rangeY);
Marshal.ReleaseComObject(cells);
} return range;
}
#endregion public void Save(string fullFilePath)
{
if (string.IsNullOrEmpty(fullFilePath))
{
throw new ArgumentNullException("fullFilePath");
} string directory = Path.GetDirectoryName(fullFilePath); if (string.IsNullOrEmpty(directory))
{
throw new ArgumentException("fullFilePath is not a valid file path.");
} if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
} _workbook.SaveCopyAs(fullFilePath);
} public void Open(string fullFilePath)
{
_workbook = _workbooks._Open(fullFilePath,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value,
Missing.Value, Missing.Value); _worksheet = (Worksheet)_workbook.ActiveSheet; ColCount = 0;
RowCount = 0;
} public void AddWorkbook()
{
_workbook = _workbooks.Add(true);
_worksheet = (Worksheet)_workbook.ActiveSheet; ColCount = 0;
RowCount = 0;
} public void Reset()
{
Close();
AddWorkbook();
} private void Close()
{
if (_worksheet != null)
{
Marshal.ReleaseComObject(_worksheet);
}
if (_workbook != null)
{
_workbook.Close(false, null, null);
Marshal.ReleaseComObject(_workbook);
}
_worksheet = null;
_workbook = null;
} #region IDisposable Members public void Dispose()
{
try
{
Close(); if (_workbooks != null)
{
Marshal.ReleaseComObject(_workbooks);
}
if (_excel != null)
{
_excel.Quit();
Marshal.ReleaseComObject(_excel);
}
}
catch (Exception ex)
{
Console.WriteLine("dispose ExcelApp object failed", ex);
} _workbooks = null;
_excel = null;
} #endregion
} public class Disposable
{
public static Disposable<T> Create<T>(T o) where T : class
{
return new Disposable<T>(o);
}
} public class Disposable<T> : IDisposable where T : class
{
public T Value; internal Disposable(T o)
{
Value = o;
} public void Dispose()
{
if (Value != null)
{
Marshal.ReleaseComObject(Value);
}
}
}

调用示例:

 using (var excel = new ExcelApp())
{
excel.AddWorkbook(); using (var range = Disposable.Create(excel.GetCell(, )))
{
using (var font = Disposable.Create(range.Value.Font))
{
font.Value.Color = ;
} range.Value.Value = ;
}
}
using (var excel = new ExcelApp())
{
excel.AddWorkbook(); using (var range = Disposable.Create(excel.GetCell(0, 0)))
{
using (var font = Disposable.Create(range.Value.Font))
{
font.Value.Color = 255;
} range.Value.Value = 200;
}
}

至此在IIS里调用Excel不能退出的问题总算圆满解决了,贴出来和大家分享一下,免得其他人也在这上面浪费时间

解决在IIS中调用Microsoft Office Excel组件后进程无法正常退出的问题的更多相关文章

  1. IIS中使用Microsoft.Office.Interop.Excel 常见问题:RPC 服务器不可用。 (异常来自 HRESULT:0x800706BA) 的异常。等

    IIS中使用Microsoft.Office.Interop.Excel 异常1: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} ...

  2. window2008 64位系统无法调用Microsoft.Office.Interop组件进行文件另存的解决办法

    生成execl时遇到的问题: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80070005 ...

  3. ERP中通过EDI导入资料的时候出现【Microsoft Office Excel不能访问文件‘C:\Windows\TEMP\433....’

    问题描述: ERP中导入单据的时候报错,Microsoft Office Excel不能访问文件'C:\Windows\TEMP\433....可能的原因有:·文件名称或路径不存在,文件正被其他程序使 ...

  4. CVE-2011-0104:Microsoft Office Excel 中的栈溢出漏洞调试分析

    0x01 前言 CVE-2011-0104 是 Microsoft Office 中的 Excel(没有打补丁的情况下)表格程序在处理 TOOLBARDEF 中的 Record 字节时没有对 Len ...

  5. Microsoft Office Excel 不能访问文件 的解决办法

    Microsoft Office Excel 不能访问文件"a.xls". 可能的原因有: ? 文件名称或路径不存在.  ? 文件正被其他程序使用.  ? 您正要保存的工作簿与当前 ...

  6. Microsoft Office Excel 不能访问文件及COM无法访问

    Microsoft Office Excel 不能访问文件及COM无法访问 Microsoft Office Excel 不能访问文件“*.xls”. 可能的原因有: 1 文件名称或路径不存在. 2  ...

  7. [Excel操作]Microsoft Office Excel 不能访问文件

    最近,客户服务器迁移,因操作系统环境变化而引起的的环境问题一堆,遇到的问题并解决方法在“[Excel]操作”类别会体现. Microsoft Office Excel 不能访问文件“C:\\LMSEx ...

  8. CVE-2011-0104 Microsoft Office Excel缓冲区溢出漏洞 分析

    漏洞简述   Microsoft Excel是Microsoft Office组件之一,是流行的电子表格处理软件.        Microsoft Excel中存在缓冲区溢出漏洞,远程攻击者可利用此 ...

  9. Microsoft Office Excel 不能访问文件

    问题描述: Microsoft Office Excel 不能访问文件“XX.xls”.可能的原因有: 1 文件名称或路径不存在.2 文件正被其他程序使用.3 您正要保存的工作簿与当前打开的工作簿同名 ...

随机推荐

  1. 第九十九节,JavaScript数据类型

    JavaScript数据类型 学习要点: 1.typeof操作符 2.Undefined类型 3.Null类型 4.Boolean类型 5.Number类型 6.String类型 7.Object类型 ...

  2. Stack Overflow for web end

    Jquery UI tooltip on disabled button In general, disabled elements do not trigger any DOM events. Th ...

  3. javascript基础(三)运算

    原文http://pij.robinqu.me/ 递增递减操作符(包括前置和后置).一元正负符号操作符 这些操作符适用于任何数据类型的值,针对不同类型的值,该操作符遵循以下规则(经过对比发现,其规则与 ...

  4. deque (STL)

    //双端队列 //deque的成员函数 c.assign(beg, end); //将[beg, end]区间中的数据赋值给c c.assign(n, elem); //将n个elem的拷贝赋值给c ...

  5. lua 中操作系统库

    time 和 date 两个函数在lua中实现所有的时钟查询功能. 函数time在没有参数时返回当前时钟的数值.(在许多操作系统中,该数值是距离某个特定时间的秒数). date是time的一种“反函数 ...

  6. 【转】ActiveMQ与虚拟通道

    郑重提示,本文转载自http://shift-alt-ctrl.iteye.com/blog/2065436 ActiveMQ提供了虚拟通道的特性(Virtual Destination),它允许一个 ...

  7. 洛谷-小鱼会有危险吗-BOSS战-入门综合练习2

    题目描述 Description 有一次,小鱼要从A处沿直线往右边游,小鱼第一秒可以游7米,从第二秒开始每秒游的距离只有前一秒的98%.有个极其邪恶的猎人在距离A处右边s米的地方,安装了一个隐蔽的探测 ...

  8. Table获取checkbox选中行数据

    //检测勾选值 function checkEnter() { var Ivalue = ""; $("#dataTable tr").each(functio ...

  9. Objective-C中的instancetype与id的区别

    一.什么是instancetype instancetype是clang 3.5开始,clang提供的一个关键字,表示某个方法返回的未知类型的Objective-C对象.我们都知道未知类型的的对象可以 ...

  10. 2015 ACM/ICPC Asia Regional Shenyang Online

    1001 Traversal 1002 Best Solver 1003 Minimum Cut 类似于POJ 3417的做法. 考虑每条新边对树边的覆盖次数. 每条树边被覆盖的次数其实就是断裂这条树 ...