Word com对象失败

时间:2018-12-19 01:03:30

标签: powershell

脚本目的

该脚本背后的想法是从大量文档中递归提取文本,并使用提取的文本更新Azure SQL数据库中的字段。基本上,我们正在从文档内容的Windows搜索转移到SQL全文搜索以提高速度。

问题

当脚本遇到打开文件的问题(例如受密码保护)时,此脚本对于随后的每个文档都会失败。这是脚本中处理文件的部分:

foreach ($list in (Get-ChildItem ( join-path $PSScriptRoot "\FileLists\*" ) -include *.txt )) {

    ## Word object
    $word = New-Object -ComObject word.application
    $word.Visible = $false
    $saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatText")
    $word.DisplayAlerts = 0


    Write-Output ""
    Write-Output "################# Parsing $list"
    Write-Output ""

    $query = "INSERT INTO tmp_CachedText (tCachedText, tOID)
              VALUES "

    foreach ($file in (Get-Content $list)) {
        if ($file -like "*-*" -and $file -notlike "*~*") {

            Write-Output "Processing: $($file)"
            Try {
                $doc = $word.Documents.OpenNoRepairDialog($file, $false, $false, $false, "ttt")

                if ($doc) {
                    $fileName = [io.path]::GetFileNameWithoutExtension($file)
                    $fileName = $filename + ".txt"
                    $doc.SaveAs("$env:TEMP\$fileName", [ref]$saveFormat)
                    $doc.Close()


                    $4ID = $fileName.split('-')[-1].replace(' ', '').replace(".txt", "")
                    $text = Get-Content -raw "$env:TEMP\$fileName"
                    $text = $text.replace("'", "''")

                    $query += "
                    ('$text', $4ID),"
                    Remove-Item -Force "$env:TEMP\$fileName"

                    <# Upload to azure #>
                    $query = $query.Substring(0,$query.Length-1)
                    $query += ";"

                    Invoke-Sqlcmd @params -Query $Query -ErrorAction "SilentlyContinue"

                    $query = "INSERT INTO tmp_CachedText (tCachedText, tOID)
                      VALUES "
                }
            }
            Catch {
                Write-Host "$($file) failed to process" -ForegroundColor RED;
                continue
            }
        }
    }
    Remove-Item -Force $list.FullName


    Write-Output ""
    Write-Output "Uploading to azure"
    Write-Output ""


    <# Upload to azure #>
    Invoke-Sqlcmd @params -Query $setQuery -ErrorAction "SilentlyContinue"


    $word.Quit()
    TASKKILL /f /PID WINWORD.EXE
}

基本上,它将分析包含x个文档路径的.txt文件文件夹,创建一个T-SQL更新语句,并在完全解析每个文件之后针对Azure SQL数据库运行。这些文件的生成如下:

if (!($continue)) {
    if ($pdf){
        $files = (Get-ChildItem -force -recurse $documentFolder -include *.pdf).fullname
    }
    else {
        $files = (Get-ChildItem -force -recurse $documentFolder -include *.doc, *.docx).fullname
    }


    $files | Out-File (Join-Path $PSScriptRoot "\documents.txt")
    $i=0; Get-Content $documentFile -ReadCount $interval | %{$i++; $_ | Out-File (Join-Path $PSScriptRoot "\FileLists\documents_$i.txt")}
}

$ interval 变量定义为给定的每次上传到Azure提取多少个文件。最初,我在循环之外创建了单词对象,直到结束都没有关闭过。不幸的是,这似乎不起作用,因为脚本每次击中一个无法打开的文件时,随后的每个文件都会失败,直到到达内部foreach循环foreach ($file in (Get-Content $list)) {的末尾。

这意味着要获得预期的结果,我必须以1的间隔运行它,这花费了太长时间。

2 个答案:

答案 0 :(得分:1)

这是在黑暗中的镜头

但是对我来说,这听起来像是失败的原因是因为Word Com对象现在由于无法打开文件而提示您执行某些操作,因此循环中的所有后续项也会失败。这可能可以解释为什么将$Interval设置为1的原因,因为当PowerShell设置为1时,它每次都会关闭并打开Com对象,并且这要花很多时间(我用excel做到了)。

您可以做的是在catch语句中,关闭并打开一个新的Word Com对象,该对象应该使您继续循环(但是如果需要大量打开Com对象,它会慢一些)。

如果您想进一步调试问题,请将Com对象设置为可见,然后在不与Word交互的情况下缓慢遍历程序。这将向您显示Word发生了什么以及是否有任何提示导致应用程序挂起。

当然,如果要全速运行它,则需要先检测无法打开的文档,或者可以通过打开多个Word Com对象来对其进行多线程处理,从而可以在以下位置加载多个文档时间。

答案 1 :(得分:0)

至于...

问题

当脚本遇到打开文件的问题(例如受密码保护)时,此脚本对于随后的每个文档都会失败。

...然后按照此处所述进行测试...

How to check if a word file has a password?

$filename = "C:\path\to\your.doc"

$wd = New-Object -COM "Word.Application"

try {
  $doc = $wd.Documents.Open($filename, $null, $null, $null, "")
} catch {
  Write-Host "$filename is password-protected!"
}

...并跳过文件以避免剩余文件失败。