質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

2回答

3099閲覧

Comを利用したExcel操作時 リトライしたり スリープを挟まないと シートの削除 、ブックの保存などが多々失敗する のを回避したい 出来なければ VBAでいうところのResume が無いか知りたい

kamikazelight

総合スコア305

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

2クリップ

投稿2018/12/01 06:57

編集2018/12/04 01:06

前提・実現したいこと

最終的には
エラーが起きないように、もしくはVBAでいうところのresume のような
一括でのリトライ処理 をしたいです。


Excel操作時 リトライしたり スリープを挟まないと シートの削除 、ブックの保存などが多々失敗します。
Excel操作一つ一つに リトライ処理を施すのはつらいですし
スリープは処理時間が入れた分だけ伸びますし、実行環境によって必要な時間が変わってきてしまうと思うので避けたいです。

一つの処理の実行中に次の処理が実行されて失敗しているような感じがするのですが、
powershellでのExcel操作で あり得るのでしょうか?

思い当たる節としてはExcel.Applicationのインスタンスを別スレッドで作成、取得 していることくらいですが
影響はあるのでしょうか?
別スレッドは過去の質問にあるクラス _MultiThread を使った方法とほぼ同じです。

発生している問題・エラーメッセージ

Worksheet クラスの Delete プロパティを取得できません。 発生場所 ****************** + $BK.Sheets($($Page + $Index + 1)).Delete() + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (:) [], COMException + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException

該当のソースコードに近いコード(二台以上のPCで問題の再現が出来ました)

powershell

1<# 2 情報源 3 guitarrapc_tech 様 4 タイトル : PowerShell による同期処理、非同期処理、並列処理 を考えてみる 5 URL : http://tech.guitarrapc.com/entry/2013/10/29/100946 6 7 *** 様 8 タイトル : [properties] Powershellクラスはget setプロパティを実装します 9 URL : https://code-examples.net/ja/q/25e096e 10#> 11using namespace System.Management.Automation.Runspaces; 12using namespace System.Collections; 13using namespace System.Threading; 14using namespace System.Runtime.InteropServices 15 16# クラスを定義します 17class _MultiThread 18{ 19 20 # ノーマル プロパティ 21 [array] $RunspaceCollection 22 23 # 非表示 プロパティ 24 hidden [RunspacePool] $runspacePool 25 26 # 完了 プロパティ 27 hidden [bool]$_AllCompleted = $($this | Add-Member ScriptProperty -Name 'AllCompleted' -Value { 28 $($this.runspaceCollection.RunSpace | sort IsCompleted -Unique).IsCompleted -eq $true 29 }) 30 31 # コンストラクター 32 _MultiThread ($MyHost) 33 { 34 # create Runspace 35 [initialsessionstate] $sessionstate = [initialsessionstate]::CreateDefault() 36 [int] $minPoolSize = [int] $maxPoolSize = 5000 37 $this.runspacePool = [runspacefactory]::CreateRunspacePool($minPoolSize, $maxPoolSize, $sessionstate, $MyHost) # create Runspace Pool 38 $this.runspacePool.ApartmentState = "STA" 39 $this.runspacePool.Open() # open pool 40 } 41 42 # スレッドの追加 43 [void]AddThread([scriptblock]$ScriptBlock) 44 { 45 # Main Invokation 46 [powershell] $powershell = [PowerShell]::Create().AddScript($ScriptBlock) 47 $powershell.RunspacePool = $this.runspacePool 48 $this.RunspaceCollection += New-Object -TypeName PSObject -Property @{ 49 Runspace = $powershell.BeginInvoke(); 50 powershell = $powershell 51 } 52 # $powershell.Dispose() 53 } 54 55 # スレッドの追加 56 [void]AddThread([scriptblock]$ScriptBlock,[psobject]$Parameters) 57 { 58 # Main Invokation 59 [powershell] $powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddParameters($Parameters) 60 $powershell.RunspacePool = $this.runspacePool 61 $this.RunspaceCollection += New-Object -TypeName PSObject -Property $( 62 [ordered]@{ 63 Runspace = $powershell.BeginInvoke(); 64 powershell = $powershell 65 } 66 ) 67 } 68 69 70 # 結果の取得 71 [object]GetResult() 72 { 73 # get process result and end powershell session 74 return $(foreach ($runspace in $this.runspaceCollection) 75 { 76 # get reuslt 77 $runspace.powershell.EndInvoke($runspace.Runspace) 78 }) 79 } 80 81 # デストラクタの代わり手動で実行 82 [void]Dispose() 83 { 84 if (!($this.runspacePool.runspacePool.IsDisposed)) 85 { 86 foreach ($Runspace in $this.RunspaceCollection) 87 { 88 $Runspace.powershell.Dispose() 89 } 90 91 # Dispose Runspace 92 $this.runspacePool.Dispose() 93 } 94 } 95} 96 97function _New_MultiThread() 98{ 99[OutputType([_MultiThread])] 100 $Thread = New-Object _MultiThread($host) 101 return $Thread 102} 103 104$ExcelPath = "$PSScriptRoot\Tmp.xlsx" 105$OpenPath = "" 106if (Test-Path -Path $ExcelPath) 107{ 108 $OpenPath = $ExcelPath 109} 110$Thread = _New_MultiThread 111 112$NewBook = { 113 #Param($Path) 114 $App = New-Object -ComObject "Excel.Application" 115 116 #if($Path -eq "") 117 #{ 118 $null = $App.WorkBooks.Add() 119 Write-host "新規ブック" 120 #} 121 #else 122 #{ 123 # $null = $App.WorkBooks.Add("$Path") 124 # Write-host "既存ブックをテンプレートとして立ち上げ" 125 #} 126 127 $App 128} 129 130clear-host 131$Error.Clear() 132try 133{ 134 135 136 137 1..2 | foreach { $Thread.AddThread($NewBook<#,$Add_Args#>) } 138 139 while(!($Thread.AllCompleted)) {Start-Sleep -Milliseconds 5} 140 141 if ($OpenPath -ne "") 142 { 143 Remove-Item -Path $OpenPath 144 } 145 146 $Apps = $($Thread.GetResult()) 147 148 $Apps | foreach { $_.Caption = "Test_Apps" } 149 150 $App = $Apps[0] 151 152 $BK = $App.ActiveWorkbook 153 154 $BK2 = $Apps[1].ActiveWorkbook 155 156 $Sh = $App.ActiveSheet 157 158 $i = 0 159 160 $Tmp = "$PSScriptRoot\tmp2.xlsx" 161 162 if (Test-Path -Path $Tmp) 163 { 164 Remove-Item -Path $Tmp 165 } 166 167 $Bk2.saveAs("$Tmp") 168 169 $Bk2.close($false) 170 171 $Bk2 = $Apps[0].Workbooks.Open($Tmp) 172 173 1..10 | foreach { $Bk2.Sheets(1).Copy($Sh) } 174 175 176 $BK.Sheets(1).Activate() 177 178 $App.DisplayAlerts = $false 179 180 181 Write-host "残りシートが5になるまで削除" 182 while($BK.sheets.Count -gt 5){ 183 $Try = $true 184 while ($Try) 185 { 186 187 try 188 { 189 $BK.Sheets(6).Delete() 190 $Try = $false 191 } 192 catch 193 { 194 Write-host "Delete Retry" 195 } 196 197 } 198 } 199 200 Write-host "$ExcelPath`に保存" 201 $Try = $true 202 while ($Try) 203 { 204 205 try 206 { 207 $null = $BK.SaveAs("$ExcelPath") 208 $Try = $false 209 } 210 catch 211 { 212 Write-host "Save Retry" 213 } 214 215 } 216 217 $Apps | foreach { $_.WorkBooks | foreach {$_.close($false) } } 218 219} 220catch 221{ 222 Write-Host "$Error" 223} 224finally 225{ 226 Write-Host "クリーンアップしています..." 227 228 $null = [Marshal]::FinalReleaseComObject($Sh) 229 $null = [Marshal]::FinalReleaseComObject($Bk) 230 $null = [Marshal]::FinalReleaseComObject($Bk2) 231 $Apps | foreach { $_.quit() } 232 $null = $Apps | foreach { [Marshal]::FinalReleaseComObject($_) } 233 $Thread.Dispose() 234 Remove-Item -Path $ExcelPath 235 Remove-Item -Path $Tmp 236} 237

