Condividi tramite


Chiamare GetLastError immediatamente dopo P/Invoke

Aggiornamento: novembre 2007

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 di richiamo piattaforma.

Descrizione della regola

Un metodo di richiamo piattaforma 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 di errore viene mantenuto per ogni singolo thread e viene sovrascritto dalla successiva chiamata a SetLastError. Dopo una chiamata a un metodo di richiamo piattaforma 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 richiamo piattaforma.

La regola ignora le chiamate ai seguenti membri gestiti quando si verificano tra la chiamata al metodo di richiamo piattaforma 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 richiamo piattaforma.

Correzione di violazioni

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

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

Spostare i P/Invoke nella classe dei metodi nativi

I punti di ingresso P/Invoke devono esistere

I P/Invokes non devono essere visibili

Specificare il marshalling per gli argomenti di stringa P/Invoke

Utilizzare equivalenti gestiti dell'API Win32