CA1404: P/Invoke の直後に GetLastError を呼び出します

TypeName

CallGetLastErrorImmediatelyAfterPInvoke

CheckId

CA1404

分類

Microsoft.Interoperability

互換性に影響する変更点

なし

原因

Marshal.GetLastWin32Error メソッドまたは同等の Win32 GetLastError 関数が呼び出されました。その直前に、プラットフォーム呼び出しメソッドが呼び出されていません。

規則の説明

プラットフォーム呼び出しメソッドは、Visual Basic の Declare キーワードまたは System.Runtime.InteropServices.DllImportAttribute 属性を使用して、アンマネージ コードにアクセスし、定義されます。 一般に、エラーが発生した場合、アンマネージ関数は Win32 の SetLastError 関数を呼び出し、そのエラーに関連付けられたエラー コードを設定します。 エラーが発生した関数の呼び出し元は、Win32 の GetLastError 関数を呼び出してエラー コードを取得し、エラーの原因を判断します。 エラー コードはスレッドごとに保持され、SetLastError を次に呼び出すと上書きされます。 マネージ コードでは、エラーの発生したプラットフォーム呼び出しメソッドの呼び出し後に、GetLastWin32Error メソッドを呼び出してエラー コードを取得できます。 エラー コードは、他のマネージ クラス ライブラリのメソッドからの内部的な呼び出しによって上書きできます。そのため、GetLastError または GetLastWin32Error メソッドは、プラットフォーム呼び出しメソッドを呼び出した直後に呼び出す必要があります。

次のマネージ メンバーに対する呼び出しが、プラットフォーム呼び出しメソッドと GetLastWin32Error の呼び出しの間に発生した場合、この規則では無視されます。 このようなメンバーによって、エラー コードは変化しないため、一部のプラットフォーム呼び出しメソッドの呼び出しが成功したことを判断するときに役立ちます。

違反の修正方法

この規則違反を修正するには、GetLastWin32Error の呼び出しを、プラットフォーム呼び出しメソッドの呼び出しの直後に移動します。

警告を抑制する状況

プラットフォーム呼び出しメソッドの呼び出しと GetLastWin32Error メソッドの呼び出しの間にあるコードによって、エラー コードが明示的または暗黙的に変更されない場合は、この規則による警告を抑制しても安全です。

使用例

この規則に違反しているメソッドと、規則に適合するメソッドを次の例に示します。

Imports System
Imports System.Runtime.InteropServices
Imports System.Text

Namespace InteroperabilityLibrary

   Class NativeMethods

      Private Sub New()
      End Sub

      ' Violates rule UseManagedEquivalentsOfWin32Api.
      Friend Declare Auto Function _
         ExpandEnvironmentStrings Lib "kernel32.dll" _ 
         (lpSrc As String, lpDst As StringBuilder, nSize As Integer) _ 
         As Integer

   End Class

   Public Class UseNativeMethod

      Dim environmentVariable As String = "%TEMP%"
      Dim expandedVariable As StringBuilder

      Sub ViolateRule()

         expandedVariable = New StringBuilder(100)

         If NativeMethods.ExpandEnvironmentStrings( _ 
            environmentVariable, _ 
            expandedVariable, _ 
            expandedVariable.Capacity) = 0

            ' Violates rule CallGetLastErrorImmediatelyAfterPInvoke.
            Console.Error.WriteLine(Marshal.GetLastWin32Error())
         Else
            Console.WriteLine(expandedVariable)
         End If

      End Sub

      Sub SatisfyRule()

         expandedVariable = New StringBuilder(100)

         If NativeMethods.ExpandEnvironmentStrings( _ 
            environmentVariable, _ 
            expandedVariable, _ 
            expandedVariable.Capacity) = 0

            ' Satisfies rule CallGetLastErrorImmediatelyAfterPInvoke.
            Dim lastError As Integer = Marshal.GetLastWin32Error()
            Console.Error.WriteLine(lastError)
         Else
            Console.WriteLine(expandedVariable)
         End If

      End Sub

   End Class

End Namespace
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace InteroperabilityLibrary
{
   internal class NativeMethods
   {
      private NativeMethods() {}

      // Violates rule UseManagedEquivalentsOfWin32Api.
      [DllImport("kernel32.dll", CharSet = CharSet.Auto, 
          SetLastError = true)]
      internal static extern int ExpandEnvironmentStrings(
         string lpSrc, StringBuilder lpDst, int nSize);
   }

   public class UseNativeMethod
   {
      string environmentVariable = "%TEMP%";
      StringBuilder expandedVariable;

      public void ViolateRule()
      {
         expandedVariable = new StringBuilder(100);

         if(NativeMethods.ExpandEnvironmentStrings(
            environmentVariable, 
            expandedVariable, 
            expandedVariable.Capacity) == 0)
         {
            // Violates rule CallGetLastErrorImmediatelyAfterPInvoke.
            Console.Error.WriteLine(Marshal.GetLastWin32Error());
         }
         else
         {
            Console.WriteLine(expandedVariable);
         }
      }

      public void SatisfyRule()
      {
         expandedVariable = new StringBuilder(100);

         if(NativeMethods.ExpandEnvironmentStrings(
            environmentVariable, 
            expandedVariable, 
            expandedVariable.Capacity) == 0)
         {
            // Satisfies rule CallGetLastErrorImmediatelyAfterPInvoke.
            int lastError = Marshal.GetLastWin32Error();
            Console.Error.WriteLine(lastError);
         }
         else
         {
            Console.WriteLine(expandedVariable);
         }
      }
   }
}

関連規則

CA1060: P/Invoke を NativeMethods クラスに移動します

CA1400: P/Invoke エントリ ポイントは存在しなければなりません

CA1401: P/Invoke は参照可能になりません

CA2101: P/Invoke 文字列引数に対してマーシャリングを指定します

CA2205: Win32 API に相当するマネージ API を使用します