none
PowerShell上で常時Win32エラーコード203が発生している RRS feed

  • 質問

  • PowerShellコンソール上で

    [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
    

    または

    $cs=@'
      public class Test {
        public System.Int32 M() {
          return System.Runtime.InteropServices.Marshal.GetLastWin32Error();
        }
      }
    '@
    
    Add-Type -TypeDefinition $cs
    
    [Test]::new().M()
    
    


    を実行すると、いつも203と表示されます。

    Win32 APIをP/Invokeで使用するC#のコードをAdd-Typeで読み込んで使用したいのですが、最終エラーが常時発生している状態だとAPIのエラーに応じた処理に支障をきたすため非常に困っています。

    一応、.NET Framework 4.7.2のコンパイラーのcsc.exeで

    public class Test {
    	public static void Main() {
    		System.Console.WriteLine(
    			System.Runtime.InteropServices.Marshal.GetLastWin32Error()
    			
    		);
    		
    	}
    	
    }
    
    
    

    をコンパイルしてできたアプリを実行すると、ちゃんと0と表示されます。

    環境はWindows 10 Pro 1809 (x64)、Windows PowerShell 5.1 (x64)です。

    2020年1月11日 9:23

回答

  • この問題を解決する方法を見つけて自己解決しました。

    それは、エラーの起こりそうな処理の前に予めWin32 APIのSetLastError()を呼び出して最終エラーをリセットしておくことでした。

    C#のメソッドの中でSetLastError()で任意の最終エラーをセットすると、そのメソッドの中に限りMarshal.GetLastWin32Error()がセットした最終エラーの値を返してくれます。

    検証コードは以下の通りです。

    $cs = @'
    using System;
    using System.Runtime.InteropServices;
    
    public class Test {
      public readonly Int32 ErrorCode = 0;
      public readonly Int32 HResult = 0;
      public readonly Exception Error = null;
      
      [DllImport( @"kernel32.dll", EntryPoint = @"SetLastError", SetLastError = true )]
      private static extern void setLastError( UInt32 errorCode );
      
      public Test( UInt32 errorCode ) {
        setLastError( errorCode );
        this.ErrorCode = Marshal.GetLastWin32Error();
        this.HResult = Marshal.GetHRForLastWin32Error();
        this.Error = Marshal.GetExceptionForHR( this.HResult );
      }
    }
    '@
    
    Add-Type -TypeDefinition $cs
    
    $test = [Test]::new( [System.UInt32] 0 )
    $test.ErrorCode
    $test.HResult
    $test.Error
    
    $test = [Test]::new( [System.UInt32] 5 )
    $test.ErrorCode
    $test.HResult
    $test.Error
    

    これを実行した結果は

    0
    -2147024896
    この操作を正しく終了しました。 (HRESULT からの例外:0x80070000)
    
    5
    -2147024891
    アクセスが拒否されました。 (HRESULT からの例外:0x80070005 (E_ACCESSDENIED))
    
    


    と正しい値になっています。

    また、正しい値を返すのはメソッドの中だけであり、メソッドが終了してから

    [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
    

    とすると、相変わらず

    203
    

    と表示されます。

    • 回答としてマーク fzok4234 2020年1月15日 4:19
    2020年1月15日 4:05

すべての返信

  • こんにちは。フォーラムオペレーターのFanです。

    フォーラムにご投稿くださいましてありがとうございます。


    PowerShell上で常時Win32エラーコード203が発生していることについて

    ご存知の方おりましたら、ご意見を共有頂ければ本当に幸いです.

     

    どうぞよろしくお願いいたします。

    Fan


    Please remember to mark the replies as an answers if they help. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com

    2020年1月14日 2:25
    モデレータ
  • 回答が付かないようなので…。

    ざっと検索した感じ、本質的な解決案は見つからなかったので、コード側で回避策を講じるしかなさそうです。

    Add-Typeで読み込むというC#のコードがどんなのか知りませんが、C#内で失敗時にWin32Exceptionを投げるようにするなり、GetLastError(Marshal.GetLastWin32Error)の結果をrefで返すなり。

    2020年1月14日 8:30
  • この問題を解決する方法を見つけて自己解決しました。

    それは、エラーの起こりそうな処理の前に予めWin32 APIのSetLastError()を呼び出して最終エラーをリセットしておくことでした。

    C#のメソッドの中でSetLastError()で任意の最終エラーをセットすると、そのメソッドの中に限りMarshal.GetLastWin32Error()がセットした最終エラーの値を返してくれます。

    検証コードは以下の通りです。

    $cs = @'
    using System;
    using System.Runtime.InteropServices;
    
    public class Test {
      public readonly Int32 ErrorCode = 0;
      public readonly Int32 HResult = 0;
      public readonly Exception Error = null;
      
      [DllImport( @"kernel32.dll", EntryPoint = @"SetLastError", SetLastError = true )]
      private static extern void setLastError( UInt32 errorCode );
      
      public Test( UInt32 errorCode ) {
        setLastError( errorCode );
        this.ErrorCode = Marshal.GetLastWin32Error();
        this.HResult = Marshal.GetHRForLastWin32Error();
        this.Error = Marshal.GetExceptionForHR( this.HResult );
      }
    }
    '@
    
    Add-Type -TypeDefinition $cs
    
    $test = [Test]::new( [System.UInt32] 0 )
    $test.ErrorCode
    $test.HResult
    $test.Error
    
    $test = [Test]::new( [System.UInt32] 5 )
    $test.ErrorCode
    $test.HResult
    $test.Error
    

    これを実行した結果は

    0
    -2147024896
    この操作を正しく終了しました。 (HRESULT からの例外:0x80070000)
    
    5
    -2147024891
    アクセスが拒否されました。 (HRESULT からの例外:0x80070005 (E_ACCESSDENIED))
    
    


    と正しい値になっています。

    また、正しい値を返すのはメソッドの中だけであり、メソッドが終了してから

    [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
    

    とすると、相変わらず

    203
    

    と表示されます。

    • 回答としてマーク fzok4234 2020年1月15日 4:19
    2020年1月15日 4:05