43

(Somewhat of a follow on from the post (which remains unanswered): https://stackoverflow.com/q/6197829/314661)

Using the following code

Application app = new Application();
_Document doc = app.Documents.Open("myDocPath.docx", false, false, false);
doc.PrintOut(false);
doc.Close();

I am attempting to open and print a file programmatically.

The problem is each time I run the above code a new WINWORD.exe process is started and obviously this quickly eats up all the memory.

The application class doesn't seem to contain a dispose/close or similar method.

After a bit of research I (realized) and changed the code to the following.

 Application app = new Application();
 _Document doc = app.Documents.Open(fullFilePath + ".doc", false, false, false);
 doc.PrintOut(false);
 doc.Close();
 int res = System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
 int res1 = System.Runtime.InteropServices.Marshal.ReleaseComObject(app);

And I can see the remaining reference count is zero but the processes remain?

PS: I'm using Version 14 of the Microsoft.Office.Interop library.

Community
  • 1
  • 1
Maxim Gershkovich
  • 45,951
  • 44
  • 147
  • 243

8 Answers8

54

Do you not need to call Quit?

app.Quit();
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
46

Perhaps try setting doc = null and calling GC.Collect()

Edit, not really my own code I forget where I got it but this is what I use to dispose of Excel, and it does the job maybe you can glean something from this:

public void DisposeExcelInstance()
{
    app.DisplayAlerts = false;
    workBook.Close(null, null, null);
    app.Workbooks.Close();
    app.Quit();
    if (workSheet != null)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(workSheet);
    if (workBook != null)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(workBook);
    if (app != null)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
    workSheet = null;
    workBook = null;
    app = null;
    GC.Collect(); // force final cleanup!
}
Nick
  • 1,903
  • 2
  • 21
  • 40
  • 14
    Absolute legand. App.Quit() was the key... Thank you! – Maxim Gershkovich Jul 21 '11 at 14:25
  • 1
    @gangelo, I realize that, but it is still Office Interop so I posted it in hopes that it would lead him to the answer, which it did. – Nick Jul 21 '11 at 14:28
  • to use GC.Collect is not so good idea it wil do an total clean up with GC. GC.Collect(3) would be better regarding to the GC.Collect function. but i agree with @Maxim app.Quit() is better – Lukas Huzen Feb 22 '12 at 11:02
  • @LukasHuzen while I agree that it is better practice to specify the generation with GC.Collect(), not calling it at all is not the answer. Perhaps it is an issue with 2010 as that is the only version I have tested with, but simply calling app.Quit() still leaves the process hanging in the background. – Nick Feb 22 '12 at 13:53
  • 4
    GC.Collect() is like Betelgeuse: you have to call it three times for it to respond in any observable way. – Yandros Jun 14 '12 at 21:44
  • Calling GC.Collect from application is not a good idea. Let dotnet framework decide when it is best to do a GC. – Nimesh Madhavan Jan 28 '13 at 20:12
  • 3
    There is no need for `GC.Collect()` nor `Marshal.ReleaseComObject()` if you close the document and quit the application –  Nov 28 '15 at 04:32
  • Calling GC.Collect is really only useful for diagnostics. If something "works" after calling GC.Collect, either something needs explicit cleanup, or there's a bug in libraries that you're using. – Andrew Rondeau Oct 07 '16 at 18:19
6

I think the main issue, which nobody seems to have picked up on, is that you shouldn't be creating a new Application object in the first place if Word is already open. Those of us who have been coding since the days of COM and/or VB6 will remember GetActiveObject. Fortunately .Net only requires a ProgID.

The recommended way of doing this is as follows:

try
{
    wordApp = (word.Application) Marshal.GetActiveObject("Word.Application");
}
catch(COMException ex) when (ex.HResult == -2147221021)
{
    wordApp = new word.Application();
}
Charlie
  • 69
  • 1
  • 1
5

The best solution.. last:

try {

    Microsoft.Office.Interop.Word.Application appWord = new Microsoft.Office.Interop.Word.Application();
    appWord.Visible = false;
    Microsoft.Office.Interop.Word.Document doc = null;
    wordDocument = appWord.Documents.Open((INP), ReadOnly: true);

    wordDocument.ExportAsFixedFormat(OUTP, Microsoft.Office.Interop.Word.WdExportFormat.wdExportFormatPDF);

    // doc.Close(false); // Close the Word Document.
    appWord.Quit(false); // Close Word Application.
} catch (Exception ex) {
    Console.WriteLine(ex.Message + "     " + ex.InnerException);
}
Pramod Karandikar
  • 5,289
  • 7
  • 43
  • 68
T Bass
  • 51
  • 1
  • 1
3

You need to calls app.Quit() to close the application. I used below code & it worked like a charm for me -

try
{
   Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();
   wordApp.Visible = false;
   Microsoft.Office.Interop.Word.Document doc = null;

   //Your code here...

   doc.Close(false); // Close the Word Document.
   wordApp.Quit(false); // Close Word Application.
}
catch (Exception ex)
{
   MessageBox.Show(ex.Message + "     " + ex.InnerException);
}
finally
{
   // Release all Interop objects.
   if (doc != null)
      System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
   if (wordApp != null)
      System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
   doc = null;
   wordApp = null;
   GC.Collect();
}
a1ashiish
  • 81
  • 4
  • 3
    Weird how it worked for you coz, your code should give build error , since doc and wordApp is created inside try and it wont be available in finally. – Unnie Jan 20 '15 at 09:46
  • There is no need for `GC.Collect()` nor `Marshal.ReleaseComObject()` if you close the document and quit the application –  Nov 28 '15 at 04:31
2

Agreed with other posters that GC.Collect() and Marshal.ReleaseComObject() is not needed. If the process still exists after running app.Quit(false), it might be because you're running the app invisible, and there is a prompt that is preventing the application from closing, such as a Document Recovery dialog. If that's the case, you need to add this when creating your application.

app.DisplayAlerts = false;
Jordan Ryder
  • 2,336
  • 1
  • 24
  • 29
1

Try this..

doc.Close(false);
app.Quit(false);
if (doc != null)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
if (app != null)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
doc = null;
app = null;
GC.Collect();
Asiri Jayaweera
  • 199
  • 1
  • 7
1

I close the document, then the application, that works for me, then force garbage collection.

// Document
object saveOptionsObject = saveDocument ? Word.WdSaveOptions.wdSaveChanges : Word.WdSaveOptions.wdDoNotSaveChanges;
this.WordDocument.Close(ref saveOptionsObject, ref Missing.Value, ref Missing.Value);

// Application
object saveOptionsObject = Word.WdSaveOptions.wdDoNotSaveChanges;
this.WordApplication.Quit(ref saveOptionsObject, ref Missing.Value, ref Missing.Value); 

GC.Collect();
GC.WaitForPendingFinalizers();
gangelo
  • 3,034
  • 4
  • 29
  • 43
  • 1
    There is no need for `GC.Collect()` nor `Marshal.ReleaseComObject()` if you close the document and quit the application –  Nov 28 '15 at 04:32