Excel - ukončení procesu   zodpovězená otázka

VB.NET, Office

Zdravím, používám šablony excelu (.XLT) pro vytváření reportů z mé aplikace. Narazil jsem zde na problém, že ten excel zůstane viset v procesech ve správci úloh i pokud ho uživatel zavře.

ukázka vytváření excelu:

        'vytvořit a spustit excel
        Dim e As New Microsoft.Office.Interop.Excel.Application()
        e.Workbooks.Open(IO.Path.Combine(Application.StartupPath, "Templates\" & sablona_nazev)).Activate()


'zde načítám do excelu nějáká data
'...
'...
'...



'zobrazení excelu uživatel
 e.Visible = True

Pokud ale uživatel excel zavře, proces se neukončí. A to ani tehdy, pokud zavolám

e.Quit()
e=Nothing

Jak bych měl teda ten proces správně korektně ukončit?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém bude v tom, že v paměti zůstává instance COM objektu Excelu. Zkusil bych to řešit bez COM Interop Assembly, ale bude to mít určité nevýhody (Late Binding), tedy nepřidávejte do projektu referenci na to excelové COM rozhraní ale použijte:

'Vytvořit instanci
Dim excel = CreateObject("Excel.Application")
'Potom už normálně volat metody (e.Workbooks.Open)

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Díky za tip, bohužel problém přetrvává. Odstranil jsem i všechny reference na knihovny excelu a VBA.

Zřejmě bude problém v tom, že na konci nevolám správnou metodu. e.Quit() excel neuvolní z paměti, navíc ho ani volat nemůžu, protože bych tím uživateli zavřel sešti před nosem.

Nicméně jsem si myslel, že pokud objekt nastavím na nothing, mělo by dojít k ukončení procesu a uvolnění z paměti až to Garbage collector schroupne.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Navíc jsem teď zjistil, že mi z aplikace nejdou volat VBA metody, takže se asi budu muset vrátit k předchozímu řešení.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pozor. Garbage Collector uvolňuje pouze Managed věci, tj. řízený kód běžící v CLR. Veškeré COM komponenty jsou Unmanaged nativní kód, který se spouští v externím procesu. Takže záleží na tom jak je COM komponenta napsaná (pokud prasácky což u aplikací Office nepředpokládám, pak se nemusí uvolnit z paměti), nebo jestli nemá nějaké metody pro explicitní uvolnění z paměti.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Tak nakonec se mi podařilo něco vygooglit, hodil jsem to do procedury:

Imports Microsoft.Office.Interop
Imports System.Runtime.InteropServices


    Sub CloseExcel(ByRef objExcel As Excel.Application, ByRef objWorkBook As Excel.Workbook, ByRef objWorkSheet As Excel.Worksheet)

        GC.Collect()
        GC.WaitForPendingFinalizers()

        Marshal.FinalReleaseComObject(objWorkSheet)
        Marshal.FinalReleaseComObject(objWorkBook)
        objExcel.Quit()
        Marshal.FinalReleaseComObject(objExcel)
    End Sub

Vypadá to že to funguje bez problémů. Procedura po sobě uklidí, takže otevřené excely už nezůstávají viset v procesech.

Přiznávám ale, že mi není uplně jasný jak to funguje.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

No tak to je jasné, třída Marshal obsahuje metody pro práci s Unmanaged věcmi a FinalReleaseComObject je právě metoda pro uvolnění COM objektu z paměti. Uplně jsem na tuto důležitou metodu zapoměl.

Ještě bych měl poznámku k tomu snippetu co jste napsal. V té třídě se pracuje s unmanaged kódem a proto by bylo velmi profesionální implementovat v ní rozhraní IDisposable a potom by šlo na tuto třídu používat Using...End Using, přičemž by bylo zajištěno automatické uvolňování zdrojů. Tím že byste to implementoval i do destruktoru třídy by ani nebylo nutné volat Dispose, protože by se provedlo automaticky Garbage Collectorem a Excelový COM by byl automaticky uvolněn z paměti.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Díky za vysvěltení a připomínku. V pondělí se na tu třídu mrknu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Takže snippet upraven. Implementoval jsem rozhraní IDisposable, takže teď je možné používat i Using. Sice jsem se do toho trošku zamotal, protože jsem toto rozhraní zatím nikdy neimplementoval, nicméně se zdá že to funguje bez problémů.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

mě ani toto nakonec nefungovalo s Office 2003, jediné co pomohlo bylo toto:

Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Int32, ByVal wMsg As Int32, _
ByVal wParam As Int32, ByVal lParam As Int32) As Int32

    Const WM_QUIT = &H12

a potom (za předpokladu že EX je Excel.Application)

PostMessage(EX.Hwnd, WM_QUIT, 0, 0)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Co Vám konkrétně nefungoval? Mně to funguje tak jak má i bez API.

Jinak aby to šlapalo, tak musíte mít samozřejmě nareferencované příslušné knihovny, to jsem do toho snippetu zapomněl napsat.

jsou to:

Microsoft Excel 11.0 Object Library

Microsoft Office 11.0 Object Library

Microsoft Visual Basic for Applications Extensibility 5.3

Myslím si že verze knihoven můžou být i novější, pokud má uživatel novější office.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback