PowerShell — работа с несколькими ComObjects нестабильна?

Я пишу скрипт, который открывает и копирует таблицу из 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()

Вот ошибка введите здесь описание изображения

Есть идеи?

* Обновление: странно, что скрипт работает без ошибок на моем компьютере в офисе, но не на моем ноутбуке =.=


person Bum    schedule 08.12.2015    source источник
comment
Видите ли вы другие ошибки? Было бы полезно, если бы вы включили полный текст ошибки. Как вы запускаете их несколько раз? Может петля? Если вы запускаете это несколько раз для группы файлов, вы можете оставить свои объекты открытыми до тех пор, пока не будет обработан последний файл.   -  person Matt    schedule 08.12.2015
comment
больше нет ошибок, кроме этих двух. я просто пытаюсь отладить, запустить это в тех же файлах excel и doc, а не в цикле. иногда он работает идеально, как я хочу, иногда нет.   -  person Bum    schedule 08.12.2015
comment
Я заметил, что вы выпускаете книгу и объекты Excel? Почему бы не использовать объекты doc и wd? У меня нет опыта работы с PowerShell, но много опыта работы с .NET и COM: отказ от выпуска COM-объектов и их постоянное повторное использование в конечном итоге блокирует их повторное использование и связывает функциональность Word Server...   -  person Cindy Meister    schedule 09.12.2015
comment
Я не знаю почему, но Excel.quit, похоже, не работает. Закройте словарь и выйдите из Excel r недостаточно, чтобы убить процесс Excel. Он по-прежнему появляется в диспетчере задач, и не один, а много. В приложении Word такого не бывает   -  person Bum    schedule 10.12.2015


Ответы (1)


Сообщение об ошибке «Вызов был отклонен вызываемым абонентом» соответствует коду ошибки RPC_E_SERVERCALL_RETRYLATER 0x8001010A. Название кода уже содержит подсказку к решению. COM-сервер (то есть приложение Office) слишком занят и не может ответить на вызов в данный момент, но вы можете повторить попытку позже.

Повторная попытка может быть обработана путем реализации IOleMessageFilter, как описано в Как исправить "Приложение занято" ' и "Вызов был отклонен вызывающим абонентом".

Однако реализовать этот интерфейс непросто, поскольку мы находимся внутри скрипта PowerShell. Тем не менее, это можно сделать (благодаря отличному ответу @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()
person Dirk Vollmar    schedule 08.12.2015