chobowski
chobowski

Reputation: 109

How to properly close/terminate WINWORD.EXE/Word interop?

I have an application which generates a report using a Word file template. The application will fill up the template with the details supplied by the user's database records. I'm running my application to another server using Remote Desktop.

My problem is that the WINWORD.EXE process fails to terminate and remains in the Task Manager after the report generation is completed (saved the report to database and delete the generated file).

When the Service Account is signed-in, the WINWORD.EXE process is terminating properly and is being removed in the Task Manager’s list. When signing off, the WINWORD.EXE remains in the list. This may lead to an Out-of-Memory-Exception when several Word processes are not terminated.

Word reports are generated using Microsoft.Office.Interop.Word, Version=12.0.0.0.

Here's the code in saving the new file, closing the Word application, saving the report, and deleting the generated file:

Marshal.releaseComObject(rng)

Dim strFileName As String = comm.GenerateFileName(strUser_id, strVPN) & ".docx"

' Save the document.
Dim filename As Object = Path.GetFullPath(strNewFilePath & strFileName)
newDoc.SaveAs(FileName:=filename)

' Close.
Dim save_changes As Object = False
newDoc.Close(save_changes)
WordApp.Quit(save_changes)

Marshal.ReleaseComObject(newDoc)
Marshal.ReleaseComObject(WordApp)

rng= Nothing
newDoc = Nothing
WordApp = Nothing

' Let GC know about it
GC.Collect()

' Save the file to database
SaveFormat(Filename, strUser_Id, strFamilyId, strVPN, DateTime.Now.ToString(), "application/vnd.ms-word", br_id, "doc")

If File.Exists(filename) then
      File.Delete(filename)
End If

I'm still researching on different work around here.

In Excel we get the solution by getting the process ID of the opened Excel instance using the HWND property and GetProcessById class.

I read somewhere that Word 2013 and later has an Application.HWND property. Is it true? Does Word have a HWND property?

Upvotes: 1

Views: 3604

Answers (4)

Svetlin Nakov
Svetlin Nakov

Reputation: 1669

I found very easy solution. The Application object has Quit() method, which correctly closes the MS Word app and releases all associated resources (and exits the WINWORD.EXE process).

Application wordApp = new Application();
wordApp.Visible = false; // Show / hide MS Word app window
wordApp.ScreenUpdating = false; // Enable / disable screen updates after each change

try
{
    Document document = wordApp.Documents.Open("example.docx");

    // TODO: process the document here ...

}
finally
{
    wordApp.Quit(false);
}

Upvotes: 0

Otxoto
Otxoto

Reputation: 41

When I have to write Word documents I always open the document, work with it inside a Try[...]Catch and close it afterwards:

Private Sub doSomethingWithWord()
    Dim oWord As Word.Application

    'Create document
    Dim oDoc As Word.Document
    oWord = CreateObject("word.Application")
    oDoc = oWord.Documents.Add

    Try
        'Do something
        Dim oPara1 As Word.Paragraph
        oPara1 = oDoc.Content.Paragraphs.Add
        oPara1.Range.Text = "Hello world"
        oPara1.Range.Font.Bold = True
        oPara1.Format.SpaceAfter = 24
        oPara1.Range.InsertParagraphAfter()

        'Everything is OK, then save!
        oDoc.Save()
    Catch ex As Exception
        MsgBox("Error")
    End Try

    'Whatever happens always finishes closing word
    oDoc.Close()
    oWord.Quit()

End Sub

Upvotes: 0

Bugs
Bugs

Reputation: 4489

This piece of code is what I've always used based on Siddharth Rout's bit of code and should give you what you are after:

Private Sub ReleaseObject(ByVal obj As Object)
    Try
        Dim intRel As Integer = 0
        Do
            intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
        Loop While intRel > 0
        obj = Nothing
    Catch ex As Exception
        obj = Nothing
    Finally
        GC.Collect()
    End Try
End Sub

Add the above method to your application. You can then call upon this as and when you want to close Word.

It's also worth noting that it's important to get the ordering right. This always seems to be a problem with Interop.

What I would do is, replace this bit of code:

newDoc.Close(save_changes)
WordApp.Quit(save_changes)

Marshal.ReleaseComObject(newDoc)
Marshal.ReleaseComObject(WordApp)

rng= Nothing
newDoc = Nothing
WordApp = Nothing

With:

newDoc.Close(False)
ReleaseObject(newDoc)
WordApp.Quit(False)
ReleaseObject(WordApp)

I can also see you are attempting to dispose of your rng variable. I would change: Marshal.releaseComObject(rng) and use the new method ReleaseObject(rng).

Overall your code would look something like:

Dim strFileName As String = comm.GenerateFileName(strUser_id, strVPN) & ".docx"

' Save the document.
Dim filename As Object = Path.GetFullPath(strNewFilePath & strFileName)
newDoc.SaveAs(FileName:=filename)

ReleaseObject(rng)
newDoc.Close(False)
ReleaseObject(newDoc)
WordApp.Quit(False)
ReleaseObject(WordApp)

' Save the file to database
SaveFormat(Filename, strUser_Id, strFamilyId, strVPN, DateTime.Now.ToString(), "application/vnd.ms-word", br_id, "doc")

If File.Exists(filename) then
      File.Delete(filename)
End If

You mention that you use the GetProcessById method to then kill your Excel process. This really isn't needed and you should be fine using the above method ReleaseObject. As I said, quite often it's the ordering that causes a problem.

Have a look at my answer in particular:

Dim app As New Excel.Application()
app.Visible = False
app.DisplayAlerts = False

Dim wbs As Excel.Workbooks = app.Workbooks
Dim wb As Excel.Workbook = wbs.Add()
Dim ws As Excel.Worksheet = CType(wb.Sheets(1), Excel.Worksheet)

ReleaseObject(ws)
wb.Close()
ReleaseObject(wb)
ReleaseObject(wbs)
app.Quit()
ReleaseObject(app)

Upvotes: 1

Maarten van Stam
Maarten van Stam

Reputation: 1899

Cleanup code using the Garbage Collector (GC) would look like this:

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

Usually Word would clean up (with some delay) when all references are properly cleared, but if you want to force the cleanup (still after making sure all references are cleared) would be with the code above.

It looks kinda strange but if you want to know the ins-outs go find the book by Andrew Whitechapel (former Office PM) https://www.amazon.com/Microsoft%C2%AE-Development-Microsoft-Developer-Reference/dp/0735621322 in Chapter two, from the top of my head.

Upvotes: 1

Related Questions