0

Got the two dots bad, one dot good thing down, marshalling com objects, setting everything to null at the end but Excel still won't get out of the task manager list. Here's what I have (try/catch/finally removed):

excelApp = new Application();
excelWbs = excelApp.Workbooks;
excelWb = excelWbs.Open(path);
excelWs = (Worksheet) excelWb.Worksheets["Information"];
cell = excelWs.Cells[2, 1];
ver = (cell as Range).Text;

excelWb.Close(false);
excelWbs.Close();
excelApp.Quit();

if (cell != null) Marshal.ReleaseComObject(cell);
if (excelWs != null) Marshal.ReleaseComObject(excelWs);
if (excelWb != null) Marshal.ReleaseComObject(excelWb);
if (excelWbs != null) Marshal.ReleaseComObject(excelWbs);
if (excelApp != null) Marshal.ReleaseComObject(excelApp);
cell = null;
excelWs = null;
excelWb = null;
excelWbs = null;
excelApp = null;

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

I've left it in the task manager hoping it would be collected at some point but no luck, just sits there until I kill it in task manager. I've also tried killing Excel but keep getting "Access Denied" when I do. I've tried a number of ways to kill the process.

Process[] excelProcs = Process.GetProcessesByName("EXCEL");
foreach (Process proc in excelProcs)
    proc.Kill();

or

int hWnd = excelApp.Application.Hwnd;
uint processID;
GetWindowThreadProcessId((IntPtr)hWnd, out processID);
Process.GetProcessById((int)processID).Kill();
Process.GetProcessById((int)processID).WaitForExit();

Still get the access denied error. What am I missing?

EDIT: Per Hans Passant Post

sWork = clsExcel.GetVersion();
GC.Collect();
GC.WaitForPendingFinalizers();
...
public string GetVersion()
{
    excelApp = new Application();
    excelWbs = excelApp.Workbooks;
    excelWb = excelWbs.Open(path);
    excelWs = (Worksheet) excelWb.Worksheets["Information"];
    cell = excelWs.Cells[2, 1];
    ver = (cell as Range).Text;
    excelWb.Close(false);
    excelWbs.Close();
    excelApp.Quit();
}    
AbdelAziz AbdelLatef
  • 3,432
  • 6
  • 21
  • 48
Velocedge
  • 908
  • 7
  • 24
  • You probably [shouldn't be calling `GC.Collect()` explicitly](https://stackoverflow.com/questions/478167/when-is-it-acceptable-to-call-gc-collect/21961777#21961777). I would say the classes you're using implement the `IDisposable` interface, in which case you wrap them in a `using` statement, e.g. `using (var application = new Application()) { ... }`. This gives scoped lifetimes for the managed resources, instead of relying on the garbage collector. – Andrew Williamson Sep 12 '19 at 22:28
  • 3
    https://stackoverflow.com/a/25135685/17034 – Hans Passant Sep 12 '19 at 22:28
  • Are you sure your code executes completely? No exceptions? You didn't stop the debugger once, leaving an unclosed process? – Eric J. Sep 12 '19 at 22:31
  • 1
    @AndrewWilliamson neither COM nor RCW honor `IDisposable`. https://stackoverflow.com/questions/25134024/clean-up-excel-interop-objects-with-idisposable/25135685#25135685 – MickyD Sep 12 '19 at 22:44
  • 1
    Not entirely sure why but I followed the post Hans Passant gave and took out all the ReleaseComObject stuff and setting things to null. This was called as a separate function so after the call, I added the GC.Collect and GC.WaitForPendingFinalizers and it worked!! – Velocedge Sep 12 '19 at 22:51
  • You didn't RCO `excelWb.Worksheets` or `excelWs.Cells`. https://ausdotnet.wordpress.com/2008/06/04/com-interop-principle-3-fear-the-hidden-period/ – mjwills Sep 12 '19 at 23:06
  • mfwills: Correct. In the Passant reference link, it said "There is no need to store these object references yourself and explicitly call Marshal.ReleaseComObject(), the CLR does it for you." Appears to be right! – Velocedge Sep 12 '19 at 23:10
  • `the CLR does it for you` It does it for you **eventually** yes @Velocedge. There are fundamentally two schools of thought re: the best way to solve this issue. One is to RCO, the other is to `GC.Collect()`. There are pros and cons of each approach (https://stackoverflow.com/a/47852419/34092). The point I am making is that if you use the RCO approach, you need to do it **diligently**. The RCO based approach only works if you RCO **everything**. If you aren't going to RCO **absolutely everything** then you may as well RCO nothing at all, and rely exclusively on `GC.Collect()`. – mjwills Sep 12 '19 at 23:11
  • I personally prefer the RCO based approach, but it definitely does require more diligence. The `GC.Collect` approach is simpler, and assuming you can live with its downsides then you should use it (particularly since it seems to be working for you). – mjwills Sep 12 '19 at 23:15

0 Answers0