Jak prawidłowo wyczyścić obiekty Excel interop?

Używam programu Excel interop w C #(ApplicationClass) i umieściłem następujący kod w mojej klauzuli finally:

while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();

Chociaż tego rodzaju działania, Excel.exe Proces jest nadal w tle, nawet po zamknięciu Excela. Jest on wydany TYLKO po ręcznym zamknięciu mojej aplikacji.

Co robię źle, czy jest jakaś alternatywa dla zapewnienia, że obiekty interop są właściwie usuwane?

Author: Peter Mortensen, 2008-10-01

30 answers

Excel nie kończy się, ponieważ aplikacja nadal przechowuje odwołania do obiektów COM.

Domyślam się, że wywołujesz przynajmniej jednego członka obiektu COM bez przypisywania go do zmiennej.

Dla mnie to był excelApp.Arkusze obiekt, którego użyłem bezpośrednio bez przypisywania go do zmiennej:
Worksheet sheet = excelApp.Worksheets.Open(...);
...
Marshal.ReleaseComObject(sheet);

Nie wiedziałem, że wewnętrznie C# stworzył wrapper dla arkuszy obiektu COM, który nie został wydany przez mój kod (ponieważ nie był tego świadomy) i był przyczyną, dla której Excel nie został rozładowany.

Znalazłem rozwiązanie mojego problemu na tej stronie , która również ma ładną regułę użycia obiektów COM w C#:

Nigdy nie używaj dwóch kropek z obiektami COM.


Więc z tą wiedzą właściwym sposobem zrobienia powyższego jest:

Worksheets sheets = excelApp.Worksheets; // <-- The important part
Worksheet sheet = sheets.Open(...);
...
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(sheet);
 647
Author: VVS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 14:30:47

W rzeczywistości możesz zwolnić obiekt aplikacji Excel w sposób czysty, ale musisz o to zadbać.

Rada, aby utrzymywać nazwane odniesienie dla absolutnie każdego obiektu COM, do którego uzyskujesz dostęp, a następnie wyraźnie zwolnić go za pośrednictwem Marshal.FinalReleaseComObject() jest poprawna w teorii, ale, niestety, bardzo trudna do zarządzania w praktyce. Jeśli ktoś kiedykolwiek poślizgnie się gdziekolwiek i użyje "dwóch kropek", lub iteracji komórek za pomocą pętli for each, lub innego podobnego polecenia, wtedy będziesz miał niezrealizowane obiekty COM i ryzykujesz czekaj. W tym przypadku nie byłoby sposobu, aby znaleźć przyczynę w kodzie; musiałbyś przejrzeć cały kod okiem i miejmy nadzieję znaleźć przyczynę, zadanie, które może być prawie niemożliwe dla dużego projektu.

Dobra wiadomość jest taka, że nie musisz utrzymywać nazwanej zmiennej odniesienia do każdego obiektu COM, którego używasz. Zamiast tego wywołaj GC.Collect(), a następnie GC.WaitForPendingFinalizers(), aby zwolnić wszystkie (Zwykle drobne) obiekty, do których nie masz odniesienia, a następnie jawnie zwolnić obiekty do które posiadasz nazwaną zmienną odniesienia.

Należy również zwolnić nazwane odwołania w odwrotnej kolejności ważności: najpierw obiekty zakresu, następnie arkusze robocze, skoroszyty, a następnie obiekt aplikacji Excel.

Na przykład, zakładając, że masz zmienną obiektu Range o nazwie xlRng, zmienną arkusza roboczego o nazwie xlSheet, zmienną skoroszytu o nazwie xlBook i zmienną aplikacji Excel o nazwie xlApp, wtedy twój kod czyszczenia może wyglądać jak po:

// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();

Marshal.FinalReleaseComObject(xlRng);
Marshal.FinalReleaseComObject(xlSheet);

xlBook.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlBook);

xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);

W większości przykładów kodu można zobaczyć do czyszczenia obiektów COM z. NET, wywołania GC.Collect() i GC.WaitForPendingFinalizers() są wykonywane dwa razy jak w:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

Nie powinno to być jednak wymagane, chyba że używasz Visual Studio Tools for Office (VSTO), które używa finalizatorów, które powodują, że cały wykres obiektów ma być promowany w kolejce finalizacji. Takie obiekty nie zostaną wypuszczone do następnego garbage collection. Jeśli jednak nie używasz VSTO, możesz powinien być w stanie zadzwonić GC.Collect() i GC.WaitForPendingFinalizers() tylko raz.

Wiem, że jawne wywołanie GC.Collect() jest Nie-Nie (i na pewno zrobienie tego dwa razy brzmi bardzo bolesnie), ale szczerze mówiąc, nie da się tego obejść. Dzięki normalnym operacjom wygenerujesz ukryte obiekty, do których nie posiadasz odniesienia, a których nie możesz zwolnić za pomocą innych środków niż wywołanie GC.Collect().

To skomplikowany temat, ale to naprawdę wszystko. Po utworzeniu tego szablonu dla procedura czyszczenia można kodować normalnie, bez potrzeby owijania itp. :-)

Mam tutorial na ten temat tutaj:

Automatyzacja programów biurowych z VB.Net / COM Interop

Jest napisane dla VB.NET ale nie zniechęcaj się tym, zasady są dokładnie takie same jak w przypadku używania C#.

 264
Author: Mike Rosenblum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 17:42:41

Przedmowa: moja odpowiedź zawiera dwa rozwiązania, więc bądź ostrożny podczas czytania i nie przegap niczego.

