Excel互操作COM不关闭

我有一些代码可以打开电子表格,读取一些值,然后关闭表格。 我需要为多个文件执行此操作。 我遇到的问题是Excel应用程序实例没有退出,因此当我为多个文件运行我的进程时,最终会运行多个excel.exe进程。 任何想法如何让Excel关闭?

    static void ParseFile(string file)
    {
        try
        {
            System.Console.WriteLine("parsing:" + file);
            Excel.Application excel = new Excel.Application();
            Excel.Workbook wb = excel.Workbooks.Open(file);
            Excel.Worksheet ws = wb.Worksheets[1];
            for (int i = 2; i < 27; i++)
            {
                log(ws.Cells[i, 1].Text);
            }
            wb.Close(false);                
            excel.Quit();
            excel = null;
            ws = null;
            wb = null;
        }
        catch (Exception ex)
        {
            log(ex.Message);
        }
    }

------更新12/11/12 --------仍然打开excel实例-------

static void ParseFile(string file)
    {
        try
        {
            log("parsing:" + file);
            Excel.Application excel = new Excel.Application();
            Excel.Workbook wb = excel.Workbooks.Open(file);
            Excel.Worksheet ws = wb.Worksheets[1];
    //do some stuff here
            wb.Close(false);
            excel.Quit();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Marshal.FinalReleaseComObject(ws);
            Marshal.FinalReleaseComObject(wb);
            Marshal.FinalReleaseComObject(excel);                
            excel = null;
            ws = null;
            wb = null;
            System.GC.Collect();
        }
        catch (Exception ex)
        {
            log(ex.Message);
        }            
    }

  • 使用Marshal.FinalReleaseComObject()释放资源

  • 如果您一个接一个地读取多个文件,那么性能方面最好只打开/关闭Excel应用程序一次,并将打开/关闭应用程序部分移动到单独的函数中

  • 重构代码:

    static Excel.Application OpenExcel(){
        Excel.Application excel = null;
        try{
            excel = new Excel.Application();
            return excel;
        }
        catch(Exception ex){
            log(ex.Message);
            return null;
        }
    }
    
    static void ParseFile(string file)
    {
        try
        {
            System.Console.WriteLine("parsing:" + file);            
            Excel.Workbook wb = excel.Workbooks.Open(file);
            Excel.Worksheet ws = wb.Worksheets[1];
            for (int i = 2; i < 27; i++)
            {
                log(ws.Cells[i, 1].Text);
            }
            wb.Close(false);    
        }
        catch (Exception ex)
        {
            log(ex.Message);
        }
        finally{
            Marshal.FinalReleaseComObject(ws);
            Marshal.FinalReleaseComObject(wb);
            ws = null;
            wb = null;
        }
    }
    
    static void CloseExcel(Excel.Application excel){
        try{
            excel.Quit();
        }
        catch(Exception ex){
            log(ex.Message);
        }
        finally{
            Marshal.FinalReleaseComObject(excel);
            excel = null;
        }
    }
    

    用法:

    Excel.Application excel = OpenExcel();
    if(excel != null){
        // Parse files in a loop
        ParseFile("fileName");
    
        // Close excel after parsing all files
        CloseExcel(excel);
    }
    

    您可以在实现IDisposable的实际COM对象周围使用包装器对象,以便它可以与C#的using语句一起using

    这样做的好处是它可以促进可读代码,并且可以用于任何COM对象。 以下是运行时分派的一个例子:

    using System;
    using System.Reflection;
    using System.Runtime.InteropServices;
    
    public class ComRef<T> : IDisposable where T : class
    {
        private T reference;
    
        public T Reference
        {
            get
            {
                return reference;
            }
        }
    
        public ComRef(T o)
        {
            reference = o;
        }
    
        public void Dispose()
        {
            if (reference != null)
            {
                Marshal.ReleaseComObject(reference);
                reference = null;
            }
        }
    }
    
    public class Test
    {
        static void Main()
        {
            Type excelAppType = Type.GetTypeFromProgID("Excel.Application");
            using (var comRef = new ComRef<object>(Activator.CreateInstance(excelAppType)))
            {
                var excel = comRef.Reference;
                // ...
                excel.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, excel, null);
            }
        }
    }
    

    但是,如果您已经导入了Excel的类型库或任何其他类型的库,那么您可能需要一些更友好的东西:

    public class CoClassComRef<T> : ComRef<T> where T : class, new()
    {
        public CoClassComRef() : base(new T())
        {
        }
    }
    
    public class Test
    {
        static void Main()
        {
            using (var comRef = new CoClassComRef<Excel.Application>())
            {
                var excel = comRef.Reference;
                // ...
                excel.Quit();
            }
        }
    }
    

    你应该确保你没有捕获comRef.Reference到一些comRef.Reference using语句的字段或变量。

    请注意,我没有考虑线程安全性和适当的Dispose实现。 如果只使用带有using语句的ComRef ,则线程安全并不重要。 一个适当的Dispose实现将与一个终结器协同工作,但这里没有必要这样做,因为using基本上是一个try-finally 。 如果您在using语句中不使用ComRef并且未调用Dispose ,则ComRef将简单地进行垃圾回收,并使用其基础COM对象,只有此ComRef引用该对象时才会释放该对象。

    最后,我没有使用Marshal.FinalReleaseComObject当你完全确定要释放潜在的COM对象,因为这是由(至少,从管理的环境中的所有引用),无论多少次它(重新)进入了托管世界。 然而,如果你感到幸运,你可以创建一个新的构造函数,该构造函数也会接收一个布尔值,声明是否应该调用FinalReleaseComObject而不是ReleaseComObject 。 在网络搜索任何这些方法的第一个结果将指向文章和博客文章,详细说明为什么他们通常是邪恶的,比其他更多。


    结束了杀死进程,这是唯一有效的工作。

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    
        /// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary>
        /// <param name="hWnd">Handle to the main window of the process.</param>
        /// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns>
        public static bool TryKillProcessByMainWindowHwnd(int hWnd)
        {
            uint processID;
            GetWindowThreadProcessId((IntPtr)hWnd, out processID);
            if (processID == 0) return false;
            try
            {
                Process.GetProcessById((int)processID).Kill();
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (Exception ex)
            {
                return false;
            }            
            return true;
        }
    
        static void ParseFile(string file)
        {
            try
            {
                log("parsing:" + file);
                Excel.Application excel = new Excel.Application();
                Excel.Workbook wb = excel.Workbooks.Open(file);
                Excel.Worksheet ws = wb.Worksheets[1];
                //do some stuff here
                wb.Close(false);
                int hWnd = excel.Application.Hwnd;
                excel.Quit();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                Marshal.FinalReleaseComObject(ws);
                Marshal.FinalReleaseComObject(wb);
                Marshal.FinalReleaseComObject(excel);                
                excel = null;
                ws = null;
                wb = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                TryKillProcessByMainWindowHwnd(hWnd);
            }
            catch (Exception ex)
            {
                log(ex.Message);
            }            
        }
    
    链接地址: http://www.djcxy.com/p/35679.html

    上一篇: Excel interop COM doesn't close

    下一篇: Enabled Workbook Doesn't Close & Reopen Properly