Share via


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

Item [値]
規則 ID CA1404
カテゴリ Microsoft.Interoperability
互換性に影響する変更点 なし

原因

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

規則の説明

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

この規則では、プラットフォーム呼び出しメソッドの呼び出しと GetLastWin32Error の呼び出しの間に次のマネージド メンバーへの呼び出しが発生した場合に、その呼び出しが無視されます。 これらのメンバーではエラー コードが変更されず、一部のプラットフォーム呼び出しメソッドの呼び出しが成功したかどうかを判断するのに役立ちます。

違反の修正方法

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

どのようなときに警告を抑制するか

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

次に示すのは、規則に違反するメソッドと、規則を満たすメソッドの例です。

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 を使用します