Istnieją różne sposoby i porady, jak zwolnić instancję Excela, takie jak:

  • Zwalnianie każdego obiektu com jawnie z szeryfem.Finałreleasecomobject() (nie zapominając o implicite created COM-objects). Do wydania każdy utworzony obiekt com, można użyć Zasada 2 kropek wymienionych tutaj:
    Jak prawidłowo wyczyścić Excel interop przedmioty?

  • Dzwonię do GC.Collect () i GC.WaitForPendingFinalizers () to make CLR wydaje nieużywane com-obiekty * (w rzeczywistości działa, zobacz moje drugie rozwiązanie po szczegóły)

  • Sprawdzanie czy com-server-application może pokazuje skrzynkę wiadomości czekającą na użytkownik do odpowiedzi (choć nie jestem na pewno może uniemożliwić Excel ' owi zamykam, ale słyszałem o tym kilka razy)

  • Wysyłanie wiadomości WM_CLOSE do głównego Okno Excel

  • Wykonanie funkcja, która działa z programem Excel w osobnej Appdomenie. Niektórzy uważają, że instancja Excela zostanie zamknięty, gdy AppDomain jest rozładowany.

  • Zabijanie wszystkich instancji Excela, które zostały utworzone po uruchomieniu naszego kodu Excel-interoping.

Ale! czasami wszystkie te opcje po prostu nie pomagają lub nie mogą być odpowiednie!

Na przykład wczoraj dowiedziałem się, że w jednej z moich funkcji (która działa z Excelem) Excel działa po funkcji koniec. Próbowałem wszystkiego! Dokładnie sprawdziłem całą funkcję 10 razy i dodałem Marszałek.FinalReleaseComObject () za wszystko! Miałem też GC.Collect () i GC.WaitForPendingFinalizers(). Sprawdziłem Ukryte skrzynki z wiadomościami. Próbowałem wysłać wiadomość WM_CLOSE do głównego okna Excela. Wykonałem swoją funkcję w oddzielnym AppDomain i rozładowałem tę domenę. Nic nie pomogło! Opcja zamykania wszystkich wystąpień programu excel jest nieodpowiednia, ponieważ jeśli użytkownik uruchomi ręcznie inną instancję programu Excel, podczas wykonywania mojej funkcji, która działa również z Excelem, wtedy ta instancja również zostanie zamknięta przez moją funkcję. Założę się, że użytkownik nie będzie zadowolony! Szczerze mówiąc, jest to kiepska Opcja(bez obrazy chłopaki). Więc spędziłem kilka godzin zanim znalazłem dobre (moim skromnym zdaniem) rozwiązanie : Zabij proces Excela przez hWnd jego głównego okna (jest to pierwsze rozwiązanie).

Oto prosty kod:

[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

/// <summary> Tries to find and kill process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <returns>True if process was found and killed. False if process was not found by hWnd or if it could not be killed.</returns>
public static bool TryKillProcessByMainWindowHwnd(int hWnd)
{
    uint processID;
    GetWindowThreadProcessId((IntPtr)hWnd, out processID);
    if(processID == 0) return false;
    try
    {
        Process.GetProcessById((int)processID).Kill();
    }
    catch (ArgumentException)
    {
        return false;
    }
    catch (Win32Exception)
    {
        return false;
    }
    catch (NotSupportedException)
    {
        return false;
    }
    catch (InvalidOperationException)
    {
        return false;
    }
    return true;
}

/// <summary> Finds and kills process by hWnd to the main window of the process.</summary>
/// <param name="hWnd">Handle to the main window of the process.</param>
/// <exception cref="ArgumentException">
/// Thrown when process is not found by the hWnd parameter (the process is not running). 
/// The identifier of the process might be expired.
/// </exception>
/// <exception cref="Win32Exception">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="NotSupportedException">See Process.Kill() exceptions documentation.</exception>
/// <exception cref="InvalidOperationException">See Process.Kill() exceptions documentation.</exception>
public static void KillProcessByMainWindowHwnd(int hWnd)
{
    uint processID;
    GetWindowThreadProcessId((IntPtr)hWnd, out processID);
    if (processID == 0)
        throw new ArgumentException("Process has not been found by the given main window handle.", "hWnd");
    Process.GetProcessById((int)processID).Kill();
}

Jak widzisz podałem dwie metody, zgodnie z TRY-Parse wzorzec (myślę, że jest to tutaj właściwe): jedna metoda nie wyrzuca wyjątku, jeśli proces nie mógł zostać zabity (na przykład proces już nie istnieje), a inna metoda wyrzuca wyjątek, jeśli proces nie został zabity. Jedynym słabym miejscem w tym kodzie są uprawnienia zabezpieczeń. Teoretycznie użytkownik może nie mieć uprawnień do zabicia procesu, ale w 99,99% wszystkich przypadków ma takie uprawnienia. Testowałem go również z kontem gościa - działa idealnie.

Więc, Twój kod, praca z Excelem może wyglądać tak:

int hWnd = xl.Application.Hwnd;
// ...
// here we try to close Excel as usual, with xl.Quit(),
// Marshal.FinalReleaseComObject(xl) and so on
// ...
TryKillProcessByMainWindowHwnd(hWnd);
Voila! Excel zakończony! :)

Ok, wróćmy do drugiego rozwiązania, jak obiecałem na początku postu. Drugim rozwiązaniem jest wywołanie GC.Collect () i GC.WaitForPendingFinalizers(). tak, rzeczywiście działają, ale musisz być ostrożny tutaj!
Wiele osób mówi (a ja powiedziałem), że dzwoniąc GC.Collect() nie pomaga. Ale powodem, dla którego to nie pomoże, Jest to, że nadal istnieją odniesienia do obiektów COM! Jeden z najpopularniejsze powody GC.Funkcja Collect () nie jest pomocna w uruchomieniu projektu w trybie debugowania. W trybie debugowania obiekty, które nie są już odwołane, nie będą zbierane do końca metody.
Więc, jeśli próbowałeś GC.Collect () i GC.WaitForPendingFinalizers() i nie pomogło, spróbuj wykonać następujące czynności:

1) Spróbuj uruchomić projekt w trybie Release i sprawdź, czy Excel jest poprawnie zamknięty

2) owiń metodę pracującą z Excelem w osobny metoda. Więc zamiast czegoś takiego:

void GenerateWorkbook(...)
{
  ApplicationClass xl;
  Workbook xlWB;
  try
  {
    xl = ...
    xlWB = xl.Workbooks.Add(...);
    ...
  }
  finally
  {
    ...
    Marshal.ReleaseComObject(xlWB)
    ...
    GC.Collect();
    GC.WaitForPendingFinalizers();
  }
}

Piszesz:

void GenerateWorkbook(...)
{
  try
  {
    GenerateWorkbookInternal(...);
  }
  finally
  {
    GC.Collect();
    GC.WaitForPendingFinalizers();
  }
}

private void GenerateWorkbookInternal(...)
{
  ApplicationClass xl;
  Workbook xlWB;
  try
  {
    xl = ...
    xlWB = xl.Workbooks.Add(...);
    ...
  }
  finally
  {
    ...
    Marshal.ReleaseComObject(xlWB)
    ...
  }
}

Teraz Excel się zamknie =)

 199
Author: nightcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:36

Aktualizacja: Dodano kod C# i link do zadań systemu Windows

