数据访问后在 C # 中关闭 Excel 应用程序进程

我正在用 C # 编写一个应用程序,它打开一个用于读/写操作的 Excel 模板文件。我想当用户关闭应用程序,Excel 应用程序进程已经关闭,没有保存 Excel 文件。在多次运行应用程序后,请查看我的任务管理器。

enter image description here

我用这段代码打开 Excel 文件:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbook excelBook;
excelBook = excelApp.Workbooks.Add(@"C:/pape.xltx");

对于数据访问,我使用以下代码:

Excel.Worksheet excelSheet = (Worksheet)(excelBook.Worksheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

我看到类似的问题堆栈溢出,如 这个问题这个,并测试答案,但它不工作。

190370 次浏览

Think of this, it kills the process:

System.Diagnostics.Process[] process=System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}

Also, did you try just close it normally?

myWorkbook.SaveAs(@"C:/pape.xltx", missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing, missing);
excelBook.Close(null, null, null);                 // close your workbook
excelApp.Quit();                                   // exit excel application
excel = null;                                      // set to NULL
excelBook.Close();
excelApp.Quit();

add end of the code, it could be enough. it is working on my code

Try this:

excelBook.Close(0);
excelApp.Quit();

When closing the work-book, you have three optional parameters:

Workbook.close SaveChanges, filename, routeworkbook

Workbook.Close(false) or if you are doing late binding, it sometimes is easier to use zero Workbook.Close(0) That is how I've done it when automating closing of workbooks.

Also I went and looked up the documentation for it, and found it here: Excel Workbook Close

Killing Excel is not always easy; see this article: 50 Ways to Kill Excel

This article takes the best advice from Microsoft (MS Knowlege Base Article) on how to get Excel to quit nicely, but then also makes sure about it by killing the process if necessary. I like having a second parachute.

Make sure to Close any open workbooks, Quit the application and Release the xlApp object. Finally check to see if the process is still alive and if so then kill it.

This article also makes sure that we don't kill all Excel processes but only kills the exact process that was started.

See also Get Process from Window Handle

Here is the code I use: (works every time)

Sub UsingExcel()


'declare process; will be used later to attach the Excel process
Dim XLProc As Process


'call the sub that will do some work with Excel
'calling Excel in a separate routine will ensure that it is
'out of scope when calling GC.Collect
'this works better especially in debug mode
DoOfficeWork(XLProc)


'Do garbage collection to release the COM pointers
'http://support.microsoft.com/kb/317109
GC.Collect()
GC.WaitForPendingFinalizers()


'I prefer to have two parachutes when dealing with the Excel process
'this is the last answer if garbage collection were to fail
If Not XLProc Is Nothing AndAlso Not XLProc.HasExited Then
XLProc.Kill()
End If


End Sub


'http://msdn.microsoft.com/en-us/library/ms633522%28v=vs.85%29.aspx
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
End Function


Private Sub ExcelWork(ByRef XLProc As Process)


'start the application using late binding
Dim xlApp As Object = CreateObject("Excel.Application")


'or use early binding
'Dim xlApp As Microsoft.Office.Interop.Excel


'get the window handle
Dim xlHWND As Integer = xlApp.hwnd


'this will have the process ID after call to GetWindowThreadProcessId
Dim ProcIdXL As Integer = 0


'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIdXL)


'get the process
XLProc = Process.GetProcessById(ProcIdXL)




'do some work with Excel here using xlApp


'be sure to save and close all workbooks when done


'release all objects used (except xlApp) using NAR(x)




'Quit Excel
xlApp.quit()


'Release
NAR(xlApp)


End Sub


Private Sub NAR(ByVal o As Object)
'http://support.microsoft.com/kb/317109
Try
While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
End While
Catch
Finally
o = Nothing
End Try
End Sub
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

try this.. it worked for me... you should release that xl application object to stop the process.

Ref: https://stackoverflow.com/a/17367570/132599

Avoid using double-dot-calling expressions, such as this:

var workbook = excel.Workbooks.Open(/*params*/)

...because in this way you create RCW objects not only for workbook, but for Workbooks, and you should release it too (which is not possible if a reference to the object is not maintained).

This resolved the issue for me. Your code becomes:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbooks workbooks;
public Excel.Workbook excelBook;
workbooks = excelApp.Workbooks;
excelBook = workbooks.Add(@"C:/pape.xltx");


