服务器上的 Word 自动化失败,但开发站运行良好
Word automation failing on server, but dev station works great
我通过在网页上查询用户,然后修改基本 Word 文档并将修改后的 doc 文件提供给用户的浏览器,以便传递给 Word 来自动化经常使用的纸质表单。
代码是Visual Basic,我用的是Microsoft.Office.Interop模块,通过操作Word来操作文档。在开发系统 (Visual Studio 2015) 上运行良好,但在生产服务器 (IIS 8.5) 上运行不佳。
Documents.Open() 调用和 doc.SaveAs() 调用均失败,消息 ="Command failed" Source="Microsoft Word" HResult=0x800A1066
我尝试过的事情:
添加了 whazoo 调试:单步执行在生产机器上不是一个选项,所以我用调试输出查明了问题行。
Google了一下,发现这个问题早在2007年就有人报道过,但一直没有可行的解决方案。
- 几个网站提到了计时问题,因此我添加了几次暂停和重试 -- none 有所帮助。
有人提到了特权,所以我尝试更改文件权限和应用程序池用户 - 都没有帮助。
增强了我的异常处理报告以显示更多详细信息并包括所有内部异常。这产生了神奇的数字 800A1066,它导致了更多 google 的点击,但没有答案。
添加了后备代码:如果您无法打开主文档,请创建一个简单的。那时我发现 SaveAs() 调用也失败了。
多次返回开发系统以确认是的,代码在正确的环境中仍然可以正常工作。
高度精简的示例代码不包含回退逻辑。我的 Word 文档有许多字段,其名称与作为参数传递给此函数的 XML 标记相匹配。 saveFields() 是这些名称的数组。
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)
代码应该生成一个修改过的表单(用参数中的数据填充的字段);相反,它无法打开,并且后备代码无法保存新文档。
在我的开发系统上,文档得到了应有的修改,如果我在正确的位置中断并更改了正确的变量,回退代码会运行并成功生成备用文档——但在生产服务器上,这两个路径失败并出现同样的错误。
除非这里有更好的答案,否则我的下一步是检查和使用 OpenXML and/or DocX,但对现有代码进行一些更改比选择新工具更可取,并且从头开始。
不幸的是,Lex Li 完全正确,当然,link 原因发布在我公司认为禁止访问的网站上,因此从未出现在我的 google 搜索中在编码之前。
None 我尝试过的工具能够处理我试图自动化的表单——我需要填写命名字段和 check/uncheck 复选框,这些功能似乎超出了(或非常复杂)我评估的工具...
最终我自己研究了 document.xml 格式;我开发了一个函数来修改 XML 以检查命名的复选框,并操纵原始 document.xml 以用 * 分隔的标记名称替换文本字段。这减少了对简单字符串操作的所有必要更改——其余的都是微不足道的。
该工具是 100% 自主开发的,不依赖于任何非系统库,并且 100% 适合这种特定形式。它无论如何都不是通用解决方案,而且我怀疑 document.xml 文件在修订时需要手动更改。
但对于这个特定问题 -- 这是一个解决方案。
这是我最接近复杂的部分。如果给定条件为真,此函数将选中(但不会取消选中)document.xml 中的命名复选框。
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
我通过在网页上查询用户,然后修改基本 Word 文档并将修改后的 doc 文件提供给用户的浏览器,以便传递给 Word 来自动化经常使用的纸质表单。
代码是Visual Basic,我用的是Microsoft.Office.Interop模块,通过操作Word来操作文档。在开发系统 (Visual Studio 2015) 上运行良好,但在生产服务器 (IIS 8.5) 上运行不佳。
Documents.Open() 调用和 doc.SaveAs() 调用均失败,消息 ="Command failed" Source="Microsoft Word" HResult=0x800A1066
我尝试过的事情:
添加了 whazoo 调试:单步执行在生产机器上不是一个选项,所以我用调试输出查明了问题行。
Google了一下,发现这个问题早在2007年就有人报道过,但一直没有可行的解决方案。
- 几个网站提到了计时问题,因此我添加了几次暂停和重试 -- none 有所帮助。
有人提到了特权,所以我尝试更改文件权限和应用程序池用户 - 都没有帮助。
增强了我的异常处理报告以显示更多详细信息并包括所有内部异常。这产生了神奇的数字 800A1066,它导致了更多 google 的点击,但没有答案。
添加了后备代码:如果您无法打开主文档,请创建一个简单的。那时我发现 SaveAs() 调用也失败了。
多次返回开发系统以确认是的,代码在正确的环境中仍然可以正常工作。
高度精简的示例代码不包含回退逻辑。我的 Word 文档有许多字段,其名称与作为参数传递给此函数的 XML 标记相匹配。 saveFields() 是这些名称的数组。
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)
代码应该生成一个修改过的表单(用参数中的数据填充的字段);相反,它无法打开,并且后备代码无法保存新文档。
在我的开发系统上,文档得到了应有的修改,如果我在正确的位置中断并更改了正确的变量,回退代码会运行并成功生成备用文档——但在生产服务器上,这两个路径失败并出现同样的错误。
除非这里有更好的答案,否则我的下一步是检查和使用 OpenXML and/or DocX,但对现有代码进行一些更改比选择新工具更可取,并且从头开始。
不幸的是,Lex Li 完全正确,当然,link 原因发布在我公司认为禁止访问的网站上,因此从未出现在我的 google 搜索中在编码之前。
None 我尝试过的工具能够处理我试图自动化的表单——我需要填写命名字段和 check/uncheck 复选框,这些功能似乎超出了(或非常复杂)我评估的工具...
最终我自己研究了 document.xml 格式;我开发了一个函数来修改 XML 以检查命名的复选框,并操纵原始 document.xml 以用 * 分隔的标记名称替换文本字段。这减少了对简单字符串操作的所有必要更改——其余的都是微不足道的。
该工具是 100% 自主开发的,不依赖于任何非系统库,并且 100% 适合这种特定形式。它无论如何都不是通用解决方案,而且我怀疑 document.xml 文件在修订时需要手动更改。
但对于这个特定问题 -- 这是一个解决方案。
这是我最接近复杂的部分。如果给定条件为真,此函数将选中(但不会取消选中)document.xml 中的命名复选框。
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