问题
I'm using PowersHell to automate iTunes but find the error handling / waiting for com objects handling to be less than optimal.
Example code
#Cause an RPC error
$iTunes = New-Object -ComObject iTunes.Application
$LibrarySource = $iTunes.LibrarySource
# Get "playlist" objects for main sections
foreach ($PList in $LibrarySource.Playlists)
{
if($Plist.name -eq "Library") {
$Library = $Plist
}
}
do {
write-host -ForegroundColor Green "Running a loop"
foreach ($Track in $Library.Tracks)
{
foreach ($FoundTrack in $Library.search("$Track.name", 5)) {
# do nothing... we don't care...
write-host "." -nonewline
}
}
} while(1)
#END
Go into itunes and do something that makes it pop up a message - in my case I go into the Party Shuffle and I get a banner "Party shuffle automatically blah blah...." with a "Do not display" message.
At this point if running the script will do this repeatedly:
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
Exception calling "Search" with "2" argument(s): "The message filter indicated
that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVER
CALL_RETRYLATER))"
At C:\Documents and Settings\Me\My Documents\example.ps1:17 char:45
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
Exception calling "Search" with "2" argument(s): "The message filter indicated
that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVER
CALL_RETRYLATER))"
At C:\Documents and Settings\Me\My Documents\example.ps1:17 char:45
If you waited until you you had a dialog box before running the example then instead you'll get this repeatedly:
Running a loop
You cannot call a method on a null-valued expression.
At C:\Documents and Settings\Me\example.ps1:17 char:45
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
That'll be because the $Library handle is invalid.
If my example was doing something important - like converting tracks and then deleting the old ones, not handling the error correctly could be fatal to tracks in itunes. I want to harden up the code so that it handles iTunes being busy and will silently retry until it has success. Any suggestions?
回答1:
Here's a function to retry operations, pausing in between failures:
function retry( [scriptblock]$action, [int]$wait=2, [int]$maxRetries=100 ) {
$results = $null
$currentRetry = 0
$success = $false
while( -not $success ) {
trap {
# Set status variables at function scope.
Set-Variable -scope 1 success $false
Set-Variable -scope 1 currentRetry ($currentRetry + 1)
if( $currentRetry -gt $maxRetries ) { break }
if( $wait ) { Start-Sleep $wait }
continue
}
$success = $true
$results = . $action
}
return $results
}
For the first error in your example, you could change the inner foreach
loop like this:
$FoundTracks = retry { $Library.search( "$Track.name", 5 ) }
foreach ($FoundTrack in $FoundTracks) { ... }
This uses the default values for $wait
and $maxRetries
, so it will attempt to call $Library.search
100 times, waiting 2 seconds between each try. If all retries fail, the last error will propagate to the outer scope. You can set $ErrorActionPreference
to Stop
to prevent the script from executing any further statements.
回答2:
COM support in PowerShell is not 100% reliable. But I think the real issue is iTunes itself. The application and COM model wasn't designed, IMO, for this type of management. That said, you could implement a Trap into your script. If an exception is raised, you could have the script sleep for a few seconds.
回答3:
Part of your problem might be in how $Track.name is being evaluated. You could try forcing it to fully evaluate the name by using $($Track.name).
One other thing you might try is using the -strict parameter with your new-object command/
来源:https://stackoverflow.com/questions/181585/rpc-e-servercall-retrylater-during-powershell-automation