Zamykanie procesu aplikacji programu Excel w języku C # po uzyskaniu dostępu do danych

86

Piszę aplikację w C #, która otwiera plik szablonu programu Excel do operacji odczytu / zapisu. Chcę, gdy użytkownik zamyka aplikację, proces aplikacji Excel został zamknięty, bez zapisywania pliku Excela. Zobacz mojego Menedżera zadań po wielu uruchomieniach aplikacji.

wprowadź opis obrazu tutaj

Używam tego kodu, aby otworzyć plik Excela:

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

a do dostępu do danych używam tego kodu:

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

Widzę podobne pytania w przepełnieniu stosu, takie jak to i to , i testuję odpowiedzi, ale to nie działa.

Javad Yousefi
źródło
Excel i Word są bardzo powolne i zawierają mnóstwo dziwactw, takich jak ten, na który się natknąłeś. Pliki są plikami zip z pewną zawartością XML i inną zawartością. Istnieje również Open XML SDK (a może coś nowszego), który może otwierać dokumenty. Taki kod działa również bez pakietu Office zainstalowanego lokalnie. Rozważ nie używanie programu Excel
Sten Petrov

Odpowiedzi:

95

Spróbuj tego:

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

Zamykając zeszyt ćwiczeń, masz trzy opcjonalne parametry:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)lub jeśli wykonujesz późne wiązanie, czasami łatwiej jest użyć zera. Workbook.Close(0) Tak właśnie zrobiłem podczas automatyzacji zamykania skoroszytów.

Poszedłem też do dokumentacji i znalazłem ją tutaj: Skoroszyt programu Excel Zamknij

Dzięki,

Michał
źródło
Dzięki Drogi Michale, działa poprawnie: excelBook.Zamknij (0)
Javad Yousefi
Cześć chłopaki, to nie działa, gdy otwierasz plik Excela do czytania. Poniżej znajduje się kod var excelApp = new Application (); var workBooks = excelApp.Workbooks.Open @ "c: \ temp \ myexcel.xlsx"); workBooks.Close (0); excelApp.Quit ();
user1131926
Użyłem applicationClass ... i użyłem powyższego kodu do usunięcia obiektów, ale to nie
zadziała
3
Po wielu nieudanych próbach z tymi wszystkimi odpowiedziami poszedłem z tym rozwiązaniem, aby uzyskać identyfikator procesu, który otworzyłem, i zabić go bezpośrednio.
UndeadBob
@Singaravelan: Czy sprawdziłeś odpowiedź Davida Clarke'a?
Thế Anh Nguyễn
22
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

spróbuj tego ... zadziałało dla mnie ... powinieneś zwolnić ten obiekt aplikacji xl, aby zatrzymać proces.

Sameera R.
źródło
14

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

Unikaj używania wyrażeń wywołujących podwójną kropkę, takich jak ten:

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

... ponieważ w ten sposób tworzysz obiekty RCW nie tylko dla skoroszytu, ale także dla skoroszytów i powinieneś je również zwolnić (co nie jest możliwe, jeśli odwołanie do obiektu nie jest utrzymywane).

To rozwiązało problem. Twój kod staje się:

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;

A potem wypuść wszystkie te obiekty:

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

Owijam to, try {} finally {}aby upewnić się, że wszystko zostanie zwolnione, nawet jeśli coś pójdzie nie tak (co może się nie udać?) Np

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
źródło
2
To zadziałało dla mnie, przypisując skoroszyty do zmiennej, aby można ją było wyczyścić. I jeszcze jedna uwaga, miałem dokładnie ten sam kod do czyszczenia aplikacji Excel w aplikacji internetowej i aplikacji konsolowej. Kod przed aktualizacją usuwania podwójnej kropki działał dobrze w aplikacji konsolowej, ale po uruchomieniu w aplikacji internetowej nie wyczyścił pliku EXCEL.EXE. Nie jestem pewien, dlaczego zachowywali się inaczej, ale usunięcie odwołania z podwójną kropką naprawiło to w aplikacji internetowej.
IronHide,
pracuje dla mnie. Myślę, że aby zwolnić obiekt, musimy zwolnić cały powiązany obiekt. Miałem ten sam problem z programem Solidworks.
Gs.
1
Zamknij skoroszyt, zamknij aplikację Excel, unikaj wywołania podwójnej kropki i zwolnij każdy utworzony obiekt COM. To zadziałało dla mnie. Odpowiedź ratująca życie. Dzięki.
Sinan ILYAS
13

Pomyśl o tym, to zabija proces:

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

