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

86

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

entrez la description de l'image ici

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 et ceci , et des réponses de test, mais cela ne fonctionne pas.

Javad Yousefi
la source
Excel et Word sont très lents et comportent une tonne de bizarreries comme celle sur laquelle vous êtes tombé par hasard. Les fichiers sont des fichiers zip contenant du XML et d'autres contenus. Il existe également un SDK Open XML (ou peut-être quelque chose de plus récent) qui peut ouvrir les documents. Un tel code fonctionne sans Office installé localement. Envisagez de ne pas utiliser Excel
Sten Petrov le

Réponses:

95

Essaye ça:

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

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

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)ou si vous effectuez une liaison tardive, il est parfois plus facile d'utiliser zéro. Workbook.Close(0) C'est ainsi que je l'ai fait lors de l'automatisation de la fermeture des classeurs.

Je suis également allé chercher la documentation à ce sujet et je l'ai trouvée ici: Excel Workbook Close

Merci,

Michael
la source
Merci Cher Michael, Cela fonctionne correctement: excelBook.Close (0)
Javad Yousefi
Salut les gars, cela ne fonctionne pas lorsque vous ouvrez un fichier Excel pour la lecture. Voici le code var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926
J'ai utilisé applicationClass ... et j'ai utilisé le code ci-dessus pour supprimer les objets mais cela ne fonctionnera pas ...
Singaravelan
3
Après de nombreuses tentatives infructueuses avec toutes ces réponses, j'ai opté pour cette solution pour obtenir l'ID de processus que j'ai ouvert et le tuer directement.
UndeadBob
@Singaravelan: Avez-vous vérifié la réponse de David Clarke?
Thế Anh Nguyễn
22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

essayez ceci ... cela a fonctionné pour moi ... vous devriez libérer cet objet d'application xl pour arrêter le processus.

Sameera R.
la source
14

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

Évitez d'utiliser des expressions d'appel à deux points, telles que celle-ci:

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

... parce que de cette manière, vous créez des objets RCW non seulement pour le classeur, mais pour les classeurs, et vous devez le libérer également (ce qui n'est pas possible si une référence à l'objet n'est pas conservée).

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 puis relâchez 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);

J'emballe ceci dans un try {} finally {}pour m'assurer que tout est libéré même si quelque chose ne va pas (qu'est-ce qui pourrait mal tourner?) Par exemple

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);
}
David Clarke
la source
2
Cela a fonctionné pour moi, en affectant des classeurs à une variable afin qu'elle puisse être effacée. Également une observation, j'avais exactement le même code pour effacer l'application Excel dans l'application Web et l'application console. Le code avant la mise à jour de la suppression du double point fonctionnait bien dans l'application console, mais lorsqu'il était exécuté dans l'application Web, il n'effaçait pas EXCEL.EXE. Je ne sais pas pourquoi ils se sont comportés différemment, mais la suppression de la référence à double point l'a corrigée dans l'application Web.
IronHide
travaille pour moi. Je pense que pour libérer un objet, nous devons libérer tous les objets associés. J'ai eu le même problème avec le programme solidworks.
Gs.
1
Fermez le classeur, quittez l'application Excel, évitez les appels à deux points et libérez chaque objet COM créé. Cela a fonctionné pour moi. Réponse salvatrice. Merci.
Sinan ILYAS le
13

Pensez-y, cela 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 { }
    }
}

Aussi, 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
Morris Miao
la source
Merci pour votre réponse.Votre première solution ferme tous les fichiers Excel ouverts.Lorsque j'utilise `` excelBook.Close '', une boîte de dialogue de sauvegarde Excel apparaît et je ne le veux pas :(
Javad Yousefi
1
Je l'ai édité et ajouté avec une ligne pour faire le saveAs dans le code qui remplacera la boîte de dialogue par du code, j'espère que cela aide. :)
Morris Miao
1
Je viens de l'utiliser avec une vérification initiale des processus Excel ouverts et j'ai enregistré les ID dans une liste. Ensuite, j'ai créé un nouveau processus Excel, effectué les modifications nécessaires et fermé tout processus Excel qui ne figurait pas dans la liste d'identifiants.
182764125216
Ce n'est pas la bonne façon de libérer Excel du gestionnaire de tâches. Vous devez libérer tous les objets qui ont été créés, y compris ceux qui ont été créés dans les coulisses, tels que les WorkBOoks.
ehh
De cette façon, vous tueriez chaque processus Excel en cours d'exécution. Le problème est que d'autres utilisateurs ou applications peuvent utiliser Excel en même temps. Gardez simplement à l'esprit si vous êtes prêt à le faire.
Sinan ILYAS
5

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