Spędziłem jakiś czas próbując rozgryźć ten problem, a w tym czasie XtremeVBTalk był najbardziej aktywny i reagował. Oto link do mojego oryginalnego postu, zamykanie procesu Excel Interop czysto, nawet jeśli Twoja aplikacja ulegnie awarii. Poniżej znajduje się podsumowanie postu, a kod skopiowany do tego postu.

  • zamknięcie procesu Interop za pomocą Application.Quit() i Process.Kill() działa w większości przypadków, ale nie powiedzie się, jeśli aplikacje ulegają katastrofalnym awariom. Jeśli aplikacja ulegnie awarii, proces Excel nadal będzie działać luźno.
  • rozwiązaniem jest umożliwienie OS obsługi czyszczenia procesów za pomocą obiektów zadań systemu Windows za pomocą wywołań Win32. Gdy główna aplikacja umrze, powiązane procesy (np.

Uznałem to za czyste rozwiązanie, ponieważ system operacyjny wykonuje prawdziwą pracę sprzątania. Wszystko co musisz zrobić to zarejestruj proces Excela.

Windows Job Code

Owija wywołania API Win32 do rejestracji procesów Interop.

public enum JobObjectInfoType
{
    AssociateCompletionPortInformation = 7,
    BasicLimitInformation = 2,
    BasicUIRestrictions = 4,
    EndOfJobTimeInformation = 6,
    ExtendedLimitInformation = 9,
    SecurityLimitInformation = 5,
    GroupInformation = 11
}

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
    public int nLength;
    public IntPtr lpSecurityDescriptor;
    public int bInheritHandle;
}

[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
    public Int64 PerProcessUserTimeLimit;
    public Int64 PerJobUserTimeLimit;
    public Int16 LimitFlags;
    public UInt32 MinimumWorkingSetSize;
    public UInt32 MaximumWorkingSetSize;
    public Int16 ActiveProcessLimit;
    public Int64 Affinity;
    public Int16 PriorityClass;
    public Int16 SchedulingClass;
}

[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
    public UInt64 ReadOperationCount;
    public UInt64 WriteOperationCount;
    public UInt64 OtherOperationCount;
    public UInt64 ReadTransferCount;
    public UInt64 WriteTransferCount;
    public UInt64 OtherTransferCount;
}

[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
    public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
    public IO_COUNTERS IoInfo;
    public UInt32 ProcessMemoryLimit;
    public UInt32 JobMemoryLimit;
    public UInt32 PeakProcessMemoryUsed;
    public UInt32 PeakJobMemoryUsed;
}

public class Job : IDisposable
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr CreateJobObject(object a, string lpName);

    [DllImport("kernel32.dll")]
    static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

    private IntPtr m_handle;
    private bool m_disposed = false;

    public Job()
    {
        m_handle = CreateJobObject(null, null);

        JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
        info.LimitFlags = 0x2000;

        JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        extendedInfo.BasicLimitInformation = info;

        int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
        Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

        if (!SetInformationJobObject(m_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
            throw new Exception(string.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()));
    }

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    #endregion

    private void Dispose(bool disposing)
    {
        if (m_disposed)
            return;

        if (disposing) {}

        Close();
        m_disposed = true;
    }

    public void Close()
    {
        Win32.CloseHandle(m_handle);
        m_handle = IntPtr.Zero;
    }

    public bool AddProcess(IntPtr handle)
    {
        return AssignProcessToJobObject(m_handle, handle);
    }

}

Uwaga o kodzie konstruktora

  • w konstruktorze nazywa się info.LimitFlags = 0x2000;. 0x2000 jest wartością JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE enum, a wartość ta jest zdefiniowana przez MSDN jako:

Powoduje zakończenie wszystkich procesów związanych z zadaniem, gdy ostatni uchwyt do pracy to zamknięte.

Dodatkowe wywołanie API Win32, aby uzyskać identyfikator procesu (PID)

    [DllImport("user32.dll", SetLastError = true)]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

Korzystanie z kodu

    Excel.Application app = new Excel.ApplicationClass();
    Job job = new Job();
    uint pid = 0;
    Win32.GetWindowThreadProcessId(new IntPtr(app.Hwnd), out pid);
    job.AddProcess(Process.GetProcessById((int)pid).Handle);
 46
Author: joshgo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-16 17:00:08

To działało dla projektu, nad którym pracowałem:

excelApp.Quit();
Marshal.ReleaseComObject (excelWB);
Marshal.ReleaseComObject (excelApp);
excelApp = null;

Dowiedzieliśmy się, że ważne jest, aby ustawić każde odwołanie do obiektu Excel COM NA null, kiedy już z nim skończyłeś. Obejmowały one komórki, arkusze i wszystko.

 34
Author: Philip Fourie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 14:34:03

Wszystko, co znajduje się w przestrzeni nazw programu Excel, musi zostać zwolnione. Period

Nie możesz robić:

Worksheet ws = excel.WorkBooks[1].WorkSheets[1];

Musisz robić

Workbooks books = excel.WorkBooks;
Workbook book = books[1];
Sheets sheets = book.WorkSheets;
Worksheet ws = sheets[1];

Następnie następuje uwolnienie obiektów.

 29
Author: MagicKat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 14:34:42

I znalazłem użyteczny szablon generyczny, który może pomóc w implementacji prawidłowego wzorca usuwania dla obiektów COM, które wymagają Marshala.ReleaseComObject wywoływane, gdy wychodzą poza zakres:

Użycie:

using (AutoReleaseComObject<Application> excelApplicationWrapper = new AutoReleaseComObject<Application>(new Application()))
{
    try
    {
        using (AutoReleaseComObject<Workbook> workbookWrapper = new AutoReleaseComObject<Workbook>(excelApplicationWrapper.ComObject.Workbooks.Open(namedRangeBase.FullName, false, false, missing, missing, missing, true, missing, missing, true, missing, missing, missing, missing, missing)))
        {
           // do something with your workbook....
        }
    }
    finally
    {
         excelApplicationWrapper.ComObject.Quit();
    } 
}

Szablon:

public class AutoReleaseComObject<T> : IDisposable
{
    private T m_comObject;
    private bool m_armed = true;
    private bool m_disposed = false;

    public AutoReleaseComObject(T comObject)
    {
        Debug.Assert(comObject != null);
        m_comObject = comObject;
    }

#if DEBUG
    ~AutoReleaseComObject()
    {
        // We should have been disposed using Dispose().
        Debug.WriteLine("Finalize being called, should have been disposed");

        if (this.ComObject != null)
        {
            Debug.WriteLine(string.Format("ComObject was not null:{0}, name:{1}.", this.ComObject, this.ComObjectName));
        }

        //Debug.Assert(false);
    }
#endif

