トップ回答者
タスクスケジューラから実行した際のコマンドのリターンコードについて

質問
-
いつもお世話になっております。
PowerShellにてバッチを作成しております。
その際に、コマンドのリターンコードに応じてログを出力しています。
‐‐該当の処理‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
#ゴミ箱の中身を削除
Clear-RecycleBin -Force;
if(!$?) {
$rtnCd=$False;
Write-Log $Error[0].exception;
}PowerShellを手動実行した場合、TRUEがリターンされるのですが、タスクスケジューラから実行した場合、
FALSEがリターンされ、以下のエラーメッセージが表示されます。(処理自体は正常に行われています。)
スケジューラは、最上に特権で実行するようにしております。
原因等わかる方いらっしゃいますでしょうか。
「System.ComponentModel.Win32Exception (0x80004005): 指定されたファイルが見つかりません。」
- 編集済み oga_rei 2019年10月19日 15:03
回答
-
不具合かどうかはさておき、現時点では「Clear-RecycleBin に対する成否判定は行わない」という選択肢しか無さそうな気がします。(-Force の代わりに -Confirm:$False を用いた場合も、タスクスケジューラーからの実行時には同様のエラーが発生しました)
どうしても成否判定が必要なら、「ごみ箱を空にする API を自力で呼びなおす」という手が使えるかと思ったのですが……これも厄介な問題を抱えていました。
ごみ箱を空にする際に呼び出されるのは SHEmptyRecycleBinW API です。
PowerShell のソースコード 226 行目 によれば、-force 指定時には、同 API の処理結果を Marshal.GetLastWin32Error メソッドで成否判定するようになっていたため、これが ERROR_FILE_NOT_FOUND を通知させる要因になっている可能性があります。手元で確認した限り、この時点での GetLastWin32Error は、2 (ERROR_FILE_NOT_FOUND)を返しますし、それを Win32Exception に渡すと、今回のエラーと同じメッセージが出力されたというのが、原因と推察した理由です。
# 『System.ComponentModel.Win32Exception (0x80004005): 指定されたファイルが見つかりません。』 $ERROR_FILE_NOT_FOUND = 2; (New-Object System.ComponentModel.Win32Exception $ERROR_FILE_NOT_FOUND).ToString();
とはいえ同ソースで API を宣言している 254 行目を見ると、 DllImportAttribute.SetLastError が指定されていないように見えます。その場合、ここの GetLastWin32Error が返す値は、直前の SHEmptyRecycleBinW に対する GetLastError API の結果ではなく、さらにその前に呼ばれた処理のエラーを指し示すはずなので、現状の Clear-RecycleBin の実装はこのソースと同一ではなく、実際には今回の事象とは無関係かもしれません。
いずれにせよ SDK の記述を見る限り、SHEmptyRecycleBinW の処理結果は HRESULT によって示されるものであるようで、GetLastError で判定できるという記載は見つけられませんでした。そのため、ここで GetLastWin32Error メソッドあるいは GetLastError を呼ぶことは undocumented な判定方法であるように思えます。かといって、ごみ箱が空の時に SHEmptyRecycleBinW を呼び出した場合、OS 側から COM 例外 E_UNEXPECTED “致命的なエラーです。” が返却されてしまう実装になっている以上、自力で API を呼び出す場合においても、戻り値の HRESULT をそのまま例外として扱うわけにもいかないので、成否判断を行うことができません。なかなか根の深そうな問題です。
以下、C# からの API 呼び出し実験。
using System; using System.Runtime.InteropServices; public class Program { static void Main() { string drivePath = null; int lastError1 = Marshal.GetLastWin32Error(); // 戻り値として、通常は 0x0 (=S_OK) が返されるのだが、 // ごみ箱が空の場合に呼び出した場合は 0x8000FFFF (=E_UNEXPECTED) が返される。 uint result = NativeMethod.SHEmptyRecycleBin(IntPtr.Zero, drivePath, NativeMethod.RecycleFlags.SHERB_NOCONFIRMATION | NativeMethod.RecycleFlags.SHERB_NOPROGRESSUI | NativeMethod.RecycleFlags.SHERB_NOSOUND); // DllImport 属性で SetLastError = true が指定されていた場合、 // 常に戻り値 2 (=ERROR_FILE_NOT_FOUND) が返された。 int lastError = Marshal.GetLastWin32Error(); } internal static class NativeMethod { internal enum RecycleFlags : uint { SHERB_NOCONFIRMATION = 0x00000001, SHERB_NOPROGRESSUI = 0x00000002, SHERB_NOSOUND = 0x00000004 } //[DllImport("Shell32.dll", CharSet = CharSet.Unicode)] [DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "SHEmptyRecycleBinW", ExactSpelling = true)] internal static extern uint SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlags dwFlags); } }
- 編集済み 魔界の仮面弁士MVP 2019年10月24日 1:43 -Confirm に関して追記
- 回答としてマーク oga_rei 2019年10月24日 12:52
すべての返信
-
お世話になっております。
ご教授頂きました通り、「ユーザがログオンしているときのみ実行する」にチェックを入れてみて実行しましたが、
結果は変わりませんでした。
以下、タスクスケジューラの設定(関係ありそうなところの抜粋しました。)です。
■全般
‐ユーザがログオンしているときのみ実行する。
‐最上位の特権で実行する
■操作
‐プログラムの開始
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command 起動するps1ファイルの絶対パス
OSのバグなのかなとも思っているのですが、本件のような事象をご存じの方いらっしゃいますでしょうか。
ちなみにOSは、Windows10 homeです。
-
Write-Log コマンドレットの仕様が分からなかったのですが、当方で試してみるると
Clear-Recyclebin コマンドレットが実行されて削除処理が行われた直後に、何故か追加のエラーが発生していました。Clear-RecycleBin : 指定されたファイルが見つかりません。 発生場所 C:\example\test.ps1:2 文字:1 + Clear-RecycleBin -Force; + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (RecycleBin:String) [Clear-RecycleBin]、Win32Exception + FullyQualifiedErrorId : FailedToClearRecycleBin,Microsoft.PowerShell.Commands.ClearRecycleBinCommand System.ComponentModel.Win32Exception (0x80004005): 指定されたファイルが見つかりません。
エラーメッセージで調べてみたらこんな話が見つかりました。(内容までは読み込んでいないので、今回の件に関連する内容かどうかまでは分かりません)
- PowerShell issues #6743 - "Misuse of `SHEmptyRecycleBin` in `Clear-RecycleBin` cmdlet"
-
不具合かどうかはさておき、現時点では「Clear-RecycleBin に対する成否判定は行わない」という選択肢しか無さそうな気がします。(-Force の代わりに -Confirm:$False を用いた場合も、タスクスケジューラーからの実行時には同様のエラーが発生しました)
どうしても成否判定が必要なら、「ごみ箱を空にする API を自力で呼びなおす」という手が使えるかと思ったのですが……これも厄介な問題を抱えていました。
ごみ箱を空にする際に呼び出されるのは SHEmptyRecycleBinW API です。
PowerShell のソースコード 226 行目 によれば、-force 指定時には、同 API の処理結果を Marshal.GetLastWin32Error メソッドで成否判定するようになっていたため、これが ERROR_FILE_NOT_FOUND を通知させる要因になっている可能性があります。手元で確認した限り、この時点での GetLastWin32Error は、2 (ERROR_FILE_NOT_FOUND)を返しますし、それを Win32Exception に渡すと、今回のエラーと同じメッセージが出力されたというのが、原因と推察した理由です。
# 『System.ComponentModel.Win32Exception (0x80004005): 指定されたファイルが見つかりません。』 $ERROR_FILE_NOT_FOUND = 2; (New-Object System.ComponentModel.Win32Exception $ERROR_FILE_NOT_FOUND).ToString();
とはいえ同ソースで API を宣言している 254 行目を見ると、 DllImportAttribute.SetLastError が指定されていないように見えます。その場合、ここの GetLastWin32Error が返す値は、直前の SHEmptyRecycleBinW に対する GetLastError API の結果ではなく、さらにその前に呼ばれた処理のエラーを指し示すはずなので、現状の Clear-RecycleBin の実装はこのソースと同一ではなく、実際には今回の事象とは無関係かもしれません。
いずれにせよ SDK の記述を見る限り、SHEmptyRecycleBinW の処理結果は HRESULT によって示されるものであるようで、GetLastError で判定できるという記載は見つけられませんでした。そのため、ここで GetLastWin32Error メソッドあるいは GetLastError を呼ぶことは undocumented な判定方法であるように思えます。かといって、ごみ箱が空の時に SHEmptyRecycleBinW を呼び出した場合、OS 側から COM 例外 E_UNEXPECTED “致命的なエラーです。” が返却されてしまう実装になっている以上、自力で API を呼び出す場合においても、戻り値の HRESULT をそのまま例外として扱うわけにもいかないので、成否判断を行うことができません。なかなか根の深そうな問題です。
以下、C# からの API 呼び出し実験。
using System; using System.Runtime.InteropServices; public class Program { static void Main() { string drivePath = null; int lastError1 = Marshal.GetLastWin32Error(); // 戻り値として、通常は 0x0 (=S_OK) が返されるのだが、 // ごみ箱が空の場合に呼び出した場合は 0x8000FFFF (=E_UNEXPECTED) が返される。 uint result = NativeMethod.SHEmptyRecycleBin(IntPtr.Zero, drivePath, NativeMethod.RecycleFlags.SHERB_NOCONFIRMATION | NativeMethod.RecycleFlags.SHERB_NOPROGRESSUI | NativeMethod.RecycleFlags.SHERB_NOSOUND); // DllImport 属性で SetLastError = true が指定されていた場合、 // 常に戻り値 2 (=ERROR_FILE_NOT_FOUND) が返された。 int lastError = Marshal.GetLastWin32Error(); } internal static class NativeMethod { internal enum RecycleFlags : uint { SHERB_NOCONFIRMATION = 0x00000001, SHERB_NOPROGRESSUI = 0x00000002, SHERB_NOSOUND = 0x00000004 } //[DllImport("Shell32.dll", CharSet = CharSet.Unicode)] [DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "SHEmptyRecycleBinW", ExactSpelling = true)] internal static extern uint SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlags dwFlags); } }
- 編集済み 魔界の仮面弁士MVP 2019年10月24日 1:43 -Confirm に関して追記
- 回答としてマーク oga_rei 2019年10月24日 12:52