CA1404: GetLastError unmittelbar nach P/Invoke aufrufen
TypeName |
CallGetLastErrorImmediatelyAfterPInvoke |
CheckId |
CA1404 |
Kategorie |
Microsoft.Interoperability |
Unterbrechende Änderung |
Nicht unterbrechend |
Ursache
Die Marshal.GetLastWin32Error-Methode oder die entsprechende GetLastError-Win32-Funktion wird aufgerufen, und unmittelbar zuvor wird keine Plattformaufrufmethode aufgerufen.
Regelbeschreibung
Eine Plattformaufrufmethode greift auf nicht verwalteten Code zu und wird mit dem Declare-Schlüsselwort in Visual Basic oder dem System.Runtime.InteropServices.DllImportAttribute-Attribut definiert. Bei einem Fehler rufen nicht verwaltete Funktionen im Allgemeinen die SetLastError-Win32-Funktion auf, um einen Fehlercode festzulegen, der dem Fehler zugeordnet wird. Der Aufrufer der fehlgeschlagenen Funktion ruft die GetLastError-Win32-Funktion auf, um den Fehlercode abzurufen und die Ursache des Fehlers zu ermitteln. Der Fehlercode wird threadweise verwaltet und durch den nächsten Aufruf von SetLastError überschrieben. Nach einem Aufruf einer fehlgeschlagenen Plattformaufrufmethode kann der Fehlercode durch verwalteten Code abgerufen werden. Dazu wird die GetLastWin32Error-Methode aufgerufen. Da der Fehlercode durch interne Aufrufe überschrieben werden kann, die von anderen verwalteten Klassenbibliotheksmethoden stammen, sollte die GetLastError-Methode oder die GetLastWin32Error-Methode unmittelbar nach dem Aufruf der Plattformaufrufmethode aufgerufen werden.
Die Regel ignoriert Aufrufe der folgenden verwalteten Member, wenn diese zwischen dem Aufruf der Plattformaufrufmethode und dem Aufruf von GetLastWin32Error erfolgen. Diese Member ändern den Fehlercode nicht, und mithilfe dieser Member lässt sich feststellen, ob bestimmte Aufrufe von Plattformaufruftmethoden erfolgreich waren.
Behandeln von Verstößen
Um einen Verstoß gegen diese Regel zu beheben, verschieben Sie den Aufruf in GetLastWin32Error, damit er unmittelbar auf den Aufruf der Plattformaufrufmethode folgt.
Wann sollten Warnungen unterdrückt werden?
Eine Warnung dieser Regel kann gefahrlos unterdrückt werden, wenn der Code zwischen dem Aufruf der Plattformaufrufmethode und dem Aufruf der GetLastWin32Error-Methode keine explizite oder implizite Änderung des Fehlercodes bewirken kann.
Beispiel
Das folgende Beispiel zeigt eine Methode, die gegen die Regel verstößt, und eine Methode, die der Regel entspricht.
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);
}
}
}
}
Verwandte Regeln
CA1060: P/Invokes in NativeMethods-Klasse verschieben
CA1400: Für P/Invoke müssen Einstiegspunkte vorhanden sein
CA1401: P/Invokes dürfen nicht sichtbar sein
CA2101: Marshalling für P/Invoke-Zeichenfolgenargumente festlegen