    public T ComObject
    {
        get
        {
            Debug.Assert(!m_disposed);
            return m_comObject;
        }
    }

    private string ComObjectName
    {
        get
        {
            if(this.ComObject is Microsoft.Office.Interop.Excel.Workbook)
            {
                return ((Microsoft.Office.Interop.Excel.Workbook)this.ComObject).Name;
            }

            return null;
        }
    }

    public void Disarm()
    {
        Debug.Assert(!m_disposed);
        m_armed = false;
    }

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

    #endregion

    protected virtual void Dispose(bool disposing)
    {
        if (!m_disposed)
        {
            if (m_armed)
            {
                int refcnt = 0;
                do
                {
                    refcnt = System.Runtime.InteropServices.Marshal.ReleaseComObject(m_comObject);
                } while (refcnt > 0);

                m_comObject = default(T);
            }

            m_disposed = true;
        }
    }
}

Numer referencyjny:

Http://www.deez.info/sengelha/2005/02/11/useful-idisposable-class-3-autoreleasecomobject/

 18
Author: Edward Wilde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-08 11:20:01

Nie mogę uwierzyć, że ten problem prześladuje świat od 5 lat.... Jeśli utworzyłeś aplikację, musisz ją najpierw zamknąć przed usunięciem łącza.

objExcel = new Excel.Application();  
objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing)); 

Podczas zamykania

objBook.Close(true, Type.Missing, Type.Missing); 
objExcel.Application.Quit();
objExcel.Quit(); 

Gdy nowa aplikacja excel, otwiera program excel w tle. Musisz wydać polecenie, aby ten program excel zamknął się przed zwolnieniem łącza, ponieważ ten program excel nie jest częścią twojej bezpośredniej kontroli. Dlatego pozostanie otwarty, jeśli link jest zwolniony!

Dobre programowanie każdy ~ ~

 15
Author: Colin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-21 03:41:13

Po pierwsze - ty nigdy nie musisz wywoływać Marshal.ReleaseComObject(...) lub Marshal.FinalReleaseComObject(...) podczas wykonywania Excela interop. Jest to mylący wzorzec anty, ale wszelkie informacje na ten temat, w tym od firmy Microsoft, które wskazują, że musisz ręcznie zwolnić referencje COM Z. NET, są nieprawidłowe. Faktem jest, że środowisko uruchomieniowe. NET i garbage collector prawidłowo śledzą i czyszczą odniesienia COM. Dla Twojego kodu oznacza to, że możesz usunąć cały ' while (...) pętla u góry.

Po drugie, jeśli chcesz mieć pewność, że odniesienia COM do poza procesowego obiektu COM są oczyszczane po zakończeniu procesu (tak, że proces Excel zostanie zamknięty), musisz upewnić się, że moduł garbage collector działa. Robisz to poprawnie z wywołaniami GC.Collect() i GC.WaitForPendingFinalizers(). Wywołanie tego dwa razy jest bezpieczne i zapewnia, że cykle są zdecydowanie oczyszczone zbyt (choć nie jestem pewien, czy jest to potrzebne, i byłbym wdzięczny przykład, który to pokazuje).

Po trzecie, podczas uruchamiania pod debuggerem, lokalne odniesienia będą sztucznie przechowywane żywy do końca metody (tak, że lokalna zmienna inspekcja działa). Tak więc wywołania GC.Collect() nie są skuteczne do czyszczenia obiektów takich jak rng.Cells z tej samej metody. Powinieneś podzielić kod wykonujący COM interop z GC cleanup na osobne metody. (To było dla mnie kluczowe odkrycie, z jednej części odpowiedzi zamieszczonej tutaj przez @ nightcoder.)

Ogólny wzór byłby więc taki:

Sub WrapperThatCleansUp()

    ' NOTE: Don't call Excel objects in here... 
    '       Debugger would keep alive until end, preventing GC cleanup

    ' Call a separate function that talks to Excel
    DoTheWork()

    ' Now let the GC clean up (twice, to clean up cycles too)
    GC.Collect()    
    GC.WaitForPendingFinalizers()
    GC.Collect()    
    GC.WaitForPendingFinalizers()

End Sub

Sub DoTheWork()
    Dim app As New Microsoft.Office.Interop.Excel.Application
    Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add()
    Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1")
    app.Visible = True
    For i As Integer = 1 To 10
        worksheet.Cells.Range("A" & i).Value = "Hello"
    Next
    book.Save()
    book.Close()
    app.Quit()

    ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed
End Sub

Istnieje wiele fałszywych informacji i zamieszania na ten temat, w tym wiele posty na MSDN i na Stack Overflow (a szczególnie to pytanie!).

To, co w końcu przekonało mnie do bliższego przyjrzenia się i znalezienia właściwych rad, to post na bloguszeryfie.ReleaseComObject uznane za niebezpieczne wraz ze znalezieniem problemu z referencjami utrzymywanymi przy życiu pod debuggerem, który mylił moje wcześniejsze testy.

 13
Author: Govert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:31:20

Zwykli Programiści, żadne z Twoich rozwiązań nie działało dla mnie, postanowiłem więc wdrożyć nową sztuczkę .

Najpierw określ "jaki jest nasz cel?"=>"Nie zobaczyć obiektu excel po naszej pracy w Menedżerze Zadań"

Ok. Niech nie kwestionować i zacząć go niszczyć, ale nie należy niszczyć innych instancji systemu operacyjnego Excel, które działają równolegle.

Więc pobierz listę bieżących procesorów i pobierz PID procesów EXCEL , a gdy twoje zadanie zostanie wykonane, mamy nowego gościa w liście procesów z unikalnym PID ,znajdź i zniszcz tylko ten jeden.

]}

Process[] prs = Process.GetProcesses();
List<int> excelPID = new List<int>();
foreach (Process p in prs)
   if (p.ProcessName == "EXCEL")
       excelPID.Add(p.Id);

.... // your job 

prs = Process.GetProcesses();
foreach (Process p in prs)
   if (p.ProcessName == "EXCEL" && !excelPID.Contains(p.Id))
       p.Kill();
To rozwiązuje mój problem, mam nadzieję, że Twój też.
 12
Author: Mohsen Afshin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 11:01:36

