Export (0) Print
Expand All

CA1404: Call GetLastError immediately after P/Invoke

TypeName

CallGetLastErrorImmediatelyAfterPInvoke

CheckId

CA1404

Category

Microsoft.Interoperability

Breaking Change

Non-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.


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);
         }
      }
   }
}


Community Additions

ADD
Show:
© 2014 Microsoft