• 解决在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+资源,也得显示释放资源

    代码如下:

      1 public class ExcelApp : IDisposable
      2 {
      3     private Application _excel;
      4     private Workbooks _workbooks;
      5     private Workbook _workbook;
      6     private Worksheet _worksheet;
      7 
      8     public ExcelApp()
      9     {
     10         _excel = new ApplicationClass { Visible = false, DisplayAlerts = false };
     11         _workbooks = _excel.Workbooks;
     12     }
     13 
     14     public int ColCount { get; set; }
     15 
     16     public int RowCount { get; set; }
     17 
     18     public string WorksheetName
     19     {
     20         get
     21         {
     22             return _worksheet != null ? _worksheet.Name : null;
     23         }
     24         set
     25         {
     26             if (_worksheet != null)
     27             {
     28                 _worksheet.Name = value;
     29             }
     30         }
     31     }
     32 
     33     #region Get Excel Range
     34     public Range GetCell(int rowIndex, int cellIndex)
     35     {
     36         Range cells = null;
     37         Range range = null;
     38 
     39         try
     40         {
     41             cells = _excel.Cells;
     42             range = (Range)cells[1 + rowIndex, 1 + cellIndex];
     43         }
     44         finally
     45         {
     46             Marshal.ReleaseComObject(cells);
     47         }
     48 
     49         return range;
     50     }
     51 
     52     public Range GetColumn(int cellIndex)
     53     {
     54         Range range = null;
     55         Range cells = null;
     56         object rangeX = null;
     57         object rangeY = null;
     58 
     59         try
     60         {
     61             cells = _excel.Cells;
     62             rangeX = cells[1, 1 + cellIndex];
     63             rangeY = cells[RowCount, 1 + cellIndex];
     64             range = _worksheet.get_Range(rangeX, rangeY);
     65         }
     66         finally
     67         {
     68             Marshal.ReleaseComObject(rangeX);
     69             Marshal.ReleaseComObject(rangeY);
     70             Marshal.ReleaseComObject(cells);
     71         }
     72 
     73         return range;
     74     }
     75 
     76     public Range GetRange(int xRowIndex, int xCellIndex, int yRowIndex, int yCellIndex)
     77     {
     78         Range range = null;
     79         Range cells = null;
     80         object rangeX = null;
     81         object rangeY = null;
     82 
     83         try
     84         {
     85             cells = _excel.Cells;
     86             rangeX = cells[1 + xRowIndex, 1 + xCellIndex];
     87             rangeY = cells[yRowIndex + 1, yCellIndex + 1];
     88             range = _worksheet.get_Range(rangeX, rangeY);
     89         }
     90         finally
     91         {
     92             Marshal.ReleaseComObject(rangeX);
     93             Marshal.ReleaseComObject(rangeY);
     94             Marshal.ReleaseComObject(cells);
     95         }
     96 
     97         return range;
     98     }
     99     #endregion
    100 
    101     public void Save(string fullFilePath)
    102     {
    103         if (string.IsNullOrEmpty(fullFilePath))
    104         {
    105             throw new ArgumentNullException("fullFilePath");
    106         }
    107 
    108         string directory = Path.GetDirectoryName(fullFilePath);
    109 
    110         if (string.IsNullOrEmpty(directory))
    111         {
    112             throw new ArgumentException("fullFilePath is not a valid file path.");
    113         }
    114 
    115         if (!Directory.Exists(directory))
    116         {
    117             Directory.CreateDirectory(directory);
    118         }
    119 
    120         _workbook.SaveCopyAs(fullFilePath);
    121     }
    122 
    123     public void Open(string fullFilePath)
    124     {
    125         _workbook = _workbooks._Open(fullFilePath,
    126                                         Missing.Value, Missing.Value,
    127                                         Missing.Value, Missing.Value,
    128                                         Missing.Value, Missing.Value,
    129                                         Missing.Value, Missing.Value,
    130                                         Missing.Value, Missing.Value,
    131                                         Missing.Value, Missing.Value);
    132 
    133         _worksheet = (Worksheet)_workbook.ActiveSheet;
    134 
    135         ColCount = 0;
    136         RowCount = 0;
    137     }
    138 
    139     public void AddWorkbook()
    140     {
    141         _workbook = _workbooks.Add(true);
    142         _worksheet = (Worksheet)_workbook.ActiveSheet;
    143 
    144         ColCount = 0;
    145         RowCount = 0;
    146     }
    147 
    148     public void Reset()
    149     {
    150         Close();
    151         AddWorkbook();
    152     }
    153 
    154     private void Close()
    155     {
    156         if (_worksheet != null)
    157         {
    158             Marshal.ReleaseComObject(_worksheet);
    159         }
    160         if (_workbook != null)
    161         {
    162             _workbook.Close(false, null, null);
    163             Marshal.ReleaseComObject(_workbook);
    164         }
    165         _worksheet = null;
    166         _workbook = null;
    167     }
    168 
    169     #region IDisposable Members
    170 
    171     public void Dispose()
    172     {
    173         try
    174         {
    175             Close();
    176 
    177             if (_workbooks != null)
    178             {
    179                 Marshal.ReleaseComObject(_workbooks);
    180             }
    181             if (_excel != null)
    182             {
    183                 _excel.Quit();
    184                 Marshal.ReleaseComObject(_excel);
    185             }
    186         }
    187         catch (Exception ex)
    188         {
    189             Console.WriteLine("dispose ExcelApp object failed", ex);
    190         }
    191 
    192         _workbooks = null;
    193         _excel = null;
    194     }
    195 
    196     #endregion
    197 }
    198 
    199 
    200 public class Disposable
    201 {
    202     public static Disposable<T> Create<T>(T o) where T : class
    203     {
    204         return new Disposable<T>(o);
    205     }
    206 }
    207 
    208 public class Disposable<T> : IDisposable where T : class
    209 {
    210     public T Value;
    211 
    212     internal Disposable(T o)
    213     {
    214         Value = o;
    215     }
    216 
    217     public void Dispose()
    218     {
    219         if (Value != null)
    220         {
    221             Marshal.ReleaseComObject(Value);
    222         }
    223     }
    224 }
    复制代码
    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);
            }
        }
    }
    复制代码

    调用示例:

     1 using (var excel = new ExcelApp())
     2 {
     3     excel.AddWorkbook();
     4 
     5     using (var range = Disposable.Create(excel.GetCell(0, 0)))
     6     {
     7         using (var font = Disposable.Create(range.Value.Font))
     8         {
     9             font.Value.Color = 255;
    10         }
    11 
    12         range.Value.Value = 200;
    13     }
    14 }
    复制代码
    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不能退出的问题总算圆满解决了,贴出来和大家分享一下,免得其他人也在这上面浪费时间

  • 相关阅读:
    安装ActiveMQ
    maven 包冲突的问题(maven helper)
    依赖冲突的原因以及解决办法
    inno setup读取注册表遇到的一个坑
    用格式工厂转换视频为avi格式,视频太大,不好存,想压缩了但不改变画面清晰度,怎么设置参数?
    inno setup 自定义生成的卸载程序
    innosetup 安装前、卸载前判断是否有进程正在运行
    Inno Setup安装卸载时检查程序是否运行提示并中止程序
    trs.getElementsByTagName is not a function 出现原因及解决办法
    一行上自动控制数据长度,并换行
  • 原文地址:https://www.cnblogs.com/spriteflk/p/4754516.html
Copyright © 2020-2023  润新知