Condividi tramite


CA1404: Chiamare GetLastError immediatamente dopo P/Invoke

TypeName

CallGetLastErrorImmediatelyAfterPInvoke

CheckId

CA1404

Category

Microsoft.Interoperability

Breaking Change

Non sostanziale

Causa

Viene effettuata una chiamata al metodo Marshal.GetLastWin32Error o alla funzione GetLastError Win32 equivalente e la chiamata immediatamente precedente non è una chiamata a un metodo platform invoke.

Descrizione della regola

Un metodo di platform invoke accede al codice non gestito e viene definito mediante la parola chiave Declare in Visual Basic o mediante l'attributo System.Runtime.InteropServices.DllImportAttribute. In genere, in caso di errore, le funzioni non gestite chiamano la funzione SetLastError Win32 per impostare un codice di errore associato all'errore. Il chiamante della funzione non riuscita chiama la funzione GetLastError Win32 per recuperare il codice di errore e determinare la causa dell'errore. Il codice errore viene mantenuto per ogni singolo thread e viene sovrascritto dalla successiva chiamata a SetLastError. Dopo una chiamata a un metodo di platform invoke non riuscito, il codice gestito può recuperare il codice di errore chiamando il metodo GetLastWin32Error. Poiché il codice di errore può essere sovrascritto da chiamate interne da altri metodi della libreria di classi gestite, il metodo GetLastError o GetLastWin32Error deve essere chiamato immediatamente dopo la chiamata al metodo di platform invoke.

La regola ignora le chiamate ai seguenti membri gestiti quando si verificano tra la chiamata al metodo di platform invoke e la chiamata a GetLastWin32Error. Questi membri non modificano il codice di errore e sono utili per determinare la riuscita di determinate chiamate a metodi di platform invoke.

Come correggere le violazioni

Per correggere una violazione di questa regola, spostare la chiamata a GetLastWin32Error in modo che segua immediatamente la chiamata al metodo di platform invoke.

Esclusione di avvisi

L'esclusione di un avviso da questa regola è sicura se il codice tra la chiamata al metodo P/Invoke e la chiamata al metodo GetLastWin32Error non può causare in modo esplicito o implicito la modifica del codice di errore.

Esempio

Nell'esempio riportato di seguito viene illustrato un metodo che viola la regola e un metodo che la soddisfa.

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

Regole correlate

CA1060: Spostare i P/Invoke nella classe NativeMethods

CA1400: I punti di ingresso P/Invoke devono esistere

CA1401: I P/Invoke non devono essere visibili

CA2101: Specificare il marshalling per gli argomenti di stringa P/Invoke

CA2205: Utilizzare equivalenti gestiti dell'API Win32