1

我正在使用 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 automatic blah blah ....”和“不要显示”消息。

此时如果运行脚本会重复执行此操作:

+      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 繁忙,并且会静默重试,直到成功。有什么建议么?

4

3 回答 3

1

PowerShell 中的 COM 支持并非 100% 可靠。但我认为真正的问题是 iTunes 本身。应用程序和 COM 模型不是为这种类型的管理设计的,IMO。也就是说,您可以在脚本中实现陷阱。如果引发异常,您可以让脚本休眠几秒钟。

于 2008-10-08T11:09:26.697 回答
1

这是一个重试操作的函数,在失败之间暂停:

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这使用and的默认值$maxRetries,因此它将尝试调用$Library.search100 次,每次尝试之间等待 2 秒。如果所有重试都失败,最后一个错误将传播到外部范围。您可以设置$ErrorActionPreferenceStop阻止脚本执行任何进一步的语句。

于 2008-10-19T22:39:51.323 回答
0

您的部分问题可能在于如何评估 $Track.name。您可以尝试使用 $($Track.name) 强制它完全评估名称。

您可能会尝试的另一件事是将 -strict 参数与 new-object 命令/

于 2008-10-09T17:53:12.167 回答