CA1404: Llame a GetLastError inmediatamente después de P/Invoke
TypeName |
CallGetLastErrorImmediatelyAfterPInvoke |
Identificador de comprobación |
CA1404 |
Categoría |
Microsoft.Interoperability |
Cambio problemático |
Poco problemático |
Motivo
Se llama al método Marshal.GetLastWin32Error o al equivalente de la función GetLastError de Win32 y la llamada inmediatamente anterior no es un método de invocación de plataforma.
Descripción de la regla
Un método de invocación de plataforma tiene acceso al código no administrado y se define utilizando la palabra clave Declare en Visual Basic o el atributo DllImportAttribute.Normalmente, cuando se produce un error, las funciones no administradas llaman a la función SetLastError de Win32 para establecer un código de error que se asocia al error.El llamador de las funciones que han producido un error llama a la función GetLastError de Win32 para recuperar el código de error y determinar la causa del error.El código de error se mantiene por subproceso y se sobrescribe mediante la llamada siguiente a SetLastError.Después de una llamada a un método de invocación de plataforma que provoca un error, el código administrado puede recuperar el código de error llamando al método GetLastWin32Error.Puesto que el código de error puede sobrescribirse mediante llamadas internas desde otros métodos de la biblioteca de clases administrada, el método GetLastError o GetLastWin32Error deberían llamarse inmediatamente después de la llamada al método de invocación de plataforma.
La regla omite las llamadas a los siguientes miembros administrados cuando se producen entre la llamada al método de invocación de plataforma y la llamada a GetLastWin32Error.Estos miembros no cambian el código de error y son útiles para determinar si son correctas algunas llamadas al método de invocación de plataforma.
Cómo corregir infracciones
Para corregir una infracción de esta regla, mueva la llamada a GetLastWin32Error para que inmediatamente después se produzca la llamada al método de invocación de plataforma.
Cuándo suprimir advertencias
Es seguro suprimir una advertencia de esta regla si el código entre la llamada al método de invocación de plataforma y la llamada al método GetLastWin32Error no provoca, explícita ni implícitamente, ningún código de error que haya que modificar.
Ejemplo
El ejemplo siguiente muestra un método que infringe la regla y un método que la cumple.
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);
}
}
}
}
Reglas relacionadas
CA1060: Mueva P/Invokes a la clase NativeMethods
CA1400: Deben existir puntos de entrada P/Invoke
CA1401: Los elementos P/Invoke no deben estar visibles
CA2101: Especifique cálculo de referencias para argumentos de cadena P/Invoke