Schließen des Excel-Anwendungsprozesses in C # nach dem Datenzugriff


85

Ich schreibe eine Anwendung in C #, die eine Excel-Vorlagendatei für Lese- / Schreibvorgänge öffnet. Ich möchte, wenn der Benutzer die Anwendung schließt, der Excel-Bewerbungsprozess geschlossen wurde, ohne die Excel-Datei zu speichern. Siehe meinen Task-Manager nach mehreren Ausführungen der App.

Geben Sie hier die Bildbeschreibung ein

Ich benutze diesen Code, um die Excel-Datei zu öffnen:

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

und für den Datenzugriff verwende ich diesen Code:

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

Ich sehe ähnliche Fragen in Stackoverflow wie diese Frage und diese und Test Antworten, aber es funktioniert nicht funktioniert.


Excel und Word sind sehr langsam und weisen eine Menge Macken auf, wie die, auf die Sie gestoßen sind. Die Dateien sind Zip-Dateien mit einigen XML- und anderen Inhalten. Es gibt auch ein Open XML SDK (oder etwas Neueres), mit dem die Dokumente geöffnet werden können. Dieser Code funktioniert auch ohne lokal installiertes Office. Erwägen Sie, Excel
Sten Petrov

Antworten:


92

Versuche dies:

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

Beim Schließen der Arbeitsmappe stehen Ihnen drei optionale Parameter zur Verfügung:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)oder wenn Sie eine späte Bindung durchführen, ist es manchmal einfacher, Null zu verwenden. Workbook.Close(0) So habe ich es gemacht, wenn ich das Schließen von Arbeitsmappen automatisiert habe.

Außerdem habe ich die Dokumentation dazu nachgeschlagen und sie hier gefunden: Excel Workbook Close

Vielen Dank,


Danke Lieber Michael, es funktioniert richtig: excelBook.Close (0)
Javad Yousefi

Hallo Leute, das funktioniert nicht, wenn Sie eine Excel-Datei zum Lesen öffnen. Es folgt der Code var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926

Ich habe applicationClass verwendet ... und ich habe den obigen Code verwendet, um die Objekte zu entsorgen, aber es funktioniert nicht ...
Singaravelan

3
Nach vielen fehlgeschlagenen Versuchen mit all diesen Antworten habe ich mich für diese Lösung entschieden , um die von mir geöffnete Prozess-ID abzurufen und direkt zu beenden.
UndeadBob

@ Singaravelan: Hast du die Antwort von David Clarke überprüft?
Thế Anh Nguyễn

22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

Versuchen Sie dies .. es hat bei mir funktioniert ... Sie sollten dieses xl-Anwendungsobjekt freigeben, um den Prozess zu stoppen.


14

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

Vermeiden Sie die Verwendung von Ausdrücken mit Doppelpunktaufrufen wie diesen:

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

... weil Sie auf diese Weise RCW-Objekte nicht nur für Arbeitsmappen, sondern auch für Arbeitsmappen erstellen und auch freigeben sollten (was nicht möglich ist, wenn kein Verweis auf das Objekt gepflegt wird).

Dies löste das Problem für mich. Ihr Code wird:

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;

Und dann lassen Sie alle diese Objekte los:

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

Ich wickle dies in ein, try {} finally {}um sicherzustellen, dass alles freigegeben wird, auch wenn etwas schief geht (was könnte möglicherweise schief gehen?), Z.

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

2
Dies hat bei mir funktioniert und Arbeitsmappen einer Variablen zugewiesen, damit sie gelöscht werden kann. Auch eine Beobachtung, ich hatte genau den gleichen Code zum Löschen der Excel-App in Webanwendung und Konsolenanwendung. Der Code vor dem Update zum Entfernen von Double Dot funktionierte in der Konsolen-App einwandfrei, aber wenn er in der Web-App ausgeführt wurde, wurde EXCEL.EXE nicht gelöscht. Ich bin mir nicht sicher, warum sie sich anders verhalten haben, aber die doppelte Punktreferenz wurde in der Web-App behoben.
IronHide

für mich arbeiten. Ich denke, um ein Objekt freizugeben, müssen wir alle zugehörigen Objekte freigeben. Ich hatte das gleiche Problem mit dem Solidworks-Programm.
Gs.

1
Schließen Sie die Arbeitsmappe, beenden Sie die Excel-Anwendung, vermeiden Sie Doppelpunktaufrufe und geben Sie jedes einzelne erstellte COM-Objekt frei. Das hat bei mir funktioniert. Lebensrettende Antwort. Vielen Dank.
Sinan ILYAS

13

Denken Sie daran, es beendet den Prozess:

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 { }
    }
}

Haben Sie auch versucht, es normal zu schließen?

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

Vielen Dank für Ihre Antwort. Ihre erste Lösung schließt alle geöffneten Excel-Dateien. Wenn ich 'excelBook.Close' verwende, wird das Dialogfeld zum Speichern von Excel angezeigt, und ich möchte es nicht :(
Javad Yousefi

1
Ich habe es bearbeitet und mit einer Zeile hinzugefügt, um die SaveAs im Code auszuführen, die den Dialog durch Code ersetzen. Ich hoffe, es hilft. :)
Morris Miao

