Закрыть процесс приложения EXCEL после получения данных

Я пытаюсь получить данные столбцов из файла Excel в списках следующим образом:

private void Form1_Load(object sender, EventArgs e)
        {
            Excel.Application xlApp = new Excel.Application();
            Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"D:/test.xlsx");
            Excel.Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Excel.Range xlRange = xlWorksheet.UsedRange;

            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            List<string> FirstColumnValues = new List<string>();
            List<string> SecondColumnValues = new List<string>();

            for (int row=1; row <= rowCount; row++)
            {
                for (int col = 1; col <= colCount; col++)
                {
                    switch (col)
                    {
                        case 1:
                            FirstColumnValues.Add(xlRange.Cells[row, col].Value2.ToString());
                            break;
                        case 2:
                            SecondColumnValues.Add(xlRange.Cells[row, col].Value2.ToString());
                            break;
                    }
                }
            }

            if (FirstColumnValues.Count != 0 && SecondColumnValues.Count != 0)
            {
                xlWorkbook.Close();
                xlApp.Quit();
                MessageBox.Show("Completed");
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);
                Marshal.ReleaseComObject(xlWorkbook);
                Marshal.ReleaseComObject(xlApp);
                xlApp = null;
            }
        }

Проблема в том, что процесс EXCEL.EXE не закрывается, даже если я перепробовал все, чтобы правильно его закрыть. Я знаю, что здесь размещено много вопросов о правильном закрытии процесса Excel. Но я не профессионал и перепробовал почти все, что можно сделать. Все равно не повезло.

Итак, может ли кто-нибудь сказать мне, что не так с этим кодом? А как процесс можно один раз закрыть, когда все данные хранятся в списках?


person ygssoni    schedule 21.09.2012    source источник


Ответы (4)


Я был здесь раньше. Вот статья, которая очень помогла мне разобраться:

http://devcity.net/Articles/239/1/article.aspx

Excel, кажется, очень упрямо завершает процесс. Скорее всего, вы убьете процесс с помощью System.Diagnostics.Process.

person Steven Hunt    schedule 21.09.2012
comment
Спасибо за ответ, да, я использовал способ System.Diagnostics.Process для закрытия запущенного процесса. Но он закроет весь процесс excel в списках процессов. Есть ли способ закрыть единственный процесс, запущенный new Excel.Application(). ?? - person ygssoni; 21.09.2012
comment
Если вы не возражаете против отслеживания запущенных процессов, вы можете получить список процессов Excel до запуска COM, а затем после него. Это даст вам процесс, связанный с вашим COM-объектом, не запутав его. Вам просто нужно убедиться, что вы не запускаете 2 или более экземпляра COM в отдельных потоках, иначе этот метод не будет работать. - person Steven Hunt; 21.09.2012
comment
О, звучит хорошо. Если я не получу прямого или короткого метода, я обязательно попробую его. Спасибо! :) - person ygssoni; 21.09.2012

Я знаю, что на этот вопрос уже был дан ответ, но я решил поделиться тем, что нашел, пытаясь решить ту же проблему. Надеюсь, кто-то найдет это полезным. Это есть на vb.net, но я уверен, что это можно перевести.

Dim proc As System.Diagnostics.Process
        For Each proc In System.Diagnostics.Process.GetProcessesByName("EXCEL")
            If proc.MainWindowTitle.ToString = "" Then
                proc.Kill()
            End If
        Next

Я обнаружил, что когда я открывал файлы Excel через свое приложение, заголовок окна был нулевым, поэтому вместо того, чтобы закрывать все запущенные процессы Excel, я просто закрывал те, у которых нет заголовка.

Изменить:

Если вы собираетесь завершить процесс, потому что не можете найти оставшиеся переменные, которые не были очищены (практически любая переменная, которую вы установили с помощью двух точек), то по крайней мере уничтожьте правильный экземпляр Excel:

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

...

if (xlApp != null)
{
  GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), ref excelProcessId);

  Process ExcelProc = Process.GetProcessById(excelProcessId);
  if (ExcelProc != null)
  {
    ExcelProc.Kill();
  }

Я не защищаю процессы уничтожения, вы должны правильно очистить их с помощью VSTO Contrib, например:

using (var xlApp = new Microsoft.Office.Interop.Excel.Application().WithComCleanup())
person Lift    schedule 06.11.2012
comment
Почему так? Кажется, это единственный способ завершить процесс. - person Lift; 01.05.2013

Возможно, вы могли бы рассмотреть возможность использования try{}catch{}finally{} и попробовать Marshal.FinalReleaseComObject.

Не уверен, что вы видели этот пост или нет, это также может дать вам некоторое представление.

person woodykiddy    schedule 21.09.2012

Самый простой способ гарантировать, что ваш работающий экземпляр Excel в конечном итоге завершится (т. е. когда ваше приложение выйдет), — это обернуть объект верхнего уровня в блок try/finally.

Excel.Application xlApp;  
try{  
   xlApp = new Excel.Application();  
   ...do some work...  
}  
finally{  
   xlApp.DisableAlerts=True;  
   xlApp.Quit();  
   xlApp.DisableAlerts=False;  
   Marshal.ReleaseComObject(xlApp);  
}  

//If you don't mind having the Excel process kept alive for a while,  
//you don't even have to call ReleaseComObject() on the intermediate objects.  
//The runtime will eventually free the underlying COM objects.  

//If you want to clean up right away, your bookkeeping needs to be more thorough.  
//Every time you access a method or property that returns a runtime callable wrapper  
//(System.__ComObject), you'll need to assign them to a variable  
//and make sure Marshal.ReleaseComObject() is called.  

// More thorough bookkeeping...    
Excel.Application xlApp;  
try{  
   xlApp = new Excel.Application();  

   Excel.Workbooks xlWorkbooks = xlApp.Workbooks;  
   Excel.Workbook xlWorkbook = xlWorkbooks.Open(@"D:/test.xlsx");  
   Excel.Sheets xlSheets = xlWorkbook.Sheets;  
   Excel.Worksheet xlWorksheet = xlSheets[1];  
   Excel.Range xlRange = xlWorksheet.UsedRange;  

   ...inside the loop...  
   Excel.Range xlCell = xlRange.Cells[row, col];  
   FirstColumnValues.Add(xlCell.Value2.ToString());  
   Marshal.ReleaseComObject(xlCell);  

   ...inside the loop...  
   Excel.Range xlCell = xlRange.Cells[row, col];  
   SecondColumnValues.Add(xlCell.Value2.ToString());  
   Marshal.ReleaseComObject(xlCell);  

   ...do more work...  

   ...clean up...  
   Marshal.ReleaseComObject(xlRange);  
   Marshal.ReleaseComObject(xlWorksheet);  
   Marshal.ReleaseComObject(xlSheets);  
   Marshal.ReleaseComObject(xlWorkbook);  
   Marshal.ReleaseComObject(xlWorkbooks);  
}  
finally{  
   xlApp.DisableAlerts=True;  
   xlApp.Quit();  
   xlApp.DisableAlerts=False;  
   Marshal.ReleaseComObject(xlApp);  
}  
person bricklayer137    schedule 15.03.2013