PowerShell – 使用多个ComObjects不稳定?

我正在写一个脚本,打开并复制表格从Excel到Word,但每个运行的结果是不同的。 它首先做得不错,但经过​​几次运行后,烦人的红色文本开始出现(特别是“调用被拒绝被调用者”和select对象的方法,如$ wordObject.Selection.TypeParagraph不能运行,因为$ wordObject.Selection成为一个空值expression。

closures它们之后,我尝试了ReleaseComObject,但问题仍然存在

这是因为我在同一节中使用两个ComObject? 我的脚本结构是这样的:

$Excel = New-Object -ComObject excel.application $Excel.visible = $false $Workbook = $excel.Workbooks.open($pathEx) $range = $workbook.activesheet.usedrange $cop = $range.Copy() $wd = new-object -comObject Word.application $wd.visible = $true $doc = $wd.documents.open($pathWd) $wdSelection = $wd.Selection $a = $wdSelection.Endkey(6,0) $wdSelection.typeparagraph() $wd.Selection.paste() 

closures并退出:

 $workbook.close($false) $excel.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null Remove-Variable workbook, excel $doc.saveAs() $wd.Quit() 

这里是错误在这里input图像描述

任何想法?

*更新:奇怪的是,脚本运行没有任何错误在我的电脑在办公室,但不是我的笔记本=。=

错误消息“调用被调用者拒绝”对应于错误代码RPC_E_SERVERCALL_RETRYLATER 0x8001010A 。 代码的名称已经包含解决scheme的线索。 COM服务器(即Office应用程序)太忙,目前无法接听电话 – 但您可以稍后再试。

按照如何修复“应用程序忙”和“被调用者被拒绝”错误中的描述,可以通过实现IOleMessageFilter来处理重试。

但是,实现这个接口并不是直接的,因为我们在PowerShell脚本中。 尽pipe如此,这是可以做到的(感谢MarkRucker的出色答案 ):

 param([String]$pathEx, [String]$pathWd) $source = @" namespace EnvDteUtils { using System; using System.Runtime.InteropServices; public class MessageFilter : IOleMessageFilter { // Class containing the IOleMessageFilter // thread error-handling functions. // Start the filter. public static void Register() { IOleMessageFilter newFilter = new MessageFilter(); IOleMessageFilter oldFilter = null; CoRegisterMessageFilter(newFilter, out oldFilter); } // Done with the filter, close it. public static void Revoke() { IOleMessageFilter oldFilter = null; CoRegisterMessageFilter(null, out oldFilter); } // // IOleMessageFilter functions. // Handle incoming thread requests. int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo) { //Return the flag SERVERCALL_ISHANDLED. return 0; } // Thread call was rejected, so try again. int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType) { if (dwRejectType == 2) // flag = SERVERCALL_RETRYLATER. { // Retry the thread call immediately if return >=0 & // <100. return 99; } // Too busy; cancel call. return -1; } int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType) { //Return the flag PENDINGMSG_WAITDEFPROCESS. return 2; } // Implement the IOleMessageFilter interface. [DllImport("Ole32.dll")] private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); } [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] interface IOleMessageFilter { [PreserveSig] int HandleInComingCall( int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] int RetryRejectedCall( IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] int MessagePending( IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } } "@ Add-Type -TypeDefinition $source [EnvDTEUtils.MessageFilter]::Register() $Excel = New-Object -ComObject excel.application $Excel.visible = $false $Workbook = $excel.Workbooks.open($pathEx) $range = $workbook.activesheet.usedrange $cop = $range.Copy() $wd = new-object -comObject Word.application $wd.visible = $true $doc = $wd.documents.open($pathWd) $wdSelection = $wd.Selection $a = $wdSelection.Endkey(6,0) $wdSelection.typeparagraph() $wd.Selection.paste()