Przyjęta tutaj odpowiedź jest prawidłowa, ale należy również pamiętać, że należy unikać nie tylko odniesień do "dwóch kropek", ale także obiektów, które są pobierane za pośrednictwem indeksu. Nie musisz też czekać, aż skończysz z programem, aby wyczyścić te obiekty, najlepiej jest utworzyć funkcje, które wyczyszczą je tak szybko, jak tylko skończysz z nimi, jeśli to możliwe. Oto funkcja, którą stworzyłem, która przypisuje pewne właściwości obiektu stylu o nazwie xlStyleHeader:

public Excel.Style xlStyleHeader = null;

private void CreateHeaderStyle()
{
    Excel.Styles xlStyles = null;
    Excel.Font xlFont = null;
    Excel.Interior xlInterior = null;
    Excel.Borders xlBorders = null;
    Excel.Border xlBorderBottom = null;

    try
    {
        xlStyles = xlWorkbook.Styles;
        xlStyleHeader = xlStyles.Add("Header", Type.Missing);

        // Text Format
        xlStyleHeader.NumberFormat = "@";

        // Bold
        xlFont = xlStyleHeader.Font;
        xlFont.Bold = true;

        // Light Gray Cell Color
        xlInterior = xlStyleHeader.Interior;
        xlInterior.Color = 12632256;

        // Medium Bottom border
        xlBorders = xlStyleHeader.Borders;
        xlBorderBottom = xlBorders[Excel.XlBordersIndex.xlEdgeBottom];
        xlBorderBottom.Weight = Excel.XlBorderWeight.xlMedium;
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        Release(xlBorderBottom);
        Release(xlBorders);
        Release(xlInterior);
        Release(xlFont);
        Release(xlStyles);
    }
}

private void Release(object obj)
{
    // Errors are ignored per Microsoft's suggestion for this type of function:
    // http://support.microsoft.com/default.aspx/kb/317109
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
    }
    catch { } 
}

Zauważ, że ja musiałem ustawić xlBorders[Excel.XlBordersIndex.xlEdgeBottom] na zmienną, aby to wyczyścić (nie z powodu dwóch kropek, które odnoszą się do wyliczenia, które nie musi być zwolnione, ale dlatego, że obiekt, o którym mówię, jest w rzeczywistości obiektem granicznym, który musi być zwolniony).

[[3]}tego rodzaju rzeczy nie są tak naprawdę konieczne w standardowych aplikacjach, które wykonują świetną robotę sprzątania po sobie, ale w ASP.NET aplikacje, jeśli pominiesz chociaż jedną z nich, bez względu na to, jak często dzwonisz do garbage collector, Excel nadal będzie działał na twoim serwerze.

Wymaga to dużej dbałości o szczegóły i wielu testów podczas monitorowania Menedżera zadań podczas pisania tego kodu, ale oszczędza ci to kłopotów z rozpaczliwym przeszukiwaniem stron kodu, aby znaleźć jedną instancję, którą przegapiłeś. Jest to szczególnie ważne podczas pracy z pętlami, gdzie trzeba zwolnić każdą instancję obiektu, nawet jeśli używa On tej samej nazwy zmiennej za każdym razem, gdy pętli.

 9
Author: Chris McGrath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-18 17:22:48

Aby dodać do powodów, dla których Excel nie zamyka się, nawet gdy tworzysz bezpośrednie odwołania do każdego obiektu po odczycie, Tworzenie jest pętlą "For".

For Each objWorkBook As WorkBook in objWorkBooks 'local ref, created from ExcelApp.WorkBooks to avoid the double-dot
   objWorkBook.Close 'or whatever
   FinalReleaseComObject(objWorkBook)
   objWorkBook = Nothing
Next 

'The above does not work, and this is the workaround:

For intCounter As Integer = 1 To mobjExcel_WorkBooks.Count
   Dim objTempWorkBook As Workbook = mobjExcel_WorkBooks.Item(intCounter)
   objTempWorkBook.Saved = True
   objTempWorkBook.Close(False, Type.Missing, Type.Missing)
   FinalReleaseComObject(objTempWorkBook)
   objTempWorkBook = Nothing
Next
 9
Author: Grimfort,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-06 13:08:35

Wygląda na to, że to było zbyt skomplikowane. Z mojego doświadczenia wynika, że są tylko trzy kluczowe rzeczy, aby poprawnie zamknąć Excela: {]}

1: Upewnij się, że nie ma pozostałych odniesień do utworzonej aplikacji excel (i tak powinieneś mieć tylko jedną; ustaw ją na null)

2: call GC.Collect()

3: Excel musi zostać zamknięty, albo przez użytkownika ręcznie zamykającego program, albo przez wywołanie {[3] } w obiekcie Excel. (Zauważ, że Quit będzie działać tak, jakby użytkownik próbował zamknąć program i wyświetli okno dialogowe potwierdzenia, jeśli nie są niezapisane zmiany, nawet jeśli Excel nie jest widoczny. Użytkownik może nacisnąć Anuluj, a następnie Excel nie zostanie zamknięty.)

1 musi się wydarzyć przed 2, ale 3 może się wydarzyć w każdej chwili.

Jednym ze sposobów implementacji tego jest owinięcie obiektu interop Excel własną klasą, utworzenie instancji interop w konstruktorze i zaimplementowanie IDisposable z Disposable wyglądającym jak

if (!mDisposed) {
   mExcel = null;
   GC.Collect();
   mDisposed = true;
}

To będzie oczyść Excela ze strony programu. Po zamknięciu programu Excel (ręcznie przez użytkownika lub przez wywołanie Quit) Proces zniknie. Jeśli program został już zamknięty, to proces zniknie po wywołaniu GC.Collect().

(nie jestem pewien, jak ważne jest to, ale możesz chcieć wywołać GC.WaitForPendingFinalizers() po wywołaniu GC.Collect(), ale nie jest to bezwzględnie konieczne, aby pozbyć się procesu Excela.)

To działa dla mnie bez problemu od lat. Należy jednak pamiętać, że podczas gdy to działa, trzeba zamykać z wdziękiem, żeby zadziałało. Nadal będziesz się gromadzić excel.exe przetwarza, jeśli przerwiesz program przed wyczyszczeniem programu Excel(Zwykle naciskając "stop" podczas debugowania programu).
 9
Author: Dave Cousineau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-20 23:01:29