Cet article prend les meilleurs conseils de Microsoft ( article de base MS Knowlege ) sur la façon de faire quitter Excel correctement, mais s'assure également de cela en arrêtant le processus si nécessaire. J'aime avoir un deuxième parachute.

Assurez-vous de fermer tous les classeurs ouverts, de quitter l'application et de libérer l'objet xlApp. Enfin, vérifiez si le processus est toujours actif et si c'est le cas, tuez-le.

Cet article garantit également que nous ne tuons pas tous les processus Excel, mais ne tuons que le processus exact qui a été démarré.

Voir aussi Obtenir le processus à partir de la poignée de 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
D_Bester
la source
C'était la seule solution qui fonctionnait pour moi. Merci.
Jon Vote
2

J'ai rencontré les mêmes problèmes et essayé de nombreuses méthodes pour le résoudre mais cela ne fonctionne pas. Enfin, j'ai trouvé le par mon chemin. Une référence entrez la description du lien ici

J'espère que mon code peut aider quelqu'un à l'avenir. J'ai passé plus de deux jours à le résoudre. Voici mon 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();
                    }        
                }
            }
Kenneth Wong
la source
1

excelBook.Close (); excelApp.Quit (); ajouter la fin du code, cela pourrait suffire. ça marche sur mon code

user2605046
la source
Oui, mais quand je l'utilise, une boîte de dialogue d'enregistrement apparaît et je ne veux pas qu'elle apparaisse :(
Javad Yousefi
1

Vous pouvez tuer le processus avec votre propre COMobjet excel pid

ajouter quelque part sous le code d'importation dll

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

et utilise

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

                Process ExcelProc = Process.GetProcessById(excelProcessId);
                if (ExcelProc != null)
                {
                    ExcelProc.Kill();
                }
            }
Alican Kuklaci
la source
1

J'ai trouvé qu'il est important d'avoir Marshal.ReleaseComObjectdans une Whileboucle ET de terminer avec 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();
}
Emanuel Oliveira
la source
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 { }
             }
         }

Il ferme le dernier processus de 10 secondes avec le nom "Excel"

xrhstos
la source
0

La bonne façon de fermer tous les processus Excel

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 { }
    }
}
Md. Alim Ul Karim
la source
0

Basé sur une autre solution. J'ai utilisé ceci:

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

Utilisation du "MainWindowHandle" pour identifier le processus et le fermer.

excelObj: Ceci est mon objet Excel Application Interop

merryrppin
la source
0

Utilisez une variable pour chaque objet Excel et doit boucler Marshal.ReleaseComObject >0. Sans la boucle, le processus Excel reste toujours actif.

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);
        }
}
user10170010
la source
0

Nous pouvons fermer l'application Excel lors de la conversion de xls en xlsx en utilisant le code suivant. Lorsque nous effectuons ce genre de tâche, l'application Excel s'exécute dans le gestionnaire de tâches, nous devons fermer cet excel qui s'exécute en arrière-plan. Interop est un composant Com, pour publier le composant com, nous avons utilisé 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);
   }
Sumant Singh
la source
0

La plupart des méthodes fonctionnent, mais le processus Excel reste toujours jusqu'à la fermeture de l'application.

Lorsque kill excel processus une fois, il ne peut pas être exécuté une fois de plus dans le même thread - je ne sais pas pourquoi.

Atanas Atanasov
la source
-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;

ce GetWindowThreadProcessId trouve le bon Process Id o excell .... Après l'avoir tué .... Profitez-en !!!

xrhstos
la source
-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();
    }
}
Mehmet Yardım
la source
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter une explication et donner une indication des limites et des hypothèses applicables.
Dave le