CA1404: Call GetLastError immediately after P/Invoke

 

For the latest documentation on Visual Studio 2017 RC, see Visual Studio 2017 RC Documentation.

TypeNameCallGetLastErrorImmediatelyAfterPInvoke
CheckIdCA1404
CategoryMicrosoft.Interoperability
Breaking ChangeNon-breaking

A call is made to the Marshal.GetLastWin32Error method or the equivalent Win32 GetLastError function, and the call that comes immediately before is not to a platform invoke method.

A platform invoke method accesses unmanaged code and is defined by using the Declare keyword in Visual Basic or the System.Runtime.InteropServices.DllImportAttribute attribute. Generally, upon failure, unmanaged functions call the Win32 SetLastError function to set an error code that is associated with the failure. The caller of the failed function calls the Win32 GetLastError function to retrieve the error code and determine the cause of the failure. The error code is maintained on a per-thread basis and is overwritten by the next call to SetLastError. After a call to a failed platform invoke method, managed code can retrieve the error code by calling the GetLastWin32Error method. Because the error code can be overwritten by internal calls from other managed class library methods, the GetLastError or GetLastWin32Error method should be called immediately after the platform invoke method call.

The rule ignores calls to the following managed members when they occur between the call to the platform invoke method and the call to GetLastWin32Error. These members do not change the error code and are useful for determining the success of some platform invoke method calls.

To fix a violation of this rule, move the call to GetLastWin32Error so that it immediately follows the call to the platform invoke method.

It is safe to suppress a warning from this rule if the code between the platform invoke method call and the GetLastWin32Error method call cannot explicitly or implicitly cause the error code to change.

The following example shows a method that violates the rule and a method that satisfies the rule.

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

CA1060: Move P/Invokes to NativeMethods class

CA1400: P/Invoke entry points should exist

CA1401: P/Invokes should not be visible

CA2101: Specify marshaling for P/Invoke string arguments

CA2205: Use managed equivalents of Win32 API

Show: