解决在IIS中调用Microsoft Office Excel组件后进程无法正常退出的问题
来源: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组件后进程无法正常退出的问题的更多相关文章
- IIS中使用Microsoft.Office.Interop.Excel 常见问题:RPC 服务器不可用。 (异常来自 HRESULT:0x800706BA) 的异常。等
IIS中使用Microsoft.Office.Interop.Excel 异常1: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} ...
- window2008 64位系统无法调用Microsoft.Office.Interop组件进行文件另存的解决办法
生成execl时遇到的问题: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80070005 ...
- ERP中通过EDI导入资料的时候出现【Microsoft Office Excel不能访问文件‘C:\Windows\TEMP\433....’
问题描述: ERP中导入单据的时候报错,Microsoft Office Excel不能访问文件'C:\Windows\TEMP\433....可能的原因有:·文件名称或路径不存在,文件正被其他程序使 ...
- CVE-2011-0104:Microsoft Office Excel 中的栈溢出漏洞调试分析
0x01 前言 CVE-2011-0104 是 Microsoft Office 中的 Excel(没有打补丁的情况下)表格程序在处理 TOOLBARDEF 中的 Record 字节时没有对 Len ...
- Microsoft Office Excel 不能访问文件 的解决办法
Microsoft Office Excel 不能访问文件"a.xls". 可能的原因有: ? 文件名称或路径不存在. ? 文件正被其他程序使用. ? 您正要保存的工作簿与当前 ...
- Microsoft Office Excel 不能访问文件及COM无法访问
Microsoft Office Excel 不能访问文件及COM无法访问 Microsoft Office Excel 不能访问文件“*.xls”. 可能的原因有: 1 文件名称或路径不存在. 2 ...
- [Excel操作]Microsoft Office Excel 不能访问文件
最近,客户服务器迁移,因操作系统环境变化而引起的的环境问题一堆,遇到的问题并解决方法在“[Excel]操作”类别会体现. Microsoft Office Excel 不能访问文件“C:\\LMSEx ...
- CVE-2011-0104 Microsoft Office Excel缓冲区溢出漏洞 分析
漏洞简述 Microsoft Excel是Microsoft Office组件之一,是流行的电子表格处理软件. Microsoft Excel中存在缓冲区溢出漏洞,远程攻击者可利用此 ...
- Microsoft Office Excel 不能访问文件
问题描述: Microsoft Office Excel 不能访问文件“XX.xls”.可能的原因有: 1 文件名称或路径不存在.2 文件正被其他程序使用.3 您正要保存的工作簿与当前打开的工作簿同名 ...
随机推荐
- 深入了解——CSS3新增属性
CSS3 选择器(Selector) 写过 CSS 的人应该对 CSS 选择器不陌生,我们所定义的 CSS 属性之所以能应用到相应的节点上,就是因为 CSS 选择器模式.参考下述代码: 清单 1. C ...
- L3-001. 凑零钱
L3-001. 凑零钱 题目链接:https://www.patest.cn/contests/gplt/L3-001 动态规划 这道题一看就知道应该用背包思想来做,不过想了好久没什么思路(dp实在是 ...
- 2016 JetBrains 开发者日遇见开发神器的创造者
JetBrains团队首次落地中国北京!2016 JetBrains开发者日将于2016年11月26日星期六10:00-16:30在中国北京举办! 这一次,我们将与社区演讲者一起谈论现代软件开发语言. ...
- 《JS权威指南学习总结--8.8 函数式编程和8.8.1使用函数处理数组》
内容要点: 和Lisp.Haskell不同,JS并非函数式编程语言,但在JS中可以像操控对象一样操控函数, 也就是说可以在JS中应用函数式编程技术.ES5中的数组方法(诸如map()和red ...
- HDU 1874 畅通工程续(初涉dijkstra算法实现)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1874 dijkstra算法实现可参照此博客学习:http://www.cnblogs.com/biye ...
- 【Python@Thread】Semaphore&糖果机
信号量适用与多线程竞争有限资源的情况. from atexit import register from time import ctime, sleep from threading import ...
- 安卓.点击头像-->编辑个人姓名-->提交后.同时调用js关闭页面-->返回上一层
$(document).ready(function() { $('#selfbtn').click(function(){ var u = navigator.userAgent; if (u.in ...
- 聊天系统Demo,增加文件传送功能(附源码)-- ESFramework 4.0 快速上手(14)
本文我们将介绍在ESFramework 4.0 快速上手(08) -- 入门Demo,一个简单的IM系统(附源码)的基础上,增加文件传送的功能.如果不了解如何使用ESFramework提供的文件传送功 ...
- hdu 1407 测试你是否和LTC水平一样高
Description 大家提到LTC都佩服的不行,不过,如果竞赛只有这一个题目,我敢保证你和他绝对在一个水平线上! 你的任务是: 计算方程x^2+y^2+z^2= num的一个正整数解. Inpu ...
- Python 之 geturl 学习
geturl为response对象的方法,由于有时候得到的网站url并不是真正的初始url而是通过重定向获得的,所以可以通过geturl方法获取真实的url.测试代码如下: from urllib2 ...