Po próbie

  1. Release COM objects in reverse order
  2. dodaj GC.Collect() i GC.WaitForPendingFinalizers() dwa razy na końcu
  3. nie więcej niż dwie kropki
  4. Zamknij skoroszyt i zamknij aplikację
  5. Uruchom w trybie release

Ostatecznym rozwiązaniem, które działa dla mnie jest przeniesienie jednego zestawu

GC.Collect();
GC.WaitForPendingFinalizers();

Że dodaliśmy na końcu funkcji do wrappera w następujący sposób:

private void FunctionWrapper(string sourcePath, string targetPath)
{
    try
    {
        FunctionThatCallsExcel(sourcePath, targetPath);
    }
    finally
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}
 9
Author: D.G.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:22:44

Tradycyjnie stosowałem się do rad zawartych w odpowiedzi VVS. Jednak w celu utrzymania tej odpowiedzi na bieżąco z najnowszymi opcjami, myślę, że wszystkie moje przyszłe projekty będą korzystać z biblioteki "NetOffice".

NetOffice jest kompletnym zamiennikiem pakietu Office PIAs i jest całkowicie niezależny od wersji. Jest to zbiór zarządzanych wrapperów COM, które mogą obsłużyć oczyszczanie, które często powoduje takie bóle głowy podczas pracy z pakietem Microsoft Office w .NET.

Niektóre najważniejsze cechy to:

  • głównie niezależne od wersji (i funkcje zależne od wersji są udokumentowane)
  • brak zależności
  • No PIA
  • Brak rejestracji
  • No VSTO

Nie jestem w żaden sposób związany z projektem; po prostu naprawdę doceniam wyraźne zmniejszenie bólu głowy.

 8
Author: BTownTKD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 20:16:33

°º¤ø "Strzelaj Excel proc i żuj gumę do żucia" ø ¤ º°

public class MyExcelInteropClass
{
    Excel.Application xlApp;
    Excel.Workbook xlBook;

    public void dothingswithExcel() 
    {
        try { /* Do stuff manipulating cells sheets and workbooks ... */ }
        catch {}
        finally {KillExcelProcess(xlApp);}
    }

    static void KillExcelProcess(Excel.Application xlApp)
    {
        if (xlApp != null)
        {
            int excelProcessId = 0;
            GetWindowThreadProcessId(xlApp.Hwnd, out excelProcessId);
            Process p = Process.GetProcessById(excelProcessId);
            p.Kill();
            xlApp = null;
        }
    }

    [DllImport("user32.dll")]
    static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
}
 7
Author: Antoine Meltzheim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-18 10:25:07

Musisz mieć świadomość, że Excel jest bardzo wrażliwy na kulturę, pod którą działasz.

Może się okazać, że musisz ustawić kulturę NA en-US przed wywołaniem funkcji Excela. Nie dotyczy to wszystkich funkcji - ale niektórych z nich.

    CultureInfo en_US = new System.Globalization.CultureInfo("en-US"); 
    System.Threading.Thread.CurrentThread.CurrentCulture = en_US;
    string filePathLocal = _applicationObject.ActiveWorkbook.Path;
    System.Threading.Thread.CurrentThread.CurrentCulture = orgCulture;

Dotyczy to nawet jeśli używasz VSTO.

Szczegóły: http://support.microsoft.com/default.aspx?scid=kb;PL-us; Q320369

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-06 15:21:25

Podążałem za tym dokładnie... Ale wciąż natknąłem się na problemy 1 na 1000 razy. Kto wie dlaczego. Czas wyciągnąć młotek...

Zaraz po utworzeniu klasy aplikacji Excel uzyskuję dostęp do procesu Excel, który został właśnie utworzony.

excel = new Microsoft.Office.Interop.Excel.Application();
var process = Process.GetProcessesByName("EXCEL").OrderByDescending(p => p.StartTime).First();

Potem, gdy już wykonam wszystkie powyższe sprzątanie COM, upewniam się, że proces nie działa. Jeśli nadal działa, zabij go!

if (!process.HasExited)
   process.Kill();
 6
Author: craigtadlock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:24:06

"nigdy nie używaj dwóch kropek z obiektami COM" jest świetną zasadą, aby uniknąć wycieku odniesień COM, ale Excel PIA może prowadzić do wycieku na więcej sposobów niż jest to widoczne na pierwszy rzut oka.

Jednym z tych sposobów jest zapisywanie każdego zdarzenia ujawnionego przez dowolny obiekt COM modelu obiektowego Excela.

Na przykład zapisanie się do zdarzenia WorkbookOpen klasy aplikacji.

Niektóre teorie na temat zdarzeń COM

Klasy COM ujawniają grupę zdarzeń poprzez interfejsy wywołania zwrotnego. W aby subskrybować zdarzenia, kod klienta może po prostu zarejestrować obiekt implementujący interfejs call-back, a klasa COM wywoła swoje metody w odpowiedzi na określone zdarzenia. Ponieważ interfejs wywołania zwrotnego jest interfejsem COM, obowiązkiem obiektu implementującego jest zmniejszenie liczby referencji dowolnego obiektu COM, który otrzymuje (jako parametr) dla dowolnej procedury obsługi zdarzeń.

Jak Excel Pia ujawnia zdarzenia COM

Excel Pia eksponuje zdarzenia COM aplikacji Excel klasy jako konwencjonalne zdarzenia. NET. Gdy kod klienta subskrybuje Zdarzenie. NET (z naciskiem na 'a'), Pia tworzy instancję klasy implementującej interfejs wywołania zwrotnego i rejestruje go w programie Excel.

W związku z tym wiele obiektów oddzwaniania jest rejestrowanych w programie Excel w odpowiedzi na różne żądania subskrypcji z kodu.NET. Jeden obiekt oddzwaniający na subskrypcję zdarzeń.

Interfejs call-back do obsługi zdarzeń oznacza, że PIA musi Subskrybuj wszystkie zdarzenia interfejsu dla każdego żądania subskrypcji. Net event. Nie może wybierać i wybierać. Po odebraniu wywołania zdarzenia obiekt wywołania sprawdza, czy skojarzony moduł obsługi zdarzenia. NET jest zainteresowany bieżącym zdarzeniem, czy nie, a następnie albo wywołuje ten moduł obsługi, albo po cichu ignoruje wywołanie.

Wpływ na liczbę referencji instancji COM

