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