【问题标题】:PowerShell - Working with multiple ComObjects is unstable?PowerShell - 使用多个 ComObjects 不稳定?
【发布时间】:2016-03-12 20:39:54
【问题描述】:

我正在编写一个脚本,可以打开一个表格并将其从 excel 复制到 word,但每次运行的结果都不同。一开始它做得很好,但经过几次运行后,开始出现烦人的红色文本(尤其是“调用被调用者拒绝”和选择对象的方法,如 $wordObject.Selection.TypeParagraph 无法运行,因为 $wordObject.Selection 变为空值表达。

我在关闭它们后尝试 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()

关闭并退出:

$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()

这是错误 enter image description here

有什么想法吗?

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

【问题讨论】:

  • 您还看到其他错误吗?如果您包含完整的错误文本,将会很有帮助。您如何多次运行这些?也许是一个循环?如果您对一组文件多次运行此操作,则可以让对象保持打开状态,直到处理完最后一个文件。
  • 没有更多的错误,但那两个。我只是想调试,在相同的 excel 和 doc 文件上运行它,而不是循环。有时它可以按我的意愿完美运行,有时却不行。
  • 我注意到您发布了工作簿和 excel 对象?为什么不使用 doc 和 wd 对象呢?我没有使用 PowerShell 的经验,但使用 .NET 和 COM 的经验很多:不释放 COM 对象并不断重复使用它们最终会阻止它们重复使用并绑定 Word 的“服务器”功能......
  • 我不知道为什么,但 Excel.quit 似乎不起作用。关闭 wordbook 并退出 excel r 不足以杀死 excel 进程。它仍然出现在任务管理器中,而且不止一个,而是很多。 Word 应用程序中不会发生这种情况

标签: excel powershell ms-word comobject


【解决方案1】:

错误消息“Call was denied by callee”对应于错误代码RPC_E_SERVERCALL_RETRYLATER 0x8001010A。代码的名称已经包含了解决方案的线索。 COM 服务器(即 Office 应用程序)太忙,目前无法接听电话 - 但您可以稍后重试。

可以通过实现IOleMessageFilter 来处理重试,如How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors 中所述。

但是,实现这个接口并不简单,因为我们在一个 PowerShell 脚本中。尽管如此,还是可以做到的(感谢@MarkRucker 的great answer):

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()

【讨论】:

    猜你喜欢
    • 2015-12-29
    • 1970-01-01
    • 2011-01-20
    • 2012-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    • 2013-05-05
    相关资源
    最近更新 更多