...


Excel.Sheets sheets = excelBook.Worksheets;
Excel.Worksheet excelSheet = (Worksheet)(sheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

And then release all those objects:

System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
excelBook .Save();
excelBook .Close(true);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

I wrap this in a try {} finally {} to ensure everything gets released even if something goes wrong (what could possibly go wrong?) e.g.

public Excel.Application excelApp = null;
public Excel.Workbooks workbooks = null;
...
try
{
excelApp = new Excel.Application();
workbooks = excelApp.Workbooks;
...
}
finally
{
...
if (workbooks != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
}
         wb.Close();
app.Quit();


System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName) && p.StartTime.AddSeconds(+10) > DateTime.Now)
{
try
{
p.Kill();
}
catch { }
}
}

It Closes last 10 sec process with name "Excel"

        GetWindowThreadProcessId((IntPtr)app.Hwnd, out iProcessId);
wb.Close(true,Missing.Value,Missing.Value);
app.Quit();
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (p.Id == iProcessId)
{
try
{
p.Kill();
}
catch { }
}
}
}
[DllImport("user32.dll")]


private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);


uint iProcessId = 0;

this GetWindowThreadProcessId finds the correct Process Id o excell .... After kills it.... Enjoy It!!!

The right way to close all excel process

var _excel = new Application();
foreach (Workbook _workbook in _excel.Workbooks) {
_workbook.Close(0);
}


_excel.Quit();
_excel = null;

Using process example, this may close all the excel process regardless.

var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process) {
if (!string.IsNullOrEmpty(p.ProcessName)) {
try {
p.Kill();
} catch { }
}
}

I met the same problems and tried many methods to solve it but doesn't work. Finally , I found the by my way. Some reference enter link description here

Hope my code can help someone future. I have been spent more than two days to solve it. Below is my Code:

//get current in useing excel
Process[] excelProcsOld = Process.GetProcessesByName("EXCEL");
Excel.Application myExcelApp = null;
Excel.Workbooks excelWorkbookTemplate = null;
Excel.Workbook excelWorkbook = null;
try{
//DO sth using myExcelApp , excelWorkbookTemplate, excelWorkbook
}
catch (Exception ex ){
}
finally
{
//Compare the EXCEL ID and Kill it
Process[] excelProcsNew = Process.GetProcessesByName("EXCEL");
foreach (Process procNew in excelProcsNew)
{
int exist = 0;
foreach (Process procOld in excelProcsOld)
{
if (procNew.Id == procOld.Id)
{
exist++;
}
}
if (exist == 0)
{
procNew.Kill();
}
}
}

Based on another solutions. I have use this:

IntPtr xAsIntPtr = new IntPtr(excelObj.Application.Hwnd);
excelObj.ActiveWorkbook.Close();


System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (p.MainWindowHandle == xAsIntPtr)
{
try
{
p.Kill();
}
catch { }
}
}

Using the "MainWindowHandle" to identify the process and close him.

excelObj: This is my Application Interop excel objecto

You may kill process with your own COM object excel pid

add somewhere below dll import code

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

