vb.net - 服务器上的 Word 自动化失败,但开发站运行良好
问题描述
我通过在网页上查询用户,然后修改基本 Word 文档并将修改后的 doc 文件提供给用户的浏览器以移交给 Word,从而使经常使用的纸质表单自动化。
代码是 Visual Basic,我使用 Microsoft.Office.Interop 模块通过操纵 Word 来操纵文档。在开发系统 (Visual Studio 2015) 上运行良好,但在生产服务器 (IIS 8.5) 上运行良好。
Documents.Open() 调用和 doc.SaveAs() 调用都失败了 Message="Command failed" Source="Microsoft Word" HResult=0x800A1066
我尝试过的事情:
添加了对 whazoo 的调试:单步在生产机器上不是一个选项,所以我用调试输出查明了问题所在。
google了一下,发现这个问题早在2007年就有报道了,但是没有报道可行的解决方案。
- 几个网站提到了时间问题,所以我添加了几次暂停和重试——没有任何帮助。
有些人提到了特权,所以我尝试更改文件权限和应用程序池用户 - 都没有帮助。
增强了我的异常处理报告以显示更多详细信息并包括所有内部异常。这产生了神奇的数字 800A1066,它导致了更多的谷歌点击,但没有答案。
添加后备代码:如果您无法打开主文档,请创建一个简单的。那时我发现 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 和/或 DocX,但是对现有代码进行一些更改比选择一个新工具并从头开始要好得多。
解决方案
不幸的是,Lex Li 是绝对正确的,当然,指向原因的链接发布在我公司认为禁止访问的网站上,因此在编码之前从未出现在我的谷歌搜索中。
我尝试过的任何工具都无法处理我试图自动化的表单——我需要填写命名字段并选中/取消选中复选框,这些能力似乎超出了我评估的工具(或非常复杂)......
最终我自己研究了 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
推荐阅读
- https - 如何找到我的服务器的公钥信息以在 OkHttp 中进行证书固定?
- python - 启动 spyder 时出现此错误
- python - AnalysisException:在根据前几行计算列值时,窗口函数中不支持表达式
- php - 从 php codeigniter 发送的电子邮件无法在特定域上接收
- android - 如何在没有网站的情况下设置包名称?
- android - 以编程方式将 Fragment 添加到 Kotlin 中的 Activity
- java - 无法在全新的 Manjaro 安装上启动 Eclipse (2019-12)
- hyperledger-fabric - Hyperledger 结构对等更新失败:签名集不满足策略
- javascript - 我的代码如何理解该函数和超时正在运行?
- string - Dart 中等效的 strstr 函数