試したこと

・リトライ

powershell

1 $Try = $true 2 while ($Try) 3 { 4 5 try 6 { 7 $BK.Sheets($($Page + $Index + 1)).Delete() 8 $Try = $false 9 } 10 catch 11 { 12 Write-host "Delete Retry" 13 } 14 15 }

・Start-Sleep

・問題が起きているのと同じようなコードを書いてみたのですが
問題が再現できませんでした....

二台以上のPCで問題の再現が出来ました。

補足情報(FW/ツールのバージョンなど)

Win10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imihito

2018/12/01 07:21

「Excel.Applicationのインスタンスを別スレッドで作成、取得」はそれぞれ別にExcelを起動している、ということでよろしいですか?
kamikazelight

2018/12/01 16:21

過去の質問にある_MultiThreadに Excelを起動してcomを返す スクリプトブロック渡して 別スレッドで実行 あとで完了確認をしてから用意してあるメソッドGetResult()を使いcomを受け取っています。説明が下手ですみません。。
kamikazelight

2018/12/03 05:29

問題が再現出来なかったのであまり意味が無いかもしれませんが 同じようなコードを載せました。
kamikazelight

2018/12/04 01:20

問題の再現(シート削除でリトライが必要)が出来ましたので載せました。
guest

回答2

0

ベストアンサー

今更ですがWorksheet クラスの Delete プロパティを取得できません。で検索したところ以下のページが見つかりました。

Excel 2013 をオートメーションで実行し、異なるウィンドウのシートをコピー後にブックのシートを削除すると、エラーが発生する

質問に記載されたコードも同じ処理をしており、記事の回避策、

削除対象のシートのウィンドウをアクティブ ウィンドウにしてから、Sheets.Delete メソッドを実行します。

でエラーが出なくなることも確認出来ました(削除の前に$BK.Activate()を追加)。

まずはエラーメッセージで検索するのが一番という事ですかね。

投稿2018/12/04 11:09

imihito

総合スコア2166

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/12/05 02:10

Excel 側の問題だったのですね... ありがとうございました。
guest

0

まずはtry catchして握りつぶしているのが気になります。
下記のようにしてエラー内容を把握してみてはいかがでしょうか。

catch{ $error[0] | Format-List -force | Out-File [出力先のファイルパス] }

その上で対応を考えるのが近道だと思います。

また、while文をかけているようですが、確実にエラーが起こり続ける場合処理が終わらなく成る(無限ループ)が発生し危険ですので必ず抜けることができる様にする等の対応も必要かと思います。

投稿2018/12/01 07:16

編集2018/12/01 07:24
n_takapyon

総合スコア443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamikazelight

2018/12/01 16:14

エラー内容は確認してあります。 毎回同じエラーなので無視しているのですが それでもログをとったほうがいいのでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問