CA1404: Call GetLastError immediately after P/Invoke
Item | Value |
---|---|
RuleId | CA1404 |
Category | Microsoft.Interoperability |
Breaking change | Non-breaking |
Cause
A call is made to the System.Runtime.InteropServices.Marshal.GetLastWin32Error method or the equivalent Win32 GetLastError
function, and the call that comes immediately before is not to a platform invoke method.
Rule description
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.
How to fix violations
To fix a violation of this rule, move the call to GetLastWin32Error so that it immediately follows the call to the platform invoke method.
When to suppress warnings
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.
Example
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);
}
}
}
}
Related rules
CA1060: Move P/Invokes to NativeMethods class
CA1400: P/Invoke entry points should exist
CA1401: P/Invokes should not be visible