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