Wszystkie te obiekty wywołujące nie zmniejszają liczby referencji żadnego z obiektów COM, które otrzymują (jako parametry) dla każdej z metod wywołania zwrotnego (nawet dla tych, które są po cichu ignorowane). Polegają wyłącznie na CLR garbage collector, aby uwolnić obiekty COM.

Ponieważ GC run nie jest deterministyczny, może to prowadzić do wstrzymania procesu Excela na dłuższy czas niż jest to pożądane i stworzyć wrażenie "wycieku pamięci".

Rozwiązanie

Jedynym rozwiązaniem na razie jest uniknięcie dostawcy zdarzeń PIA dla klasy COM i napisanie własnego dostawcy zdarzeń, który deterministycznie uwalnia obiekty COM.

Dla klasy aplikacji można to zrobić poprzez implementację interfejsu AppEvents, a następnie zarejestrowanie implementacji w programie Excel za pomocą interfejsu IConnectionPointContainer . Klasa aplikacji (a w tym przypadku wszystkie obiekty COM wystawiające zdarzenia za pomocą mechanizmu callback) implementuje interfejs IConnectionPointContainer.

 6
Author: Amit Mittal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:33:33

Jak zauważyli inni, musisz utworzyć wyraźne odniesienie dla każdego używanego obiektu Excel i wywołać Marshal.ReleaseComObject on that reference, as described in this KB article . Musisz również użyć try / finally, aby upewnić się, że ReleaseComObject jest zawsze wywoływany, nawet jeśli zostanie wyrzucony wyjątek. Czyli zamiast:

Worksheet sheet = excelApp.Worksheets(1)
... do something with sheet

Musisz zrobić coś takiego:

Worksheets sheets = null;
Worksheet sheet = null
try
{ 
    sheets = excelApp.Worksheets;
    sheet = sheets(1);
    ...
}
finally
{
    if (sheets != null) Marshal.ReleaseComObject(sheets);
    if (sheet != null) Marshal.ReleaseComObject(sheet);
}

Musisz również zadzwonić do aplikacji.Zamknij przed zwolnieniem obiektu aplikacji, Jeśli chcesz programu Excel do zamknięcia.

Jak widzisz, to szybko staje się bardzo nieporęczne, gdy tylko spróbujesz zrobić coś nawet umiarkowanie złożonego. Z powodzeniem opracowałem aplikacje. NET z prostą klasą owijającą, która owija kilka prostych manipulacji modelem obiektowym Excel (otwórz skoroszyt, Zapisz do zakresu, zapisz/zamknij skoroszyt itp.). Klasa wrapper implementuje IDisposable, starannie implementuje Marshala.ReleaseComObject na każdym obiekcie, którego używa i nie wyświetla żadnego Excela obiektów do reszty aplikacji.

Ale to podejście nie jest dobrze skalowane dla bardziej złożonych wymagań.

Jest to duży niedobór. NET COM Interop. W przypadku bardziej złożonych scenariuszy, poważnie rozważyłbym napisanie ActiveX DLL w VB6 lub innym niezarządzanym języku, do którego można delegować wszystkie interakcje z obiektami out-proc COM, takimi jak Office. Następnie możesz odwołać się do tej biblioteki DLL ActiveX z aplikacji. NET, a wszystko będzie o wiele łatwiejsze, ponieważ wystarczy tylko uwolnij to jedno odniesienie.

 5
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 19:32:29

Gdy wszystkie powyższe rzeczy nie zadziałały, spróbuj dać Excelowi trochę czasu na zamknięcie arkuszy:

app.workbooks.Close();
Thread.Sleep(500); // adjust, for me it works at around 300+
app.Quit();

...
FinalReleaseComObject(app);
 4
Author: spiderman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-18 12:33:57

Upewnij się, że zwolnisz wszystkie obiekty związane z Excelem!

Spędziłem kilka godzin próbując na kilka sposobów. Wszystkie są świetnymi pomysłami, ale w końcu znalazłem swój błąd: Jeśli nie uwolnisz wszystkich obiektów, żaden z powyższych sposobów nie może Ci pomóc jak w moim przypadku. Upewnij się, że zwolnisz wszystkie obiekty, w tym range one!
Excel.Range rng = (Excel.Range)worksheet.Cells[1, 1];
worksheet.Paste(rng, false);
releaseObject(rng);

Opcje są razem tutaj .

 3
Author: Ned,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-08 18:38:33

Zasada dwóch kropek nie zadziałała dla mnie. W moim przypadku stworzyłem metodę czyszczenia moich zasobów w następujący sposób:

private static void Clean()
{
    workBook.Close();
    Marshall.ReleaseComObject(workBook);
    excel.Quit();
    CG.Collect();
    CG.WaitForPendingFinalizers();
}
 2
Author: Hahnemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-12 22:37:19

Powinieneś być bardzo ostrożny używając aplikacji Word / Excel interop. Po wypróbowaniu wszystkich rozwiązań nadal mieliśmy dużo procesu "WinWord" otwarty na serwerze (z ponad 2000 użytkowników).

Po wielogodzinnej pracy nad problemem zdałem sobie sprawę, że jeśli otworzę więcej niż kilka dokumentów używając Word.ApplicationClass.Document.Open() Na różnych wątkach jednocześnie, proces IIS worker (w3wp.exe) spowoduje awarię pozostawiając otwarte wszystkie procesy WinWord!

Więc chyba nie ma absolutnego rozwiązania tego problemu, ale przejście na inne metody, takie jak Office Open XML development.

 2
Author: Arvand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 14:43:38

Świetny artykuł o uwalnianiu obiektów COM jest 2.5 zwalnianie obiektów COM (MSDN).

Metoda, którą zalecam, to unieważnienie Twojego Excela.Odwołania Interop jeśli są zmiennymi nielokalnymi, a następnie wywołują GC.Collect() i GC.WaitForPendingFinalizers() dwa razy. Lokalnie scopowane zmienne Interop będą obsługiwane automatycznie.

Eliminuje to konieczność przechowywania nazwanego odniesienia dla każdego obiektu COM.

Oto przykład zaczerpnięty z Artykuł:

public class Test {

    // These instance variables must be nulled or Excel will not quit
    private Excel.Application xl;
    private Excel.Workbook book;

    public void DoSomething()
    {
        xl = new Excel.Application();
        xl.Visible = true;
        book = xl.Workbooks.Add(Type.Missing);

        // These variables are locally scoped, so we need not worry about them.
        // Notice I don't care about using two dots.
        Excel.Range rng = book.Worksheets[1].UsedRange;
    }

