c # i excel automatyzuje zamykanie działającej instancji


Próbuję zautomatyzować Excel przez C #. Podążałem za wszystkim instrukcjami firmy Microsoft, jak to zrobić, ale nadal walczę o odrzucenie ostatniego linku do programu Excel, aby zamknąć i pozwolił GC zebrać ją.
Poniżej znajduje się przykładowy kod. Kiedy komentuję blok kodu zawierający struny podobne do:
Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();

plik jest zapisywany i program Excel kończy pracę. W przeciwnym razie plik jest zapisywany, ale program Excel nadal działa jako proces. Następnym razem, gdy ten kod zostanie uruchomiony, utworzy nową instancję, która ostatecznie się kumuluje.
Wszelka pomoc zostanie doceniona. Podziękować.
To są nagie kości mojego kodu:
Excel.Application xl = null;
Excel._Workbook wBook = null;
Excel._Worksheet wSheet = null;
Excel.Range range = null; object m_objOpt = System.Reflection.Missing.Value; try
{
// open the template
xl = new Excel.Application();
wBook = (Excel._Workbook)xl.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate, false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
wSheet = (Excel._Worksheet)wBook.ActiveSheet; int iRowCount = 2;// enumerate and drop the values straight into the Excel file
while (data.Read())
{ wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();
wSheet.Cells[iRowCount, 2] = data["brand"].ToString();
wSheet.Cells[iRowCount, 3] = data["agency"].ToString();
wSheet.Cells[iRowCount, 4] = data["advertiser"].ToString();
wSheet.Cells[iRowCount, 5] = data["product"].ToString();
wSheet.Cells[iRowCount, 6] = data["comment"].ToString();
wSheet.Cells[iRowCount, 7] = data["brief"].ToString();
wSheet.Cells[iRowCount, 8] = data["responseDate"].ToString();
wSheet.Cells[iRowCount, 9] = data["share"].ToString();
wSheet.Cells[iRowCount, 10] = data["status"].ToString();
wSheet.Cells[iRowCount, 11] = data["startDate"].ToString();
wSheet.Cells[iRowCount, 12] = data["value"].ToString(); iRowCount++;
} DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
_report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
wBook.Close(true, _report.ReportLocation, m_objOpt);
wBook = null; }
catch (Exception ex)
{
LogException.HandleException(ex);
}
finally
{
NAR(wSheet);
if (wBook != null)
wBook.Close(false, m_objOpt, m_objOpt);
NAR(wBook);
xl.Quit();
NAR(xl);
GC.Collect();
}private void NAR(object o)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
}
catch { }
finally
{
o = null;
}
}


Aktualizacja
>
Bez względu na to, co próbuję zrobić, metodę "czysty" lub metodę "brzydkiej" (patrz odpowiedzi poniżej), instancja Excel wciąż się zawiesia, gdy tylko ta linia spada:
wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();

Jeśli wykomentuję tę linię (i inne podobne, oczywiście poniżej), aplikacja Excel zakończy pracę z wdziękiem. Po odkomentowaniu jednej linii powyżej, Excel pozostaje na swoim miejscu.
Myślę, że będę musiał sprawdzić, czy zostanie uruchomiona instancja przed przypisaniem zmiennej XL, a zamiast tego połączyć się z nim. Zapomniałem wspomnieć, że jest to usługa systemu Windows, ale nie powinna istnieć, prawda?
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


UPDATE

(Listopad 2016.

)
Właśnie przeczytałem

