Уничтожение процесса EXCEL.exe из C # в службе Windows - PullRequest
9 голосов
/ 12 апреля 2011

У меня есть служба Windows, которая открывает электронную таблицу Excel через объект Microsoft.Office.Interop.Excel.Application.

Application xlApp = new Application();
Workbook workbook = xlApp.Workbooks.Open(fileName, 2, false);
...
...
workbook.Close();
xlApp.Quit();

Я хотел бы завершить процесс EXCEL.exe, который остается запущенным после того, как он завершил работу с книгой.

Я попробовал следующее безуспешно ...

// This returns a processId of 0
IntPtr processId;
GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), out processId);
Process p = Process.GetProcessById(processId.ToInt32());   
p.Kill();

У кого-нибудь есть идеи относительно того, как я могу сделать это с помощью службы Windows?

Ответы [ 11 ]

10 голосов
/ 12 апреля 2011

Правильно закрыть открытую книгу Excel и выйти из приложения крайне сложно.Если я смогу найти ссылки, я опубликую их, но по сути вы должны очистить все ссылки на любой созданный вами COM-объект .Это включает в себя все, от ODBCConnections (подключения к данным), рабочих таблиц, рабочих книг и приложения Excel.Комбинация, которую я получил, включала сборку мусора и объект System.Runtime.InteropServices.Marshal:

// Garbage collecting
GC.Collect();
GC.WaitForPendingFinalizers();
// Clean up references to all COM objects
// As per above, you're just using a Workbook and Excel Application instance, so release them:
workbook.Close(false, Missing.Value, Missing.Value);
xlApp.Quit();
Marshal.FinalReleaseComObject(workbook);
Marshal.FinalReleaseComObject(xlApp);

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

Редактировать: см. этот вопрос для получения дополнительной информации..

7 голосов
/ 06 октября 2012

Вам нужно проверить дескрипторы файлов и получить PID, которые открываются процессом, а затем уничтожить его. У меня это сработало.

