CA1404:紧接在 P/Invoke 之后调用 GetLastError

适用范围:yesVisual Studio noVisual Studio for Mac noVisual Studio Code

“值”
RuleId CA1404
Category Microsoft.Interoperability
重大更改 非中断

原因

System.Runtime.InteropServices.Marshal.GetLastWin32Error 方法或等效的 Win32 GetLastError 函数进行调用,并且紧接在此之前的调用不是对平台 invoke 方法的调用。

规则说明

平台调用方法访问非托管代码,并使用 Visual Basic 中的 Declare 关键字或 System.Runtime.InteropServices.DllImportAttribute 属性进行定义。 通常,在失败时,非托管函数调用 Win32 SetLastError 函数来设置与失败关联的错误代码。 失败函数的调用方调用 Win32 GetLastError 函数来检索错误代码并确定失败的原因。 错误代码基于每个线程进行维护,并被下一次调用 SetLastError 覆盖。 调用失败的平台调用方法后,托管代码可以通过调用 GetLastWin32Error 方法检索错误代码。 由于错误代码可能会被其他托管类库方法的内部调用覆盖,因此应在平台调用方法调用后立即调用 GetLastErrorGetLastWin32Error 方法。

当对平台调用方法的调用和对 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/Invokes 应为不可见

CA2101:指定对 P/Invoke 字符串参数进行封送处理

CA2205:使用 Win32 API 的托管等效项