przekonujący argument
https://stackoverflow.com/a/25135685/98422
Hans Passant mówi, że użycie
GC.Collect
jest właściwie właściwą drogą. Nie pracuję już z Office (dzięki Bogu), ale gdybym to zrobił, prawdopodobnie chciałbym spróbować ponownie - z pewnością uprościłoby to większość (tysiące wierszy) kodu, który napisałem, próbując zrobić to w ten sposób, jak „dobrze” ( tak jak wtedy to widziałem).
Zostawię moją pierwotną odpowiedź dla potomności ...
Gdy Mike mówi w swojej odpowiedzi, istnieje prosty i trudny sposób radzenia sobie z tym. Mike oferuje łatwy sposób, ponieważ ... łatwiej jest. Osobiście nie sądzę, że to dość dobry powód i nie wierzę, że jest to właściwy sposób. Dla mnie, uderza "wyłącz go i ponownie".
Mam kilka lat doświadczenia w tworzeniu aplikacji do automatyzacji pakietu Office w roku .NET i te problemy z interopem COM nękały mnie przez pierwsze kilka tygodni & amp; miesięcy, kiedy pierwszy raz zetknąłem się z tym problemem, nie tylko dlatego, że Microsoft bardzo skromnie przyznaje, że był problem w pierwszej kolejności, a dobrą radę trudno było wtedy znaleźć w Internecie.
Mam sposób pracy, że teraz używam praktycznie bez myślenia i minęło wiele lat, odkąd miałem problemy. Ważne jest, aby żyć dla wszystkich ukrytych przedmiotów, które możesz stworzyć, i tak, jeśli tęsknisz, możesz mieć wyciek, który stanie się oczywisty tylko później. Ale nie jest gorsza niż w starych złych czasach Malloc/
Free
.
Naprawdę myślę, że jest coś do powiedzenia o sprzątaniu po sobie, a nie na końcu. Jeśli dopiero zaczynasz od Excela, aby wypełnić kilka komórek, może to nie mieć znaczenia, ale jeśli masz zamiar wykonywać ciężkie prace, to inna sprawa.
W każdym przypadku, którego użyj metodę, jest użycie powłoki klasy, która implementuje Idisposable i który w
Wyrzuć
Wywołania ReliasceComObject . Dlatego mogę użyć wyciągów
za pomocą
, aby upewnić się, że obiekt zostanie usunięty (a obiekt COM zostanie zwolniony), gdy tylko się z nim skończy.
Ważne jest, aby pamiętać, że zostanie usunięty/zwolniony, nawet jeśli moja funkcja zwróci wcześniej, lub wystąpi wyjątek itp. Ponadto zostanie usunięta/zwolniona

tylko

