【问题标题】:Killing EXCEL.exe Process from C# in a Windows Service在 Windows 服务中从 C# 中杀死 EXCEL.exe 进程
【发布时间】:2011-04-11 21:04:55
【问题描述】:

我有一个 Windows 服务,它通过 Microsoft.Office.Interop.Excel.Application 对象打开一个 Excel 电子表格。

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 服务执行此操作有任何想法吗?

【问题讨论】:

  • 我在一个内部网站上考虑过这样做,但出于这个原因放弃了。

标签: excel windows-services office-interop office-automation


【解决方案1】:

正确关闭打开的 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 的实例通过其他程序运行。

编辑:请参阅this question 了解更多信息。

【讨论】:

  • 感谢您为我指明正确的方向。我使用了您的代码以及该帖子中的一些其他提示,现在 Excel 进程正在正确关闭。
【解决方案2】:

您需要检查文件句柄并获取由进程打开的 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;
}

【讨论】:

  • 你的答案值得拥有金子!!!几个月来我一直在努力解决这个问题!!!
【解决方案3】:

经过大量阅读和挫折,我找到了解决方案!

所有功劳归于dotNetkownightcoderMike Rosenblum,感谢他们在这篇文章中的解决方案:How do I properly clean up Excel interop objects?

这就是我所做的...
1.将项目的构建模式更改为“发布”(在DEBUG模式下,COM对象很难处理它们的引用。
2.删除所有双点表达式(所有COM对象应绑定到一个变量,以便它们可以被释放)
3. 在 finally 块中显式调用 GC.Collect()、GC.WaitForPendingFinalizers() 和 Marshal.FinalReleaseComObject()

这是我正在使用的实际代码:

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);
    }
}

