1

I'm writing a script that open and copy a table from excel to word but the results of each run are different. it does good job at first but after a few runs, annoying red text start appearing (especially "call was rejected by callee" and method of selection object like $wordObject.Selection.TypeParagraph cannot run because $wordObject.Selection become a null-valued expression.

I tried to ReleaseComObject after close them but the problem still happen

Is this because I working with two ComObject in a same section? My script structure is like:

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

Close and quit:

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

Here is the error enter image description here

Any idea?

*Update: It's strange that the script run without any error on my pc at office but not my laptop =.=

Bum
  • 89
  • 1
  • 6
  • Are there other errors you see? Would be helpful if you included full error text. How are you running these multiple times? A loop perhaps? If you are running this multiple times for a group of files you can leave your objects open until the last file is processed. – Matt Dec 08 '15 at 03:49
  • no more error but those two. i just trying to debug, run this on the same excel and doc files, not a loop. sometimes it run perfectly as I want to, sometimes it doesn't. – Bum Dec 08 '15 at 04:37
  • I notice you release the workbook and excel objects? Why not the doc and wd objects, as well? I have no experience with PowerShell, but plenty with .NET and COM: not releasing COM objects and continually re-using them eventually blocks them from re-use and ties up Word's "Server" functionality... – Cindy Meister Dec 09 '15 at 15:04
  • I dont know why but Excel.quit doesn't seem to work probably. Close wordbook and quit excel r not enough to kill excel process. It still appear in task manager, and not only one but many. Thats not happen in word app – Bum Dec 09 '15 at 22:26

1 Answers1

0

The error message "Call was rejected by callee" corresponds to error code RPC_E_SERVERCALL_RETRYLATER 0x8001010A. The name of the code already contains a clue to the solution. The COM server (i.e. the Office application) is too busy and can't answer the call at the moment - but you can retry later.

Retrying can be handled by implementing IOleMessageFilter as described in How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors.

However, implementing this interface is not straight-forward, because we are inside a PowerShell script. Nonetheless, it can be done (thanks to @MarkRucker's 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()
Community
  • 1
  • 1
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316