96 votes

Fermeture du processus d'application d'Excel en C# après l'accès aux données

J'écris une application en C# qui ouvre un fichier modèle Excel pour des opérations de lecture/écriture. Je veux que lorsque l'utilisateur ferme l'application, le processus de l'application Excel ait été fermé, sans sauvegarder le fichier Excel. Voir mon gestionnaire de tâches après plusieurs exécutions de l'application.

enter image description here

J'utilise ce code pour ouvrir le fichier excel :

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

et pour l'accès aux données j'utilise ce code :

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

Je vois des questions similaires dans stackoverflow telles que cette question y este et les réponses aux tests, mais ça ne marche pas.

103voto

Michael Points 1048

Essayez ça :

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

Lors de la fermeture du carnet de travail, vous disposez de trois paramètres facultatifs :

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false) ou si vous faites de la reliure tardive, il est parfois plus facile d'utiliser le zéro. Workbook.Close(0) C'est ainsi que j'ai procédé pour automatiser la fermeture des classeurs.

J'ai également cherché la documentation correspondante et je l'ai trouvée ici : Fermeture d'un classeur Excel

24voto

Sameera Rukshan Points 559
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

Essayez ceci... ça a marché pour moi... vous devez libérer cet objet d'application xl pour arrêter le processus.

16voto

David Clarke Points 3165

Réf : https://stackoverflow.com/a/17367570/132599

Évitez d'utiliser des expressions à double point, comme celle-ci :

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

...car de cette façon, vous créez des objets RCW non seulement pour le classeur, mais aussi pour les classeurs, et vous devez les libérer aussi (ce qui n'est pas possible si une référence à l'objet n'est pas maintenue).

Cela a résolu le problème pour moi. Votre code devient :

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;

Et ensuite libérer tous ces objets :

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

Je l'emballe dans un try {} finally {} pour s'assurer que tout est publié, même si quelque chose ne va pas (qu'est-ce qui pourrait aller mal ?), par ex.

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

13voto

Morris Miao Points 710

Pensez-y, ça tue le processus :

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

Avez-vous essayé de le fermer normalement ?

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

7voto

D_Bester Points 3582

Il n'est pas toujours facile de tuer Excel ; voir cet article : 50 façons de tuer Excel

Cet article reprend les meilleurs conseils de Microsoft ( Article de la base de connaissances MS ) sur la façon de faire quitter Excel gentiment, mais il s'en assure aussi en tuant le processus si nécessaire. J'aime avoir un deuxième parachute.

Veillez à fermer tous les classeurs ouverts, à quitter l'application et à libérer l'objet xlApp. Enfin, vérifiez si le processus est toujours en vie et si c'est le cas, tuez-le.

Cet article s'assure également que nous ne tuons pas tous les processus Excel mais seulement le processus exact qui a été lancé.

Voir aussi Obtenir le processus de la poignée de la fenêtre

Voici le code que j'utilise : (fonctionne à chaque fois)

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X