【讨论】:

    【解决方案4】:

    我不知道我的答案是否不是您要搜索的内容...如果是,请告诉我,我会删除它。反正我用这个:

    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 32 位和 Microsoft Office 2007。

    在使用此测试应用程序之前,我还尝试打开另一个 excel 文件:第二个 EXCEL.EXE 被打开并(使用 Quit)最后关闭,而第一个实例保持不变。

    【讨论】:

    • 最好同时设置Application.DisplayAlerts = False 以防止(大多数)阻止对话框出现。
    • 我试了一下,它并没有杀死 Excel 实例。该服务在带有 Office 2010 的 Windows Server 2008 上运行。
    • @thiag0:我看到 Excel.exe 通常不是在退出后立即关闭,而是在退出我调用 Quit() 的方法后。只是为了确保我也使用了 GC.Collect(),但它可能很贵......
    【解决方案5】:

    我使用了一个简单但有效的解决方案

    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 进程。然后获取我的 excelApplication 的进程 ID。仅杀死 id 匹配的进程。 用于在类中声明GetWindowThreadProcessId:

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

    【讨论】:

    • 你读过接受的答案吗?您的解决方案可能会关闭对我们不好的用户的 excel。公认的解决方案也有效,并避免了这个问题。您认为这些答案有何贡献?
    • @vidstige 就我个人而言,这个答案提供了唯一有效的解决方案。我有一个非常基本的应用程序,可以在 Windows Server 2008R2 64 位上自动执行 Access 2000。我已确保我遵循所有可能的其他推荐指南,但我的流程并没有关闭。使用这种方法,我可以使用 PID 来不断关闭我需要的实例。
    • @Safrin,希望我能给你更多的 +1。非常感谢您抽出宝贵时间。 :) 我对这个该死的自动化简直要疯了。
    • 解决方法不对。 Excel 进程将保持活动状态,直到调用 Marshal.FinalReleaseComObject(xlApp);。正因为如此,你总是在强行杀死它,甚至在它被优雅地释放之前。我建议修改您的答案并切换代码行。另外,根据我自己的经验,尽管您以应有的方式发布所有内容,但通过互操作运行的宏代码可能会留下一些东西,这将阻止您的应用程序退出。如果您可以将其应用于您对 Excel 的特定用途(考虑到其他 Excel 用户),那么拥有此备份系统是一件好事。
    【解决方案6】:

    我的解决方案

    [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();
    }
    

    【讨论】:

    • "user32.dll" 如果是,它是一个 dll 名称,那么我从哪里得到这个?
    【解决方案7】:

    以下是打开和删除 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);
    
    
    
            }
    

    【讨论】:

      【解决方案8】:

      我会使用 Process.GetProcess 并在那里寻找 exe 我不相信与服务世界中的窗口有关的任何事情,因为我认为窗口是在您无权访问的桌面上创建的。

      【讨论】:

      • 感谢您的回复。我可以通过带有 Process.GetProcessByName("EXCEL") 的 foreach 循环杀死它,但是这个 Windows 服务是多线程的,所以我必须杀死正确的实例。
      【解决方案9】:

      我正在使用:

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

      杀死进程。

      【讨论】:

        【解决方案10】:

        也许它不是那么优雅,但我最终得到了公认的解决方案和 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);
                }
            }
        

        【讨论】:

          【解决方案11】:

          这是我杀死所有未使用的 Excel 进程的代码

          Process[] process = Process.GetProcessesByName("excel");
                  foreach (Process excel in process)
                  {
                      if (excel.HasExited)
                      {
                          excel.Kill();
                      }
                  }
                  process = null;
          

          【讨论】:

            【解决方案12】:

            这就是我正在做的事情.. 可能会过度杀戮.. 但对我来说效果很好。

                [DllImport("user32.dll", SetLastError = true)]
                static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
            
                private void KillProcess(uint 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;
                }
                public void ReleaseObject(object obj) {
                    try {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                        obj = null;
                    }
                    catch (Exception ex) {
                        obj = null;
                        MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
                    }
                    finally { GC.Collect(); }
                }
            
            
                                    xlWorkBook.Save();
                                    xlWorkBook.Close();
                                    xlApp.Quit();
            
                                    uint pid;
                                    HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
                                    GetWindowThreadProcessId((IntPtr)xlApp.Hwnd, out pid);
                                    //GetWindowThreadProcessId(hwnd, out pid);
            
                                    KillProcess(pid, "EXCEL");
            
                                    ReleaseObject(worksheets);
                                    ReleaseObject(xlWorkBook);
                                    ReleaseObject(xlApp);
            

            【讨论】:

              【解决方案13】:

              要杀死当时用于您的应用程序的确切 Excel 进程,首先通过在方法 1(可能是主要方法)中的保存和关闭说明下输入以下代码来识别其 PID:

                  int pid = -1;
                  HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
                  GetWindowThreadProcessId(hwnd, out pid);
              
                  KillProcess(pid, "EXCEL");
              

              在上面的方法1下面再输入这个新方法:

              [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
              public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);
              static public void KillProcess(int pid, string processName)
              {
                  // to kill current process of excel
                  Process[] AllProcesses = Process.GetProcessesByName(processName);
                  foreach (Process process in AllProcesses)
                  {
                      if (process.Id == pid)
                      {
                          process.Kill();
                      }
                  }
                  AllProcesses = null;
              }
              

              所以所有代码在更大的区域视图中都是这样的:

              public CopyPaste2()
              {
                  srcWb= @"C:\WIP\sourceWB.xlsm";
                  destWb= @"C:\WIP\destinationWB.xlsm";
              
                  Application xlApp = new Application();
                  xlApp.Visible = true;
              
                  Workbook strSrcWb= xlApp.Workbooks.Open(srcWb, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                  Workbook strDestWb= xlApp.Workbooks.Open(destWb, 0, false, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
              
                  Worksheet srcWs = strSrcWb.Worksheets.get_Item("Sheet1");
                  Worksheet destWs = strDestWb.Worksheets.get_Item("Sheet1");
              
                  ... rest of the executive methods ...
              
                  strDestWb.Save();
                  strSrcWb.Close();
                  strDestWb.Close();
                  xlApp.Quit();
              
                  int pid = -1;
                  HandleRef hwnd = new HandleRef(xlApp, (IntPtr)xlApp.Hwnd);
                  GetWindowThreadProcessId(hwnd, out pid);
              
                  KillProcess(pid, "EXCEL");
              }
              
              
              [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
              public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);
              static public void KillProcess(int pid, string processName)
              {
                  // to kill current process of excel
                  Process[] AllProcesses = Process.GetProcessesByName(processName);
                  foreach (Process process in AllProcesses)
                  {
                      if (process.Id == pid)
                      {
                          process.Kill();
                      }
                  }
                  AllProcesses = null;
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-09-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-08-08
                • 1970-01-01
                相关资源
                最近更新 更多