Czy próbowałeś po prostu normalnie go zamknąć?

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
źródło
Dziękuję za odpowiedź. Twoje pierwsze rozwiązanie zamyka wszystkie otwarte pliki Excela. Kiedy używam 'excelBook.Close', pojawia się okno dialogowe zapisywania programu Excel i nie chcę tego :(
Javad Yousefi
1
Edytowałem go i dodałem z linią, aby wykonać saveAs w kodzie, który zastąpi okno dialogowe kodem, mam nadzieję, że to pomoże. :)
Morris Miao
1
Właśnie użyłem tego przy wstępnym sprawdzeniu otwartych procesów programu Excel i zapisałem identyfikatory na liście. Następnie utworzyłem nowy proces programu Excel, dokonałem edycji, którą musiałem, i zamknąłem każdy proces programu Excel, którego nie było na liście identyfikatorów.
182764125216
Nie jest to właściwy sposób na zwolnienie programu Excel z menedżera zadań. Powinieneś zwolnić wszystkie obiekty, które zostały utworzone, w tym te, które zostały utworzone za sceną, takie jak WorkBOoks.
ehh
W ten sposób zabiłbyś każdy działający proces Excela. Problem polega na tym, że w tym samym czasie mogą być inni użytkownicy lub aplikacje korzystające z programu Excel. Pamiętaj tylko, jeśli chcesz to zrobić.
Sinan ILYAS
5

Zabicie Excela nie zawsze jest łatwe; zobacz ten artykuł: 50 sposobów na zabicie programu Excel

W tym artykule zawarto najlepsze rady firmy Microsoft ( artykuł bazy wiedzy MS Knowlege ), jak sprawić, by program Excel ładnie zakończył pracę, ale także upewnia się o tym, zabijając proces, jeśli to konieczne. Lubię mieć drugi spadochron.

Zamknij wszystkie otwarte skoroszyty, zamknij aplikację i zwolnij obiekt xlApp. Na koniec sprawdź, czy proces nadal żyje, a jeśli tak, zakończ go.

W tym artykule upewniono się również, że nie zabijamy wszystkich procesów programu Excel, a tylko dokładnie ten proces, który został uruchomiony.

Zobacz także Pobierz proces z uchwytu okna

Oto kod, którego używam: (działa za każdym razem)

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
źródło
To było jedyne rozwiązanie, które działało dla mnie. Dzięki.
Jon Vote
2

Spotkałem te same problemy i wypróbowałem wiele metod, aby je rozwiązać, ale nie działa. Wreszcie znalazłem przy okazji. Niektóre odniesienia wprowadź tutaj opis linku

Mam nadzieję, że mój kod pomoże komuś w przyszłości. Na rozwiązanie tego problemu spędziłem ponad dwa dni. Poniżej mój kod:

//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
źródło
1

excelBook.Close (); excelApp.Quit (); dodaj koniec kodu, może wystarczyć. działa na moim kodzie

user2605046
źródło
Tak, ale kiedy go używam, pojawia się okno dialogowe zapisywania i nie chcę, aby się pojawiło :(
Javad Yousefi
1

Możesz zabić proces za pomocą własnego COMobiektu Excel pid

dodaj gdzieś poniżej kodu importu dll

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

I użyć

 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
źródło
1

I odkryli, że ważne jest, aby mieć Marshal.ReleaseComObjectw Whilepętli i zakończyć 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
źródło
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 { }
             }
         }

Zamyka ostatnie 10 sekund proces o nazwie „Excel”

xrhstos
źródło
0

Właściwy sposób na zamknięcie wszystkich procesów programu 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
źródło
0

Oparty na innych rozwiązaniach. Użyłem tego:

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

Korzystanie z „MainWindowHandle” w celu zidentyfikowania procesu i zamknięcia go.

excelObj: To jest mój obiekt Excel Application Interop

merryrppin
źródło
0

Użyj zmiennej dla każdego obiektu programu Excel i musi zostać zapętlona Marshal.ReleaseComObject >0. Bez pętli proces Excela nadal pozostaje aktywny.

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
źródło
0

Możemy zamknąć aplikację Excel podczas konwersji xls do xlsx za pomocą następującego kodu. Kiedy wykonujemy tego rodzaju zadanie, aplikacja Excel działa w menedżerze zadań, powinniśmy zamknąć ten program Excel, który działa w tle. Interop jest składnikiem Com, aby zwolnić składnik com użyliśmy 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
źródło
0

Większość metod działa, ale proces excela pozostaje zawsze do zamknięcia aplikacji.

Kiedy zabijamy proces Excela, nie można go wykonać ponownie w tym samym wątku - nie wiem dlaczego.

Atanas Atanasov
źródło
-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;

to GetWindowThreadProcessId znajduje poprawny identyfikator procesu o excell .... Po jego zabiciu .... Ciesz się !!!

xrhstos
źródło
-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
źródło
Chociaż ten kod może rozwiązać problem, w tym wyjaśnienie, jak i dlaczego to rozwiązuje problem, naprawdę pomogłoby poprawić jakość twojego posta i prawdopodobnie zaowocowałoby większą liczbą pozytywnych głosów. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz. Proszę edytować swoje odpowiedzi, aby dodać wyjaśnienie, i dać wskazówkę co zastosować ograniczenia i założenia.
Dave