1
Ich habe dies nur bei einer ersten Überprüfung auf offene Excel-Prozesse verwendet und die IDs in einer Liste gespeichert. Dann habe ich einen neuen Excel-Prozess erstellt, die Bearbeitung durchgeführt und alle Excel-Prozesse geschlossen, die nicht in der ID-Liste enthalten waren.
182764125216

Es ist nicht der richtige Weg, Excel vom Task-Manager freizugeben. Sie sollten alle Objekte freigeben, die erstellt wurden, einschließlich derjenigen, die hinter den Kulissen erstellt wurden, z. B. WorkBOoks.
ehh

Auf diese Weise würden Sie jeden einzelnen ausgeführten Excel-Prozess beenden. Das Problem ist, dass möglicherweise andere Benutzer oder Anwendungen gleichzeitig Excel verwenden. Denken Sie daran, wenn Sie dazu bereit sind.
Sinan ILYAS

5

Excel zu töten ist nicht immer einfach. Siehe diesen Artikel: 50 Möglichkeiten, Excel zu töten

Dieser Artikel enthält die besten Ratschläge von Microsoft ( MS Knowlege Base Article ), wie Excel ordnungsgemäß beendet werden kann, stellt dies jedoch auch sicher, indem der Prozess bei Bedarf abgebrochen wird . Ich habe gerne einen zweiten Fallschirm.

Stellen Sie sicher, dass Sie alle geöffneten Arbeitsmappen schließen, die Anwendung beenden und das xlApp-Objekt freigeben. Überprüfen Sie abschließend, ob der Prozess noch aktiv ist, und beenden Sie ihn, wenn dies der Fall ist.

Dieser Artikel stellt außerdem sicher, dass nicht alle Excel-Prozesse beendet werden, sondern nur der genaue Prozess, der gestartet wurde.

Siehe auch Prozess vom Fenstergriff abrufen

Hier ist der Code, den ich benutze: (funktioniert jedes Mal)

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

Dies war die einzige Lösung, die für mich funktioniert hat. Vielen Dank.
Jon Vote

2

Ich bin auf die gleichen Probleme gestoßen und habe viele Methoden ausprobiert, um sie zu lösen, aber es funktioniert nicht. Endlich habe ich das übrigens gefunden. Einige Referenzen geben hier die Linkbeschreibung ein

Hoffe, mein Code kann jemandem in der Zukunft helfen. Ich habe mehr als zwei Tage damit verbracht, es zu lösen. Unten ist mein 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();
                    }        
                }
            }

1

excelBook.Close (); excelApp.Quit (); Fügen Sie das Ende des Codes hinzu, es könnte ausreichen. Es funktioniert an meinem Code


Ja, aber wenn ich es benutze, wird ein Dialogfeld zum Speichern angezeigt, und ich möchte nicht, dass es angezeigt wird :(
Javad Yousefi

1

Sie können den Prozess mit Ihrem eigenen COMObjekt Excel Pid beenden

Fügen Sie irgendwo unterhalb des DLL-Importcodes hinzu

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

und verwenden

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

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

1

Ich habe festgestellt, dass es wichtig ist, Marshal.ReleaseComObjectinnerhalb einer WhileSchleife UND mit Garbage Collection zu beenden .

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

0
         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 { }
             }
         }

Es schließt den letzten 10-Sekunden-Prozess mit dem Namen "Excel".


0

Der richtige Weg, um alle Excel-Prozesse zu schließen

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

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

0

Basierend auf anderen Lösungen. Ich habe dies verwendet:

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 { }
                    }
                }

Verwenden Sie den "MainWindowHandle", um den Prozess zu identifizieren und ihn zu schließen.

excelObj: Dies ist mein Application Interop Excel-Objekt


0

Verwenden Sie für jedes Excel-Objekt eine Variable und müssen eine Schleife ausführen Marshal.ReleaseComObject >0. Ohne die Schleife bleibt der Excel-Prozess weiterhin aktiv.

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

0

Wir können die Excel-Anwendung schließen, während wir xls in xlsx konvertieren, indem wir den folgenden Code verwenden. Wenn wir diese Art von Aufgabe ausführen, wird die Excel-Anwendung im Task-Manager ausgeführt. Wir sollten dieses Excel schließen, das im Hintergrund ausgeführt wird. Interop ist eine Com-Komponente, um die von uns verwendete com-Komponente Marshal.FinalReleaseComObject freizugeben.

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

0

Die meisten Methoden funktionieren, aber der Excel-Prozess bleibt immer bis zum Abschluss der Anwendung bestehen.

Wenn der Excel-Prozess einmal beendet wird, kann er nicht mehr im selben Thread ausgeführt werden - ich weiß nicht warum.


-1
        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;

Diese GetWindowThreadProcessId findet die richtige Prozess-ID, um sie zu übertreffen.


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

Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, und geben Sie an, welche Einschränkungen und Annahmen gelten.
Dave
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.