远程过程调用失败,无法在空值表达式上调用方法-重新启动时有效

时间:2019-05-02 12:48:18

标签: vba powershell access-vba

我有一个PowerShell脚本,该脚本也调用Access数据库中的VBA脚本。当我尝试运行PowerShell脚本时,它不起作用,并引发以下错误:

The remote procedure call failed. (Exception from HRESULT: 0x800706BE)
At Z:\Report\Run\RUN.ps1:18 char:1
+ $wb_con1 = $excel.Workbooks.Open($FilePath_con1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException

You cannot call a method on a null-valued expression.
At Z:\Report\Run\RUN.ps1.ps1:19 char:1
+ $ws_con1 = $wb_con1.sheets.Item(1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

下面还会有更多的错误发生,You cannot call a method on a null-valued expression.

当我重新启动计算机时,脚本可以正常工作。该驱动器是网络共享驱动器。即使我确保Excel和Access都没有运行,似乎文件仍在某个地方打开。我不太确定。我的PowerShell脚本在下面,我也很高兴提供我的Access VBA脚本,但是我相信问题出在PowerShell脚本上,因为错误是在Excel的初始步骤中发生的。

我的代码如下。任何指导或协助将不胜感激。

# start Excel
$excel = New-Object -comobject Excel.Application
$ms_access = New-Object -comobject Access.Application

#set path files
$FilePath_con1 = 'Z:\Report\Data\StatusReport_C.xls'
$FilePath_eval1 = 'Z:\Report\Data\StatusReport_E.xls'

$FilePath_con2 = 'Z:\Report\Data\StatusReport_E.xlsx'

$FilePath_eval2 = 'Z:\Report\Data\StatusReport_E.xlsx'

$FilePath_date = 'Z:\Report\Data\ReportDate.xlsx'

$FilePath_access = "Z:\Report\Data\Access\Processing.accdb"

#Open workbook where the data will be copied from
$wb_con1 = $excel.Workbooks.Open($FilePath_con1)
$ws_con1 = $wb_con1.sheets.Item(1)

#Open workbook where the data will be copied to
$wb_con2 = $excel.Workbooks.Open($FilePath_con2)
$ws_con2 = $wb_con2.sheets.Item(1)

#Clear the workbook that the data will be copied to
$ws_con2.Cells.Clear()

#make Excel visible ($true) or invisible ($false)
$excel.Visible = $false

#Find the row count and column count of the data
$lrow1 = $ws_con1.usedRange.Rows.Count
$lcol1 = $ws_con1.usedRange.Columns.Count

#Copy from the header row which is located on Row 3
$range1=$ws_con1.Range("A3:V$lrow1")
$range1.copy()

#Copy the Report Date
$cpy_range_con = $ws_con2.Range("A1")
$ws_con2.Paste($cpy_range_con)

#Save the workbook where the data was copied to and close it
$wb_con2.Save()
$wb_con2.Close()
$wb_con1.Close()

#Open workbook where the data will be copied from 
$wb_eval1 = $excel.Workbooks.Open($FilePath_eval1)
$ws_eval1 = $wb_eval1.sheets.Item(1)

#Open workbook where the data will be copied to
$wb_eval2 = $excel.Workbooks.Open($FilePath_eval2)
$ws_eval2 = $wb_eval2.sheets.Item(1)

#Open workbook where the data will be copied from 
$wb_date = $excel.Workbooks.Open($FilePath_date)
$ws_date = $wb_date.sheets.Item(1)

#Clear the workbook where the data will be copied to
$ws_eval2.Cells.Clear()

#Count the rows where the data will be copied from in E
$lrow2 = $ws_eval1.usedRange.Rows.Count

#Define the range as the cell starting in A3 until the last row
$range2=$ws_eval1.Range("A3:V$lrow2")

#Copy the data range
$range2.copy()

#Define the cell where the data will start in the workbook where the data will be copied to
$cpy_range_eval = $ws_eval2.Range("A1")

#Paste the data 
$ws_eval2.Paste($cpy_range_eval)

#Define the cell for the ReportDate and copy it
$date_range = $ws_eval1.Range("B1")
$date_range.copy()

#Define the cell where the Report date will be copied to and paste it
$cpy_range_date = $ws_date.Range("A2")
$ws_date.Paste($cpy_range_date)

#Save and close the workbooks
$wb_date.Save()
$wb_date.Close()

$wb_eval2.Save()
$wb_eval2.Close()
$wb_eval1.Close()

#$excel.quit() | Out-Null

$ms_access.OpenCurrentDatabase($Filepath_access)
$ms_access.Run("ExportExcel")
#$ms_access.Quit() | Out-Null

$wshell = New-Object -ComObject Wscript.Shell
$output = $wshell.Popup("The task has finished")

Add-Type -AssemblyName System.Windows.Forms
$global:balloon = New-Object System.Windows.Forms.NotifyIcon
$path = (Get-Process -id $pid).Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Info 
$balloon.BalloonTipText = 'The reports are completed.'
$balloon.BalloonTipTitle = "Attention $Env:USERNAME" 
$balloon.Visible = $true 
$balloon.ShowBalloonTip(20000)

1 个答案:

答案 0 :(得分:2)

您已注释掉Excel和Access上的Quit()操作。 这样,您将使这些应用程序保持运行状态,并且每次运行脚本时,都会创建新的Com对象,并且永远不会从内存中清除它们。

您需要使用

退出并将其从内存中删除
$excel.Quit()
$ms_access.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb_con1)   | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb_con2)   | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb_eval1)  | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb_eval2)  | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb_date)   | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)     | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ms_access) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
$excel = $ms_access = $null

完成这些对象后。


我想您需要检查所有文件路径以查看文件是否全部存在:

if (!(Test-Path -Path $FilePath_con1 -PathType Leaf)) {
    Write-Error "The file $FilePath_con1 could not be found"
    exit
}

对您要打开的所有文件执行该操作。

但是。.检查文件是否确实存在是一回事,但这仍然并不意味着您可以使用$excel.Workbooks.Open($FilePath_con1)打开文件,因为也许另一个Excel进程已经打开了文件。另一个原因可能是您无权访问文件权限。

也许实用程序功能可以帮助您。它会测试是否可以找到文件,如果可以找到,是否已经锁定了文件。

function Test-IsFileAvailable {
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [string]$Path
    )

    if (!(Test-Path -Path $Path -PathType Leaf)) {
        # the file is not found
        Write-Verbose "File '$Path' not found."
        return $false
    }

    try {
        $file   = New-Object System.IO.FileInfo $Path
        $stream = $file.Open([System.IO.FileMode]::Open, 
                             [System.IO.FileAccess]::ReadWrite, 
                             [System.IO.FileShare]::None)

        if ($stream) { $stream.Close() }
        Write-Verbose "File '$Path' is not locked."
        return $true
    } 
    catch {
        # file is locked by a process.
        Write-Verbose "File '$Path' is locked by another process."
        return $false
    }
}

像这样使用它:

if (!(Test-IsFileAvailable $FilePath_con1 -Verbose)) { exit }

P.S。现在,我看到您还创建了一个Com对象来显示消息框。
也许更简单的方法是这样的:

Add-Type -AssemblyName Microsoft.VisualBasic

$Buttons = "OKOnly"    # other options are "OKCancel", "AbortRetryIgnore", "YesNoCancel", "YesNo", "RetryCancel"
$Icon = "Information"  # other options are "Critical", "Question", "Exclamation"
# show the messagebox
[Microsoft.VisualBasic.Interaction]::MsgBox("The task has finished", "$Buttons,SystemModal,$Icon", $Title)

希望有帮助