private void genExcel(
{
   int pid = -1;
   //Get PID
   xlApp = new Excel.Application();
   HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
   GetWindowThreadProcessId(hwnd, out pid);
   .
   .
   .
   .
   //Finally
   KillProcess(pid,"EXCEL");
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);

private void KillProcess(int pid, string processName)
{
    // to kill current process of excel
    System.Diagnostics.Process[] AllProcesses = System.Diagnostics.Process.GetProcessesByName(processName);
    foreach (System.Diagnostics.Process process in AllProcesses)
    {
       if (process.Id == pid)
       {
         process.Kill();
       }
    }
    AllProcesses = null;
}
6 голосов
/ 12 апреля 2011

После долгих чтений и разочарований я нашел решение!

Вся заслуга dotNetkow , nightcoder и Mike Rosenblum за их решения в этом посте: Как правильно очистить объекты взаимодействия Excel ?

Вот что я сделал ...
1. Изменен режим сборки проекта на «Release» (в режиме отладки COM-объектам сложно избавиться от своих ссылок.
2. Убраны все выражения двойных точек (все COM-объекты должны быть связаны с переменной, чтобы их можно было освобождать)
3. Вызов GC.Collect (), GC.WaitForPendingFinalizers () и Marshal.FinalReleaseComObject () явно в блоке finally

Вот код, который я использую:

Application xlApp = null;
Workbooks workbooks = null;
Workbook workbook = null;
Worksheet sheet = null;
Range r = null;
object obj = null;

try
{
    xlApp = new Application();
    xlApp.DisplayAlerts = false;
    xlApp.AskToUpdateLinks = false;
    workbooks = xlApp.Workbooks;
    workbook = workbooks.Open(fileName, 2, false);
    sheet = workbook.Worksheets[1];

    r = sheet.get_Range("F19");
    obj = r.get_Value(XlRangeValueDataType.xlRangeValueDefault);
}
finally
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    if (value != null) Marshal.FinalReleaseComObject(value);
    if (r != null) Marshal.FinalReleaseComObject(r);
    if (sheet != null) Marshal.FinalReleaseComObject(sheet);
    if (workbooks != null) Marshal.FinalReleaseComObject(workbooks);
    if (workbook != null)
    {
        workbook.Close(Type.Missing, Type.Missing, Type.Missing);
        Marshal.FinalReleaseComObject(workbook);
    }
    if (xlApp != null)
    {
        xlApp.Quit();
        Marshal.FinalReleaseComObject(xlApp);
    }
}
3 голосов
/ 19 ноября 2011

Я использовал простое, но эффективное решение

finally   { 
GC.Collect();
GC.WaitForPendingFinalizers();           
        if (xlApp != null)
            {
                xlApp .Quit();
                int hWnd = xlApp .Application.Hwnd;
                uint processID;GetWindowThreadProcessId((IntPtr)hWnd, out processID);
                Process[] procs = Process.GetProcessesByName("EXCEL");
                foreach (Process p in procs)
                {
                    if (p.Id == processID)
                        p.Kill();
                }
                Marshal.FinalReleaseComObject(xlApp );
            } 
        }

Найти все процессы Excell.exe.затем получите идентификатор процесса моего приложения Excel.убить только процесс, чей идентификатор совпадает.Используйте для объявления GetWindowThreadProcessId в классе:

[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
3 голосов
/ 12 апреля 2011

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

Application xlApp = new Application();
xlApp.DisplayAlerts = false;
xlApp.Visible = true; // Only for debug purposes
Workbook workbook = xlApp.Workbooks.Open(filename, 2, false);
...
...
workbook.Close();
xlApp.Quit();

Закрытие книги и выход из xlApp удаляет EXCEL.EXE из памяти на моем компьютере.Я использую Windows XP 32bit и Microsoft Office 2007.

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

2 голосов
/ 18 июня 2014

Мое решение

[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

private void GenerateExcel()
{
    var excel = new Microsoft.Office.Interop.Excel.Application();
    int id;
    // Find the Process Id
    GetWindowThreadProcessId(excel.Hwnd, out id);
    Process excelProcess = Process.GetProcessById(id);

try
{
    // Your code
}
finally
{
    excel.Quit();

    // Kill him !
    excelProcess.Kill();
}
1 голос
/ 27 сентября 2012

Ниже приведен код, который открывает и удаляет экземпляр Excel.Нам просто нужно убедиться, что все объекты, связанные с Excel, закрыты.

    string strFilePath = @"C:\Sample.xlsx";
        try
        {
            Excel.Application excelApp = null;
        Excel.Workbook excelWorkbook = null;
        Excel.Sheets excelSheets = null;
        Excel.Worksheet excelWorksheet = null;
        Excel.Workbooks excelWorkbooks = null;
        Excel.Range excelUsedRange = null;



            excelApp = new Microsoft.Office.Interop.Excel.Application();
            int nData = excelApp.Hwnd;
            // excelApp = new Excel.ApplicationClass();
            //excelApp.Visible = true;
            excelWorkbooks = excelApp.Workbooks;
            excelWorkbook = excelWorkbooks.Add(System.Reflection.Missing.Value);

            excelWorkbook = excelApp.Workbooks.Open(strFilePath, 2, false);
            //excelWorkbook = excelApp.Workbooks.Open(strFilePath,
            //                                                                                       
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing,
            //                                                                                    
               Type.Missing, Type.Missing);


            excelSheets = excelWorkbook.Worksheets;
           // excelWorksheet = (Excel.Worksheet)excelSheets.get_Item(1);
            excelWorksheet = (Excel.Worksheet)excelWorkbook.Worksheets["Dem0_1"];



            excelUsedRange = excelWorksheet.UsedRange;


            //Excel.Range lastCell = usedRange.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
            //int lastRow = lastCell.Row;
            //int lastCol = lastCell.Column;
            //int rowMin = lastRow + 1;
            //int colMin = lastCol + 1;

            int nRowsCount = excelUsedRange.Rows.Count;
            int nColCount = excelUsedRange.Columns.Count;



             int N_Quality_Header = -1;
             int N_Measurement_Name = -1;
             int N_Lower_Tolerance = -1;
             int N_Upper_Tolerance = -1;


             //Read the Columns Index 
             for (int nColIndex = 1; nColIndex <= nColCount; nColIndex++)
             {
                 Excel.Range cell = usedRange.Cells[1, nColIndex] as Excel.Range;
                 String strCellValue = cell.Value2.ToString();
                 if (strCellValue == "Quality Header")
                     N_Quality_Header = nColIndex;

                 else if (strCellValue.IndexOf("Measurement Name", StringComparison.OrdinalIgnoreCase) > -1)
                     N_Measurement_Name = nColIndex;
                 else if (strCellValue.IndexOf("Lower Tolerance", StringComparison.OrdinalIgnoreCase) > -1)
                     N_Lower_Tolerance = nColIndex;
                 else if (strCellValue.IndexOf("Upper Tolerance", StringComparison.OrdinalIgnoreCase) > -1)
                     N_Upper_Tolerance = nColIndex;
             }

             //Read all rows to get the values
             for (int nRowIndex = 2; nRowIndex <= nRowsCount; nRowIndex++)
             {
                 Excel.Range cellQualityHeader = usedRange.Cells[nRowIndex, N_Quality_Header] as Excel.Range;
                 String strValue = cellQualityHeader.Value2.ToString();
                 if (strValue == String_Empty)
                     continue;


             }


        }
        catch (Exception oException)
        {


        }
        finally
        {
            excelUsedRange.Clear();
            //excelWorkbook.Save();
            excelWorkbook.Close(false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);

            excelWorkbooks.Close();
            excelApp.Quit();

            Marshal.ReleaseComObject(excelUsedRange);
            Marshal.ReleaseComObject(excelWorksheet);
            Marshal.ReleaseComObject(excelSheets);
            Marshal.ReleaseComObject(excelWorkbooks);
            Marshal.ReleaseComObject(excelWorkbook);
            Marshal.ReleaseComObject(excelApp);


            excelUsedRange = null;
            excelWorksheet = null;
            excelSheets = null;
            excelWorkbooks = null;
            excelWorkbook = null;
            excelApp = null;

            GC.GetTotalMemory(false);
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.GetTotalMemory(true);



        }
0 голосов
/ 16 марта 2016

Это мой код, чтобы убить все неиспользуемые процессы Excel

Process[] process = Process.GetProcessesByName("excel");
        foreach (Process excel in process)
        {
            if (excel.HasExited)
            {
                excel.Kill();
            }
        }
        process = null;
0 голосов
/ 31 октября 2014

Возможно, это не так уж и элегантно, но в итоге я получил комбинацию принятого решения и решения Safrin. Итак, сначала я пытаюсь сделать это элегантным способом, и если это не удается, я использую грубую силу. Причина в том, что код является частью пакетной процедуры, которая должна быть в состоянии продолжаться, даже если одна операция обновления Excel завершается неудачно. Моя проблема заключалась в том, что некоторые сбои были связаны с ошибками в модели PowerPivot, которая вызвала диалог с сообщением об ошибке. Это диалоговое окно не было видно, потому что оно запускается как фоновый процесс, и кажется, что Excel не закроется, и мой процесс не продолжится, пока диалоговое окно не будет закрыто (?!). Поэтому запуск процесса в отдельном потоке с механизмом тайм-аута и уничтожение Excel при утилизации моего рабочего объекта, если выход из него не работает, было единственным решением, о котором я мог думать (это работает) ...

    public void Dispose()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        if (workbook != null)
        {
            try
            {
                workbook.Close(false);
                Marshal.FinalReleaseComObject(workbook);
            }
            catch { }
        }
        if (excel != null)
        {
            try { excel.Quit(); }
            catch {
                int hWnd = excel.Application.Hwnd;
                uint processID; 
                GetWindowThreadProcessId((IntPtr)hWnd, out processID);
                Process[] procs = Process.GetProcessesByName("EXCEL");
                foreach (Process p in procs)
                {
                    if (p.Id == processID) p.Kill();
                }

            }
            Marshal.FinalReleaseComObject(excel);
        }
    }
0 голосов
/ 08 августа 2014

Я использую:

Process[] AllProcesses = Process.GetProcessesByName("EXCEL.EXE");

, чтобы убить процесс.

...