9

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

4

13 回答 13

11

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

编辑:有关更多信息,请参阅此问题

于 2011-04-12T13:52:07.473 回答
8

您需要检查文件句柄并获取由进程打开的 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;
}
于 2012-10-06T09:20:24.863 回答
6

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

所有功劳归功于dotNetkownightcoderMike Rosenblum在这篇文章中的解决方案:如何正确清理 Excel 互操作对象?

这是我所做的...
1. 将项目的构建模式更改为“发布”(在调试模式下,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);
    }
}
于 2011-04-12T15:34:51.843 回答
3

我不知道我的答案是否不是您要寻找的……如果是,请告诉我,我会删除它。无论如何我用这个:

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)最后关闭,而第一个实例保持不变。

于 2011-04-11T21:20:09.617 回答
3

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

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);
于 2011-11-19T09:39:03.733 回答
2

我的解决方案

[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();
}
于 2014-06-18T09:22:42.183 回答
1

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



        }
于 2012-09-27T07:08:41.310 回答
0

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

于 2011-04-11T21:12:42.303 回答
0

我在用:

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

杀死进程。

于 2014-08-08T03:49:53.833 回答
0

也许它不是那么优雅,但我最终得到了公认的解决方案和 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);
        }
    }
于 2014-10-31T16:42:57.420 回答
0

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

Process[] process = Process.GetProcessesByName("excel");
        foreach (Process excel in process)
        {
            if (excel.HasExited)
            {
                excel.Kill();
            }
        }
        process = null;
于 2016-03-16T05:45:25.657 回答
0

这就是我正在做的......也许过度杀戮......但对我来说效果很好。

    [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);
于 2020-04-01T12:41:59.527 回答
0

要杀死当时用于您的应用程序的确切 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;
}
于 2021-09-06T09:15:45.870 回答