Compartir a través de


CA1404: Llame a GetLastError inmediatamente después de P/Invoke

Nombre de tipo

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 System.Runtime.InteropServices.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

CA2205: Utilizar equivalentes administrados de la API Win32