boB
boB

Reputation: 173

Word automation failing on server, but dev station works great

I am automating a oft-used paper form by querying the user on a web page, then modifying a base Word document and feeding that modified doc file to the user's browser for hand-off to Word.

The code is Visual Basic, and I am using the Microsoft.Office.Interop module to manipulate the document by manipulating Word. Works fine on the development system (Visual Studio 2015) but not on the production server (IIS 8.5).

Both the Documents.Open() call and the doc.SaveAs() call fail with Message="Command failed" Source="Microsoft Word" HResult=0x800A1066

Things I've tried:

Greatly condensed sample code does not include fallback logic. My Word document has a number of fields whose names match the XML tokens passed as parameters into this function. saveFields() is an array of those names.

        Dim oWord As Word.Application
        Dim oDoc As Word.Document
        oWord = CreateObject("Word.Application")
        oWord.Visible = True
        oDoc = oWord.Documents.Open(docName)

        Dim ev As String
        For i = 0 To saveFields.Length - 1
            Try
                ev = dataXD.Elements(saveFields(i))(0).Value
            Catch
                ev = Nothing
            End Try
            If ev IsNot Nothing Then
                    Try
                        Dim field = oDoc.FormFields(saveFields(i))
                        If field IsNot Nothing Then
                            If field.Type = Word.WdFieldType.wdFieldFormTextInput Then
                                field.Result = ev
                            End If
                        End If
                    Catch e As Exception
                        ErrorOut("Caught exception! " & e.Message)
                    End Try
            End If
        Next
...
        oDoc.SaveAs2(localDir & filename)
        oDoc.Close()
        oWord.Quit(0, 0, 0)

The code should generate a modified form (fields filled in with data from the parameters); instead it fails to open, and the fallback code fails to save the new document.

On my dev system the document gets modified as it should, and if I break at the right place and change the right variable, the fallback code runs and generates the alternate document successfully -- but on the production server both paths fail with the same error.

Barring any better answers here, my next steps are to examine and use OpenXML and/or DocX, but making a few changes to the existing code is far preferable to picking a new tool and starting over from scratch.

Upvotes: 1

Views: 257

Answers (1)

boB
boB

Reputation: 173

Unfortunately, Lex Li was absolutely correct, and of course, the link to the reason why is posted on a site my company considers off limits, thus never showed up in my google searches prior to coding this out.

None of the tools I tried were able to handle the form I was trying to automate either -- I needed to fill in named fields and check/uncheck checkboxes, abilities which seemed beyond (or terribly convoluted in) the tools I evaluated ...

Eventually I dug into the document.xml format myself; I developed a function to modify the XML to check a named checkbox, and manipulated the raw document.xml to replace text fields with *-delimited token names. This reduced all of the necessary changes to simple string manipulation -- the rest was trivial.

The tool is 100% home-grown, not dependent upon any non-System libraries and works 100% for this particular form. It is not a generic solution by any stretch, and I suspect the document.xml file will need manual changes if and when the document is ever revised.

But for this particular problem -- it is a solution.

This was the closest I got to a complicated part. This function will check (but not uncheck) a named checkbox from a document.xml if the given condition is true.

    Private Shared Function markCheckbox(xmlString As String, cbName As String, checkValue As Boolean) As String
        markCheckbox = xmlString
        If checkValue Then ' Checkbox needs to be checked, proceed
            Dim pos As Integer = markCheckbox.IndexOf("<w:ffData><w:name w:val=""" & cbName & """/>")
            If pos > -1 Then ' We have a checkbox
                Dim endPos As Integer = markCheckbox.IndexOf("</w:ffData>", pos+1)
                Dim cbEnd As Integer = markCheckbox.IndexOf("</w:checkBox>", pos+1)
                If endPos > cbEnd AndAlso cbEnd > -1 Then ' Have found the appropriate w:ffData element (pos -> endPos) and the included insert point (cbEnd)
                    markCheckbox = markCheckbox.Substring(0, cbEnd) & "<w:checked/>" & markCheckbox.Substring(cbEnd)
                End If
                ' Any other logic conditions, return the original XML string unmangled.
            End If
        End If
    End Function

Upvotes: 2

Related Questions