Disposing of Microsoft.Office.Interop.Word.Application
Asked Answered
S

9

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.

Savill answered 21/7, 2011 at 14:11 Comment(0)
D
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!
}
Dinghy answered 21/7, 2011 at 14:18 Comment(8)
Absolute legand. App.Quit() was the key... Thank you!Savill
@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.Dinghy
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 betterSassanid
@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.Dinghy
GC.Collect() is like Betelgeuse: you have to call it three times for it to respond in any observable way.Suffice
Calling GC.Collect from application is not a good idea. Let dotnet framework decide when it is best to do a GC.Flee
There is no need for GC.Collect() nor Marshal.ReleaseComObject() if you close the document and quit the applicationHog
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.Corespondent
P
55

Do you not need to call Quit?

app.Quit();
Partake answered 21/7, 2011 at 14:27 Comment(3)
I got error BC30456: 'Quit' is not a member of 'Application'Troublesome
@MatiasMasso - The code is correct. You musn't have an instance of Application.Partake
Couldn't have called it Close, Dispose or Exit...Histogram
D
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!
}
Dinghy answered 21/7, 2011 at 14:18 Comment(8)
Absolute legand. App.Quit() was the key... Thank you!Savill
@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.Dinghy
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 betterSassanid
@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.Dinghy
GC.Collect() is like Betelgeuse: you have to call it three times for it to respond in any observable way.Suffice
Calling GC.Collect from application is not a good idea. Let dotnet framework decide when it is best to do a GC.Flee
There is no need for GC.Collect() nor Marshal.ReleaseComObject() if you close the document and quit the applicationHog
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.Corespondent
C
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();
}
Chimere answered 15/8, 2018 at 22:42 Comment(2)
There are still keepers of the knowledge.Toxicogenic
so what scenario would i make a new word.ApplicationConsumerism
B
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);
}
Bellman answered 25/8, 2015 at 6:20 Comment(0)
P
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();
}
Phlegm answered 12/12, 2013 at 9:35 Comment(2)
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.Breadfruit
There is no need for GC.Collect() nor Marshal.ReleaseComObject() if you close the document and quit the applicationHog
O
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;
Observe answered 7/11, 2019 at 18:48 Comment(0)
R
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();
Regazzi answered 21/7, 2011 at 14:24 Comment(1)
There is no need for GC.Collect() nor Marshal.ReleaseComObject() if you close the document and quit the applicationHog
E
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();
Eleaseeleatic answered 14/3, 2018 at 8:50 Comment(0)
Z
0

In my case it was because i called wordApp.Quit() instead of wordApp.Quit(false). Just added false to call of this method and now it works fine.

Zoila answered 24/6 at 17:31 Comment(1)
Multiple answers already suggest this.Columbus

© 2022 - 2024 — McMap. All rights reserved.