and use

 if (excelApp != null)
{
int excelProcessId = -1;
GetWindowThreadProcessId(new IntPtr(excelApp.Hwnd), ref excelProcessId);


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

Use a variable for each Excel object and must loop Marshal.ReleaseComObject >0. Without the loop, Excel process still remain active.

public class test{
private dynamic ExcelObject;
protected dynamic ExcelBook;
protected dynamic ExcelBooks;
protected dynamic ExcelSheet;


public void LoadExcel(string FileName)
{
Type t = Type.GetTypeFromProgID("Excel.Application");
if (t == null) throw new Exception("Excel non installato");
ExcelObject = System.Activator.CreateInstance(t);
ExcelObject.Visible = false;
ExcelObject.DisplayAlerts = false;
ExcelObject.AskToUpdateLinks = false;
ExcelBooks = ExcelObject.Workbooks;
ExcelBook = ExcelBooks.Open(FileName,0,true);
System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
ExcelSheet = ExcelBook.Sheets[1];
}
private void ReleaseObj(object obj)
{
try
{
int i = 0;
while(   System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0)
{
i++;
if (i > 1000) break;
}
obj = null;
}
catch
{
obj = null;
}
finally
{
GC.Collect();
}
}
public void ChiudiExcel() {
System.Threading.Thread.CurrentThread.CurrentCulture = ci;


ReleaseObj(ExcelSheet);
try { ExcelBook.Close(); } catch { }
try { ExcelBooks.Close(); } catch { }
ReleaseObj(ExcelBooks);
try { ExcelObject.Quit(); } catch { }
ReleaseObj(ExcelObject);
}
}

We can close the Excel Application while converting xls to xlsx by using following code. When we perform this kind of task then Excel application is running in task manager, we Should close this excel which is running in background. Interop is a Com component ,to release the com component we used Marshal.FinalReleaseComObject.

 private void button1_Click(object sender, EventArgs e)
{


Excel03to07("D:\\TestExls\\TestExcelApp.XLS");


}
private void Excel03to07(string fileName)
{
string svfileName = Path.ChangeExtension(fileName, ".xlsx");
object oMissing = Type.Missing;
var app = new Microsoft.Office.Interop.Excel.Application();
var wb = app.Workbooks.Open(fileName, oMissing, oMissing,
oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
wb.SaveAs(svfileName, XlFileFormat.xlOpenXMLWorkbook, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);


wb.Close(false, Type.Missing, Type.Missing);
app.Quit();
GC.Collect();
Marshal.FinalReleaseComObject(wb);
Marshal.FinalReleaseComObject(app);
}

Most of the methods works, but the excel process always stay until close the appliation.

When kill excel process once it can't be executed once again in the same thread - don't know why.

private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.ToString());
}
finally
{
GC.Collect();
}
}

I have found that it is important to have Marshal.ReleaseComObject within a While loop AND finish with Garbage Collection.

static void Main(string[] args)
{
Excel.Application xApp = new Excel.Application();
Excel.Workbooks xWbs = xApp.Workbooks;
Excel.Workbook xWb = xWbs.Open("file.xlsx");


Console.WriteLine(xWb.Sheets.Count);


xWb.Close();
xApp.Quit();


while (Marshal.ReleaseComObject(xWb) != 0);
while (Marshal.ReleaseComObject(xWbs) != 0);
while (Marshal.ReleaseComObject(xApp) != 0);


GC.Collect();
GC.WaitForPendingFinalizers();
}
workbook.Close(0);
excelApp.Quit();

Worked for me.

Another solution to this problem is to save the ProcessID of the Excel program in which you are working with. Then when you are done with the program, you can specifially kill that Excel process without targeting other excel processes.

I got the solution from this answer. Thought I would share it here

So first, you add these line of code outside of a class method

// The DllImport requires -- Using System.Runtime.InteropServices;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

After that,

Now in a method of your choosing, add these lines. These lines discard the specific excel process that you are working with

Modify them to your needs, but the logic is thesame

        if (ExcelApp != null)
{
int excelProcessId = 0;


//your Excel Application variable has access to its Hwnd property
GetWindowThreadProcessId(new IntPtr(ExcelApp.Hwnd), ref excelProcessId);


// you need System.Diagnostics to use Process Class
Process ExcelProc = Process.GetProcessById(excelProcessId);


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

Hence in total your program should look like this

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


static void Main(string[] args)
{
Application ExcelApp = new Application();


_Workbook ExcelWrkBook = ExcelApp.Workbooks.Open(filePath);


_Worksheet ExcelWrkSht = ExcelWrkBook.ActiveSheet;


ExcelWrkSht.Cells[1, 2] = "70";


if (ExcelApp != null)
{
int excelProcessId = 0; // simple declare, zero is merely a place holder


GetWindowThreadProcessId(new IntPtr(ExcelApp.Hwnd), ref excelProcessId);


Process ExcelProc = Process.GetProcessById(excelProcessId);


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


}
}

I have tested this and it removes my Excel processes as shown in Task Manager

using System;
using Excel = Microsoft.Office.Interop.Excel;


private Excel.Worksheet excelSheet;
private Excel.Workbook wb;
private Excel.Application excel;
public void Close()
{
wb.Close(true);
excel.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
if(excelSheet!=null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
GC.Collect();
}

Call GC collect to force a garbage collection after closing the application and workbook.