COM-объекты при работе в фоновом режиме

Пытаясь создать таблицу в слове и хотел бы немного ускорить процесс, без заданий это работает нормально, но это просто медленно. Когда я запускаю этот скрипт, все работает нормально, кроме случаев, когда скрипт

$Table.Cell($x,<column>).Range.Text = <string>    

Выдает ошибку

You cannot call a method on a null-valued expression.
    + CategoryInfo          : InvalidOperation: (Cell:String) [], 
    RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

и когда дело доходит до

$table = $doc.tables.item(1)

Выдает ошибку

You cannot call a method on a null-valued expression.
    + CategoryInfo          : InvalidOperation: (item:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Я предполагаю, что это как-то связано с

[System.Runtime.InteropServices.Marshal]::GetActiveObject('Word.Application')

не принимая всю информацию документа...

Вот мой код

#More code above
$owners = Import-CSV $ownerspath -header @("Owners")
foreach($name in $owners) {  
    #Document creation
    [ref]$SaveFormat = "microsoft.office.interop.word.WdSaveFormat" -as [type] 

    $Word = New-Object -comobject word.application
    $Word.Visible = $false
    $Doc = $Word.Documents.Add()
    $Range = $Doc.Range()

    $owner = $name.Owners
    $g = import-csv $exportpath$owner.csv -header @("Server name", "Description", "OS", "OS EOL", "SQL", "SQL EOL")
    $path = "$exportpath$owner.docx"

    if(($g.count + 1) -eq 1) {
        $rows = 2
    }
    else {
        $rows = $g.count + 1
    }

    #Table creation
    $Selection = $word.selection

    $Doc.Tables.Add($Range,$Rows,6) | Out-Null

    $Table = $Doc.Tables.item(1)

    $Table.Style = "Medium Shading 1 - Accent 1"    
    $Table.Cell(1,1).Range.Text = "Server name"
    $Table.Cell(1,2).Range.Text = "Server description"
    $Table.Cell(1,3).Range.Text = "OS Version"
    $Table.Cell(1,4).Range.Text = "OS EOL Date"
    $Table.Cell(1,5).Range.Text = "SQL Version"
    $Table.Cell(1,6).Range.Text = "SQL EOL Date"

    $x = 2
    foreach($l in $g) {
        $sn = $l.'Server name'
        $d = $l.Description
        $oper = $l.OS
        $opereol = $l.'OS EOL'
        $sequel = $l.SQL
        $sequeleol = $l.'SQL EOL'
        $scriptblock = {
            param($sn, $d, $oper, $opereol, $sequel, $sequeleol, $x)
            $word = [System.Runtime.InteropServices.Marshal]::GetActiveObject('Word.Application')
            $doc = $word.documents
            $table = $doc.tables.item(1)
            #Server names, OS, OS EOL, SQL, SQL EOL, and coloring for the EOL dates if the need them are added in this foreach loop.
            $Table.Cell($x,1).Range.Text = $sn
            $Table.Cell($x,2).Range.Text = $d
            $Table.Cell($x,3).Range.Text = $oper
            if($l.'OS EOL' -eq 'Out of date') {
                $Table.Cell($x,4).Range.shading.BackgroundPatternColor = 255
                $Table.Cell($x,4).Range.Text = $opereol
                $OoDTotal += 1
            }
            elseif($l.'OS EOL' -like "*!*") {
                $Table.Cell($x,4).Range.shading.BackgroundPatternColor = 65535
                $Table.Cell($x,4).Range.Text = $opereol
                $CloseTotal += 1
            }
            else {
                $Table.Cell($x,4).Range.Text = $opereol
                $UtDTotal += 1
            }
            $Table.Cell($x,5).Range.Text = $sequel
            if($l.'SQL EOL' -eq 'Out of date') {
                $Table.Cell($x,6).Range.shading.BackgroundPatternColor = 255
                $Table.Cell($x,6).Range.Text = $sequeleol
                $OoDTotal += 1
            }
            elseif($l.'SQL EOL' -like "*!*") {
                $Table.Cell($x,6).Range.shading.BackgroundPatternColor = 65535
                $Table.Cell($x,6).Range.Text = $sequeleol
                $CloseTotal += 1
            }
            else {
                $Table.Cell($x,6).Range.Text = $sequeleol
                $UtDTotal += 1
            }
        }
        Start-Job $scriptblock -ArgumentList $sn, $d, $oper, $opereol, $sequel, $sequeleol, $x
        $x++
    }
    while(get-job -state "Running") {
        start-sleep -seconds 1
    }
    Get-Job | Receive-Job
    Get-Job | Remove-Job

#More code below

РЕДАКТИРОВАТЬ: я запускаю этот скрипт на Posh V2 с Office Word 2007

1 ответ

Это ограниченная проблема. Вы не передаете все необходимые объекты (например, по крайней мере, и наиболее конкретно, объект Word COM) в свой блок скриптов. Задания PowerShell фактически создают совершенно отдельные экземпляры процесса powershell. Идите и проверьте:

Start-Job -ScriptBlock { start-sleep -Seconds 20 }
Start-Job -ScriptBlock { start-sleep -Seconds 20 }

PS C:\Users\hiyo!> Get-Process powershell

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName                                                                                                
-------  ------    -----      ----- -----   ------     --  -- -----------                                                                                                
    388      22    40336      48820 ...54     0.45   2720   1 powershell                                                                                                 
    443      25    54360      66860 ...75     0.58   7240   1 powershell 

И если вы на самом деле попытаетесь выполнить это так, как вам нужно, и передать все нужные параметры, вы можете столкнуться с проблемами блокировки записи, пытаясь открыть несколько экземпляров файла для R/W...

Другие вопросы по тегам