Share via


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

类型名

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/Invokes 应该是不可见的

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

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