    public void CleanUp()
    {
        book = null;
        xl.Quit();
        xl = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Te słowa pochodzą prosto z artykułu:

W prawie wszystkich sytuacjach anulowanie odniesienia do RCW i wymuszenie zbierania śmieci będzie czyszczone prawidłowo. Jeśli zadzwonisz również do GC.WaitForPendingFinalizers, garbage collection będzie tak deterministyczny,jak to tylko możliwe. Oznacza to, że będziesz prawie pewien dokładnie, kiedy obiekt został oczyszczony-po powrocie z drugiego połączenia do WaitForPendingFinalizers. Jako alternatywę można użyć Szeryfie.ReleaseComObject. Należy jednak pamiętać, że jest bardzo mało prawdopodobne, aby kiedykolwiek trzeba było używać tej metody.

 2
Author: Porkbutts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:13:17

Moje rozwiązanie

[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

private void GenerateExcel()
{
    var excel = new Microsoft.Office.Interop.Excel.Application();
    int id;
    // Find the Excel Process Id (ath the end, you kill him
    GetWindowThreadProcessId(excel.Hwnd, out id);
    Process excelProcess = Process.GetProcessById(id);

try
{
    // Your code
}
finally
{
    excel.Quit();

    // Kill him !
    excelProcess.Kill();
}
 1
Author: Loart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-30 18:40:18

Zaakceptowana odpowiedź nie zadziałała na mnie. Poniższy kod w destruktorze wykonał zadanie.

if (xlApp != null)
{
    xlApp.Workbooks.Close();
    xlApp.Quit();
}

System.Diagnostics.Process[] processArray = System.Diagnostics.Process.GetProcessesByName("EXCEL");
foreach (System.Diagnostics.Process process in processArray)
{
    if (process.MainWindowTitle.Length == 0) { process.Kill(); }
}
 1
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 12:49:35

Obecnie pracuję nad automatyzacją biur i natknąłem się na rozwiązanie, które działa za każdym razem dla mnie. Jest to proste i nie wymaga zabijania żadnych procesów.

Wydaje się, że poprzez zwykłe zapętlenie bieżących aktywnych procesów i w jakikolwiek sposób 'dostęp' do otwartego procesu Excela, każda zawieszona instancja Excela zostanie usunięta. Poniższy kod po prostu sprawdza procesy, których nazwa to "Excel", a następnie zapisuje właściwość mainwindowtitle procesu do sznurek. Ta "interakcja" z procesem wydaje się sprawiać, że system Windows nadrabia zaległości i przerywa zamrożoną instancję programu Excel.

Uruchamiam poniższą metodę tuż przed dodaniem, w którym rozwijam quits, ponieważ uruchamia ono Zdarzenie rozładowujące. Usuwa za każdym razem wiszące instancje programu Excel. Szczerze mówiąc nie jestem do końca pewien, dlaczego to działa, ale działa dobrze dla mnie i może być umieszczony na końcu każdej aplikacji Excela bez martwienia się o podwójne kropki, Marszałku.ReleaseComObject, nor procesy zabijania. Byłbym bardzo zainteresowany wszelkimi sugestiami, dlaczego jest to skuteczne.

public static void SweepExcelProcesses()
{           
            if (Process.GetProcessesByName("EXCEL").Length != 0)
            {
                Process[] processes = Process.GetProcesses();
                foreach (Process process in processes)
                {
                    if (process.ProcessName.ToString() == "excel")
                    {                           
                        string title = process.MainWindowTitle;
                    }
                }
            }
}
 1
Author: Tom Brearley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-02 14:32:38

Myślę, że niektóre z nich są po prostu sposobem, w jaki Framework obsługuje aplikacje biurowe, ale mogę się mylić. W niektóre dni niektóre aplikacje natychmiast czyszczą procesy, a w inne dni wydaje się czekać, aż aplikacja zostanie zamknięta. Ogólnie rzecz biorąc, przestaję zwracać uwagę na szczegóły i po prostu upewniam się, że pod koniec dnia nie ma żadnych dodatkowych procesów.

Również, i może jestem już za upraszczaniem rzeczy, ale myślę, że można tylko...

objExcel = new Excel.Application();
objBook = (Excel.Workbook)(objExcel.Workbooks.Add(Type.Missing));
DoSomeStuff(objBook);
SaveTheBook(objBook);
objBook.Close(false, Type.Missing, Type.Missing);
objExcel.Quit();

Jak mówiłem wcześniej, nie zwracam uwagi na szczegóły, kiedy pojawia się lub znika proces Excela, ale to zwykle działa dla mnie. Nie lubię też zatrzymywać procesów Excela przez coś innego niż minimalny czas, ale prawdopodobnie mam paranoję.

 1
Author: bill_the_loser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 14:36:41

Jak już zapewne niektórzy pisali, nie tylko ważne jest, jak zamknąć Excel (obiekt); ważne jest również, jak Otworzyć , a także według typu projektu.

W aplikacji WPF zasadniczo ten sam kod działa bez lub z kilkoma problemami.

Mam projekt, w którym ten sam plik Excel jest przetwarzany kilka razy dla różnych wartości parametru - np. parsowanie go na podstawie wartości wewnątrz listy ogólnej.

I put all Funkcje związane z Excelem do klasy bazowej, a parser do podklasy (różne parsery używają wspólnych funkcji Excela). Nie chciałem, aby Excel był ponownie otwierany i zamykany dla każdego elementu na liście ogólnej, więc otworzyłem go tylko raz w klasie bazowej i zamknąłem w podklasie. Miałem problemy podczas przenoszenia kodu do aplikacji desktopowej. Wypróbowałem wiele z wyżej wymienionych rozwiązań. GC.Collect() został już wdrożony wcześniej, dwa razy tak jak sugerowano.

Więc zdecydowałem, że przeprowadzę się kod do otwarcia Excela do podklasy. Zamiast otwierać tylko raz, teraz tworzę nowy obiekt (klasę bazową) i otwieram Excela dla każdego elementu i zamykam go na końcu. Istnieje pewna kara wydajnościowa, ale na podstawie kilku testów procesy Excela zamykają się bez problemów( w trybie debugowania), więc również pliki tymczasowe są usuwane. Będę kontynuował testy i napisać więcej, jeśli otrzymam jakieś aktualizacje.

Najważniejsze jest to, że musisz również sprawdzić kod inicjalizacji, zwłaszcza jeśli masz wiele klas itp.

 1
Author: Blaz Brencic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 15:20:36