Partager via


CA1404 : Appeler GetLastError immédiatement après P/Invoke

TypeName

CallGetLastErrorImmediatelyAfterPInvoke

CheckId

CA1404

Catégorie

Microsoft.Interoperability

Modification avec rupture

Modification sans rupture

Cause

La méthode Marshal.GetLastWin32Error ou la fonction GetLastError équivalente est appelée, et l'appel immédiatement avant ne constitue pas une méthode d'appel de code non managé.

Description de la règle

Une méthode d'appel de code non managé accède à un code non managé et est définie à l'aide du mot clé Declare en Visual Basic ou de l'attribut System.Runtime.InteropServices.DllImportAttribute. En général, en cas de défaillance, les fonctions non managées appellent la fonction de Win32 SetLastError pour définir un code d'erreur associé à la défaillance. L'appelant de la fonction qui a échoué appelle la fonction Win32 GetLastError pour récupérer le code d'erreur et déterminer la cause de la défaillance. Le code d'erreur est conservé "par thread" et remplacé par l'appel suivant à SetLastError. À la suite d'une méthode d'appel de code non managé qui a échoué, le code managé peut récupérer le code d'erreur en appelant la méthode GetLastWin32Error. Sachant que le code d'erreur peut être remplacé par des appels internes émanant d'autres méthodes de la bibliothèque de classes managée, la méthode GetLastError ou GetLastWin32Error doit être appelée immédiatement après l'appel de la méthode d'appel de code non managé.

La règle ignore les appels aux membres managés suivants lorsqu'ils se produisent entre l'appel à la méthode d'appel de code non managé et l'appel à GetLastWin32Error. Ces membres ne modifient pas le code d'erreur et sont utiles pour déterminer la réussite de certains appels de méthode d'appel de code non managé.

Comment corriger les violations

Pour corriger une violation de cette règle, redirigez l'appel vers GetLastWin32Error afin qu'il suive immédiatement l'appel à la méthode d'appel de code non managé.

Quand supprimer les avertissements

Il est possible de supprimer sans risque un avertissement de cette règle si le code exécuté entre l'appel à la méthode d'appel de code non managé et l'appel à la méthode GetLastWin32Error ne peut pas explicitement ou implicitement provoquer la modification du code d'erreur.

Exemple

L'exemple suivant présente une méthode qui viole la règle et une autre qui satisfait à la règle.

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

Règles connexes

CA1060 : Déplacer les P/Invoke vers une classe NativeMethods

CA1400 : Des points d'entrée P/Invoke doivent exister

CA1401 : Les P/Invoke ne doivent pas être visibles

CA2101 : Spécifiez le marshaling pour les arguments de chaîne P/Invoke

CA2205 : Utilisez des équivalents managés de l'API Win32