w przypadku, gdy faktycznie został stworzony w pierwszej kolejności - nazwij mnie pedantem, ale proponowany kod, który próbuje zwolnić obiekty, które nie zostały faktycznie utworzone, wygląda dla mnie jak niechlujny kod. Mam podobny zarzut co do używania FinalReleaseComObject - trzeba wiedzieć, ile razy dzwoniłeś do tworzenia łącza COM, a zatem powinieneś mieć możliwość zwolnienia go tyle samo razy.
Typowy fragment mojego kodu może wyglądać tak (albo było, gdybym użył C # V2 i mógł użyć GenericS :-)):
using (ComWrapper<Excel.Application> application = new ComWrapper<Excel.Application>(new Excel.Application()))
{
try
{
using (ComWrapper<Excel.Workbooks> workbooks = new ComWrapper<Excel.Workbooks>(application.ComObject.Workbooks))
{
using (ComWrapper<Excel.Workbook> workbook = new ComWrapper<Excel.Workbook>(workbooks.ComObject.Open(...)))
{
using (ComWrapper<Excel.Worksheet> worksheet = new ComWrapper<Excel.Worksheet>(workbook.ComObject.ActiveSheet))
{
FillTheWorksheet(worksheet);
}
// Close the workbook here (see edit 2 below)
}
}
}
finally
{
application.ComObject.Quit();
}
}

Teraz nie zamierzam udawać, że to nie jest rozwlekłe, a wcięcie spowodowane tworzeniem obiektu tak

mogą

wymknąć się spod kontroli, jeśli nie podzielisz materiału na mniejsze metody. Ten przykład jest najgorszym przypadkiem, ponieważ wszystko, co robimy, to tworzenie obiektów. Zwykle dzieje się o wiele więcej między nawiasami, a znacznie mniej na górze.
Zauważ, że tak jak w powyższym przykładzie, zawsze będę przekazywał `` opakowane '' obiekty między metodami, a nie czysty obiekt COM, a wywołujący będzie odpowiedzialny za jego usunięcie (zwykle za pomocą instrukcji
using
). Podobnie zawsze zwracałem zapakowany przedmiot, nigdy nagi, a odpowiedzialność za jego uwolnienie ponownie spoczywała na dzwoniącym. Możesz użyć innego protokołu, ale ważne jest, aby mieć jasne zasady, tak jak wtedy, gdy przyzwyczailiśmy się do samodzielnego zarządzania pamięcią.
Klasa Comwrapper & LT; TH/Code> Używane tutaj, mam nadzieję, że wymaga niewielkiego wyjaśnienia. Wystarczy przechowywać link do owiniętego obiektu COM i uwalnia go wyraźnie (przy użyciu
ReliasceComObject
) w jego
Sposh
Metoda. Metoda
Comobject
Wystarczy zwrócić wpisany link do obiektu zawijanego COM.
Mam nadzieję że to pomoże!

EDIT
: Właśnie poszedłem na link do

T-shirt Odpowiedz na inne pytanie
https://coderoad.ru/158706/
I widzę, że inna odpowiedź na to pytanie ma odniesienie do skorupy klasy, jak miałem zostać rozwiązany.
Ponadto dotyczy odpowiedzi na temat Mike'a do tego innego pytania, muszę powiedzieć, że prawie uwiedziłem argument "Po prostu użyj
GC.Collect
". Niemniej jednak sprowadzono mnie na fałszywe tło; Na pierwszy rzut oka wydawało się, że nie będzie musiał martwić o odniesienia COM. Jednak, jak mówi Mike, nadal musisz wyraźnie wydać obiekty COM związane ze wszystkimi zmiennymi w dziedzinie widoczności, a zatem wszystko, co zrobiłeś, jest zmniejszona i nie usunął potrzeby zarządzania obiektami COM. Osobiście wolałbym iść na koniec.
Zwróć uwagę na tendencję w wielu odpowiedziach na zapisanie kodu, w którym wszystko jest zwolnione na końcu metody, w dużym bloku połączeń releasecomObject . Wszystko jest bardzo dobre, jeśli wszystko działa zgodnie z planem, ale wezwałbym wszystkich, którzy pisze poważny kod, aby pomyśleć o tym, co się stanie, jeśli zostanie rzucony wyjątek lub jeśli metoda będzie miała kilka punktów wyjściowych (kod nie jest wykonywany i, Dlatego obiekty COM nie zostaną zwolnione). Dlatego wolę używać "opakowania" i za pomocą s. Jest wyraźny, ale tworzy kodeksu kuloodpornego.

EDIT2
: Zaktualizowałem powyższy kod, aby wskazać, gdzie skoroszyt powinien zostać zamknięty z lub bez zapisywania zmian. Oto kod do zapisania zmian:
object saveChanges = Excel.XlSaveAction.xlSaveChanges;workbook.ComObject.Close(saveChanges, Type.Missing, Type.Missing);
.. i tak to

nie

Zapisz zmiany, po prostu zmień Xlsavechanges na
Xldonotsavechanges
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Co się dzieje, to twój telefon:
Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();

Jest to zasadniczo to samo:
Excel.Range cell = Sheet.Cells[iRowCount, 1];
cell.Value = data["fullname"].ToString();

Dokonywanie go w ten sposób można zobaczyć, że tworzysz obiekt Excel.Range , a następnie przypisz wartość do niego. Ta metoda daje nam również nazwany link do naszej zmiennej zasięgu, zmiennej
Cell
, co pozwala nam bezpłatnie uwolnić go bezpośrednio, jeśli schodzimy. W ten sposób możesz oczyścić swoje obiekty na dwa sposoby:
(1) twarda i brzydka ścieżka:
while (data.Read())
{
Excel.Range cell = Sheet.Cells[iRowCount, 1];
cell.Value = data["fullname"].ToString();
Marshal.FinalReleaseComObject(cell); cell = Sheet.Cells[iRowCount, 2];
cell.Value = data["brand"].ToString();
Marshal.FinalReleaseComObject(cell); cell = Sheet.Cells[iRowCount, 3];
cell.Value = data["agency"].ToString();
Marshal.FinalReleaseComObject(cell);// etc...
}

W powyższym przykładzie zwolniamy każdy zakres zakresu za pomocą
Marshal.FinalReleaseComObject (Cell)
Call, jak postępuj.
(2) łatwy i czysty sposób:
Zostaw swój kod dokładnie tak, jak masz teraz, a następnie na końcu możesz go wyczyścić w następujący sposób:
GC.Collect();
GC.WaitForPendingFinalizers();if (wSheet != null)
{
Marshal.FinalReleaseComObject(wSheet)
}
if (wBook != null)
{
wBook.Close(false, m_objOpt, m_objOpt);
Marshal.FinalReleaseComObject(wBook);
}
xl.Quit();
Marshal.FinalReleaseComObject(xl);

Krótko mówiąc, istniejący kod

bardzo blisko
... Jeśli po prostu dodasz wywołania do GC.Collect () i GC.WaitForPendingFinalizers () przed wywołaniami 'NAR', myślę, że to powinno działać dla Ciebie. (Krótko mówiąc, zarówno kod Jamiego, jak i kod Ahmada są poprawne. Kod Jamiego jest bardziej przejrzysty, ale kod Ahmada jest prostszą "szybką naprawą", ponieważ wystarczy dodać wywołania do wywołań GC.Collect () i GC. WaitForPendingFinalizers () do istniejącego kodu).
Jamie i Amhad znajdują się również linki do

forum automatyzacji
http://www.xtremevbtalk.com/fo ... 5.Net, w którym uczestniczę (dzięki faceci!) Oto kilka połączonych postów, które tu zrobiłem

StackOverflow
http://ww.StackOverflow.com
:
(1)

Jak poprawnie wyczyścić obiekty międzyoperacyjne programu Excel w języku C #
https://coderoad.ru/158706/
(2)

C # automatyzuje PowerPoint Excel - PowerPoint nie wyjdzie
https://coderoad.ru/981547/
Mam nadzieję, że to pomoże, Sean ...
Mikrofon
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Ponieważ inni już patrzyli na InterOp, sugeruję, że jeśli masz do czynienia z plikami Excel z rozszerzeniem XLSX, powinieneś użyć EPPlus

,
http://epplus.codeplex.com/
Co sprawi, że twoje koszmary znikną.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Dodaj następujące informacje przed połączeniem do XL.Quit ():
GC.Collect(); 
GC.WaitForPendingFinalizers();

Możesz także użyć
Marshal.FinalReleaseComObject ()
w swojej metodzie NAR zamiast ReleaseComObject.

ReleaseComObject
http://msdn.microsoft.com/en-u ... .aspx
Zmniejsza liczbę odniesień do 1, podczas gdy

FinalReleaseComObject
http://msdn.microsoft.com/en-u ... .aspx
Darmowe wszystkie odniesienia, więc ilość wynosi 0.
Tak więc twój ostatni blok będzie wyglądał tak:
finally
{
GC.Collect();
GC.WaitForPendingFinalizers(); NAR(wSheet);
if (wBook != null)
wBook.Close(false, m_objOpt, m_objOpt);
NAR(wBook);
xl.Quit();
NAR(xl);
}

Zaktualizowana metoda NAR:
private void NAR(object o)
{
try
{
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(o);
}
catch { }
finally
{
o = null;
}
}

Zbadałem to jakiś czas temu iw przykładach odkryłem, że zwykle połączenia związane z GC kończyły się po zamknięciu aplikacji. Jest jednak MVP (Mike Rosenblum), który wspomina, że ​​należy go wymienić na samym początku. Próbowałem obu sposobów i zadziałały. Wypróbowałem też bez WaitForPendingFinalizers i zadziałało, chociaż nie powinno to niczego boleć. YMMV.
Oto odpowiednie łącza MVP, o których wspomniałem (są w VB, ale nie jest tak różne):
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Właśnie odpowiedziałem na to pytanie tutaj:
Zabijanie procesu Excela przez jego główne okno hWnd
https://coderoad.ru/158706/
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Minęły ponad 4 lata, odkąd to zostało opublikowane, ale napotkałem ten sam problem i udało mi się go rozwiązać. Najwyraźniej samo uzyskanie dostępu do tablicy komórek powoduje utworzenie obiektu COM. Więc jeśli tak:
wSheet = (Excel._Worksheet)wBook.ActiveSheet;
Microsoft.Office.Interop.Excel.Range cells = wSheet.Cells; int iRowCount = 2;// enumerate and drop the values straight into the Excel file
while (data.Read())
{
Microsoft.Office.Interop.Excel.Range cell = cells[iRowCount, 1];
cell = data["fullname"].ToString();
Marshal.FinalReleaseComObject(cell);
}
Marshal.FinalReleaseComObject(cells);

A potem reszta czyszczenia musi rozwiązać ten problem.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Miałem podobny problem.
Usunąłem
_worksheet
i
_workbook
i wszystko było w porządku.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie ma potrzeby używania obiektów Excel Com z C #. Możesz użyć OLEDB, aby zmienić arkusze.
http://www.codeproject.com/KB/ office/excel_using_oledb. aspx
http://www.codeproject.com/KB/ ... .aspx
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Że skończyło się rozwiązać podobny problem, więc otrzymał identyfikator procesu i zabił go jako ostatnie środki ...
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindowThreadProcessId(int hWnd, out IntPtr lpdwProcessId);...objApp = new Excel.Application();IntPtr processID;
GetWindowThreadProcessId(objApp.Hwnd, out processID);
excel = Process.GetProcessById(processID.ToInt32());...objApp.Application.Quit();
Marshal.FinalReleaseComObject(objApp);
_excel.Kill();
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto zawartość mojego bloku, który nie został jeszcze uszkodzony, aby wyczyścić automatyzację programu Excel. Moja aplikacja pozostawia otwarty program Excel, więc nie ma połączenia z zakończeniem. Połączyć

W komentarzach
http://www.xtremevbtalk.com/sh ... 60433
było moim źródłem.
finally
{
// Cleanup -- See [url=http://www.xtremevbtalk.com/showthread.php?t=160433]http://www.xtremevbtalk.com/sh ... 60433[/url]
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
// Calls are needed to avoid memory leak
Marshal.FinalReleaseComObject(sheet);
Marshal.FinalReleaseComObject(book);
Marshal.FinalReleaseComObject(excel);
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Najprostszym sposobem na wstawienie kodu to pytanie - nie oznacza to, że odpowiedziałem na moje pytanie (niestety).
Przepraszam wszystkich, którzy starają się mi pomóc - do tej pory nie mogłem wrócić do tej kwestii. Wciąż mnie to intryguje ...
Całkowicie wyodrębniłem kod Excela w jedną funkcję zgodnie z
private bool GenerateDailyProposalsReport(ScheduledReport report)
{
// start of test Excel.Application xl = null;
Excel._Workbook wBook = null;
Excel._Worksheet wSheet = null;
Excel.Range xlrange = null;
object m_objOpt = System.Reflection.Missing.Value; xl = new Excel.Application();
wBook = (Excel._Workbook)xl.Workbooks.Open(@"E:\Development\Romain\APN\SalesLinkReportManager\ExcelTemplates\DailyProposalReport.xls", false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
wSheet = (Excel._Worksheet)wBook.ActiveSheet;
xlrange = wSheet.Cells[2, 1] as Excel.Range;// PROBLEM LINE ************
xlrange.Value2 = "fullname";
//************************** wBook.Close(true, @"c:\temp\DailyProposalReport.xls", m_objOpt);
xl.Quit(); GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers(); Marshal.FinalReleaseComObject(xlrange);
Marshal.FinalReleaseComObject(wSheet);
Marshal.FinalReleaseComObject(wBook);
Marshal.FinalReleaseComObject(xl); xlrange = null;
wSheet = null;
wBook = null;
xl = null;// end of test
return true;}

Jeśli wykomentuję problematyczną linię powyżej, wystąpienie programu Excel zostanie zwolnione z pamięci. W obecnej postaci tak nie jest.
Byłbym wdzięczny za dodatkową pomoc w tej sprawie, ponieważ czas jest ulotny i zbliża się do terminu (jeśli nie są).
Zapytaj, czy potrzebujesz dodatkowych informacji.
Dziękuję w oczekiwaniu.

Dodanie
>
Trochę więcej informacji, które mogą rzucić więcej światła na ten temat. Uciekłem się do zatrzymania procesu (zatrzymania pomiaru) po pewnym czasie (5-10 sekund, aby dać Excelowi czas na zakończenie procesu). Mam zaplanowane dwa raporty - pierwszy raport jest generowany i zapisywany na dysku, a proces programu Excel jest zabijany i wysyłany pocztą elektroniczną. Drugi jest tworzony, zapisywany na dysku, proces zostaje zabity, ale nagle pojawia się błąd podczas próby wysłania wiadomości e-mail. Błąd jest następujący:
Proces nie może uzyskać dostępu do pliku "...." itp.
W ten sposób nawet po zabiciu programu Excel rzeczywisty plik programu Excel jest nadal przechowywany przez usługę systemu Windows. Muszę wyłączyć usługę, aby usunąć plik ...
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Obawiam się, że kończą mi się pomysły, Shawn. :-(
Gary może mieć pewne przemyślenia, ale chociaż jego podejście opakowaniowe jest bardzo solidne, to naprawdę nie pomoże ci w tym przypadku, ponieważ już robisz to całkiem dobrze.
Wymienię tutaj kilka myśli. Nie wiem, jak którykolwiek z nich zadziała z powodu twojej tajemniczej linii
xlrange.Value2 = "fullname";

Wydawałoby się, że żaden z tych pomysłów nie wpłynie na niego, ale tutaj brzmi:
(1) Nie używaj interfejsów _Workbook i _Worksheet. Zamiast tego użyj skoroszytu i arkusza. (Więcej na ten temat:

Excel Interop: _workheet lub liść pracy?
https://coderoad.ru/1051464/
.)
(2) za każdym razem, gdy masz dwie kropki („.”) W tym samym wierszu podczas uzyskiwania dostępu do obiektu programu Excel podziel go na dwa wiersze, przypisując nazwaną zmienną do każdego obiektu. Następnie w sekcji oczyszczania kodu jawnie zwolnij każdą zmienną za pomocą Marshal.FinalReleaseComObject ().
Na przykład twój kod jest tutaj:
wBook = (Excel._Workbook)xl.Workbooks.Open(@"E:\Development\Romain\APN\SalesLinkReportManager\ExcelTemplates\DailyProposalReport.xls", false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);

można podzielić na:
Excel.Workbooks wBooks = xl.Workbooks;
wBook = wBooks.Open("@"E:\Development\...\DailyProposalReport.xls", etc...);

A następnie później, w sekcji czyszczenia:
Marshal.FinalReleaseComObject(xlrange);
Marshal.FinalReleaseComObject(wSheet);
Marshal.FinalReleaseComObject(wBook);
Marshal.FinalReleaseComObject(wBooks);// <-- Added
Marshal.FinalReleaseComObject(xl);

(3) Nie jestem pewien, co dzieje się z twoim procesem. Jeśli zadzwonisz do bBook.close (), a następnie xl.quit () przed wywołaniem procesu.kill (), nie powinieneś mieć żadnych problemów. Skorosteczka
Po wywołaniu Process.Kill () możesz sprawdzić właściwość Process.HasExited w pętli lub, lepiej, wywołać metodę Process.WaitForExit (), która zatrzyma się, dopóki nie zostanie zakończona. Domyślam się, że zwykle zajmuje to znacznie mniej niż sekundę. Lepiej czekać mniej czasu i być pewnym siebie, niż czekać 5-10 sekund i po prostu zgadywać.
(4) Powinieneś wypróbować te pomysły czyszczenia, które wymieniłem powyżej, ale zaczynam podejrzewać, że możesz mieć problemy z innymi procesami, które mogą działać z programem Excel, takimi jak dodatek lub program antywirusowy. Te dodatki mogą powodować zawieszanie się programu Excel, jeśli nie zostaną poprawnie wykonane. Jeśli tak się stanie, uzyskanie programu Excel do wydania może być bardzo trudne lub niemożliwe. Będziesz musiał dowiedzieć się, co to jest program, a następnie go wyłączyć. Inną możliwością jest to, że działanie jako usługa systemu Windows jest w jakiś sposób problemem. Nie rozumiem, dlaczego tak jest, ale nie mam doświadczenia w automatyzacji programu Excel za pośrednictwem usługi systemu Windows, więc nie mogę powiedzieć. Jeśli Twoje problemy są z tym związane, najprawdopodobniej jedynym rozwiązaniem będzie użycie Process.Kill.
To wszystko, o czym mogę myśleć, Sean. Mam nadzieję że to pomoże. Daj nam znać, jak leci ...
- Mike.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Sean,
Zamierzam ponownie opublikować swój kod z moimi zmianami (poniżej). Unikam zbyt wiele zmian w swoim kodzie, więc nie dodałem żadnej obsługi wyjątku itp. Ten kod

nie

jest niezawodny.
private bool GenerateDailyProposalsReport(ScheduledReport report)
{
Excel.Application xl = null;
Excel.Workbooks wBooks = null;
Excel.Workbook wBook = null;
Excel.Worksheet wSheet = null;
Excel.Range xlrange = null;
Excel.Range xlcell = null; xl = new Excel.Application(); wBooks = xl.Workbooks; wBook = wBooks.Open(@"DailyProposalReport.xls", false, false, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); wSheet = wBook.ActiveSheet; xlrange = wSheet.Cells; xlcell = xlrange[2, 1] as Excel.Range; xlcell.Value2 = "fullname"; Marshal.ReleaseComObject(xlcell);
Marshal.ReleaseComObject(xlrange);
Marshal.ReleaseComObject(wSheet); wBook.Close(true, @"c:\temp\DailyProposalReport.xls", Type.Missing); Marshal.ReleaseComObject(wBook);
Marshal.ReleaseComObject(wBooks); xl.Quit(); Marshal.ReleaseComObject(xl); return true;
}

Chwile do uwagi:
  • Skoroszyty
    Klasa Aplikacja Tworzy obiekt
    Workbooks
    , który zawiera link do odpowiedniego Obiekt COM, więc musimy się upewnić który następnie zwolnij ten link, Dlatego dodałem zmienną WBBOY i odpowiednie połączenie
    ReliasteComObject
    .
  • Podobnie,
    Komórki
    Metoda Arkusz Zwraca obiekt
    z innym odniesieniem COM, Dlatego musimy go wyczyścić. Stąd potrzeba dwóch oddzielnych zmiennych
    Range
    .
  • Wydałem linki COM (przy użyciu
    ReleaseComObject
    ) tak szybko, jak nie będą już potrzebne, co myślę jest dobrą praktyką, nawet jeśli tak jest Nie jest to niezbędne. Dodatkowo, Może to być przesąd uwolnił wszystkie posiadane przedmioty zarezerwuj przed zamknięciem książkę i uwolnił książkę przed zamknięciem programu Excel.
  • Nie dzwonię do
    GC.Collect
    itp. Ponieważ nie jest to konieczne. Prawdziwe!
  • Używam ReleaseComObject i Nie finalReaseComObject, ponieważ powinno to być wystarczająco dużo.
  • Nie jestem następnie zerowaniem zmiennych posługiwać się; Znowu, to nie nic wartościowego.
  • Nie ma tu znaczenia, ale dla wygody Użyj
    Type.Missing
    zamiast
    System.reflection.missing.Value
    . Roll do C # V4, gdzie Opcjonalne parametry będą Obsługiwany kompilatorowi!

Nie byłem w stanie skompilować ani uruchomić tego kodu, ale jestem prawie pewien, że zadziała.

Powodzenia!
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Czy rozważałeś użycie rozwiązania Net .NET, takie jak

SpreadshearGear dla .NET.
http://www.spreadsheetgear.com/
? Oto, jak może wyglądać Twój kod podczas korzystania z programu SpreadsheetGear:
// open the template 
using (IWorkbookSet workbookSet = SpreadsheetGear.Factory.GetWorkbookSet())
{
IWorkbook wBook = workbookSet.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate);
IWorksheet wSheet = wBook.ActiveWorksheet;
int iRowCount = 2;
// enumerate and drop the values straight into the Excel file
while (data.Read())
{
wSheet.Cells[iRowCount, 1].Value = data["fullname"].ToString();
wSheet.Cells[iRowCount, 2].Value = data["brand"].ToString();
wSheet.Cells[iRowCount, 3].Value = data["agency"].ToString();
wSheet.Cells[iRowCount, 4].Value = data["advertiser"].ToString();
wSheet.Cells[iRowCount, 5].Value = data["product"].ToString();
wSheet.Cells[iRowCount, 6].Value = data["comment"].ToString();
wSheet.Cells[iRowCount, 7].Value = data["brief"].ToString();
wSheet.Cells[iRowCount, 8].Value = data["responseDate"].ToString();
wSheet.Cells[iRowCount, 9].Value = data["share"].ToString();
wSheet.Cells[iRowCount, 10].Value = data["status"].ToString();
wSheet.Cells[iRowCount, 11].Value = data["startDate"].ToString();
wSheet.Cells[iRowCount, 12].Value = data["value"].ToString();
iRowCount++;
}
DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
_report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
wBook.SaveAs(_report.ReportLocation, FileFormat.OpenXMLWorkbook);
}

Jeśli masz więcej niż kilka linii, możesz być zszokowany, jak działa. I nigdy nie będziesz musiał się martwić o wiszącą instancję Excel.
Możesz pobrać bezpłatną wersję próbną.

tutaj
https://www.spreadsheetgear.co ... .aspx
I spróbuj sam.
Zastrzeżenie: Jestem właścicielem SpreadsheetGear LLC

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się