RPC_E_SERVERCALL_RETRYLATER во время автоматизации PowerShell - PullRequest
1 голос
/ 08 октября 2008

Я использую PowersHell для автоматизации iTunes, но обнаружил, что обработка ошибок / ожидание обработки com-объектов меньше оптимальной.

Пример кода

#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 

Зайдите в itunes и сделайте что-нибудь, из-за чего появится всплывающее сообщение - в моем случае я захожу в Party Shuffle и получаю баннер "Party shuffle автоматически бла-бла-бла ...." с сообщением "Не отображать" .

На этом этапе при запуске сценария это будет повторяться:

+      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

Если вы подождали, пока у вас не появилось диалоговое окно, прежде чем запускать пример, вместо этого вы получите это несколько раз:

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

Это будет потому, что дескриптор $ Library недействителен.

Если в моем примере выполнялось что-то важное - например, преобразование треков с последующим удалением старых, неправильная обработка ошибки может быть фатальной для треков в itunes. Я хочу укрепить код так, чтобы он обрабатывал iTunes, и он будет повторять попытки до тех пор, пока он не будет успешным. Есть предложения?

Ответы [ 3 ]

1 голос
/ 20 октября 2008

Вот функция для повторения операций с паузами между сбоями:

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
}

Для первой ошибки в вашем примере вы можете изменить внутренний цикл foreach следующим образом:

$FoundTracks = retry { $Library.search( "$Track.name", 5 ) }
foreach ($FoundTrack in $FoundTracks) { ... }

Используются значения по умолчанию для $wait и $maxRetries, поэтому он попытается вызвать $Library.search 100 раз, ожидая 2 секунды между каждой попыткой. Если все попытки повторятся, последняя ошибка будет распространена на внешнюю область. Вы можете установить $ErrorActionPreference в Stop, чтобы скрипт не выполнял дальнейшие операторы.

1 голос
/ 08 октября 2008

Поддержка COM в PowerShell не на 100% надежна. Но я думаю, что настоящая проблема заключается в самом iTunes. Приложение и модель COM не были разработаны, IMO, для этого типа управления. Тем не менее, вы можете внедрить Trap в ваш скрипт. Если возникает исключение, сценарий может находиться в спящем режиме несколько секунд.

0 голосов
/ 09 октября 2008

Часть вашей проблемы может заключаться в том, как оценивается $ Track.name. Вы можете попытаться заставить его полностью оценить имя, используя $ ($ Track.name).

Еще одна вещь, которую вы можете попробовать, это использовать параметр -strict с командой new-object /

...