CA1404: вызывайте GetLastError сразу после P/Invoke
TypeName |
CallGetLastErrorImmediatelyAfterPInvoke |
CheckId |
CA1404 |
Категория |
Microsoft.Interoperability |
Критическое изменение |
Не критическое |
Причина
Выполняется вызов метода Marshal.GetLastWin32Error или эквивалентной функции Win32 GetLastError, а непосредственно предшествующий вызов не является методом вызова неуправляемого кода.
Описание правила
Метод вызова платформы получает доступ к неуправляемому коду и определяется с помощью ключевого слова Declare в Visual Basic или атрибута DllImportAttribute.Как правило, в случае сбоя, неуправляемые функции вызывают функцию Win32 SetLastError для установки кода ошибки, связанной со сбоем.Вызывающий метод ошибочной функции вызывает функцию Win32 GetLastError для извлечения кода ошибки и определения причины сбоя.Код ошибки сохраняется для каждого потока и перезаписывается следующим вызовом SetLastError.После вызова ошибочного метода вызова неуправляемого кода управляемый код может извлечь код ошибки путем вызова метода GetLastWin32Error.Поскольку код ошибки может быть перезаписан внутренними вызовами от других методов библиотеки классов, метод GetLastError или GetLastWin32Error следует вызвать непосредственно после вызова метода вызова неуправляемого кода.
Правило не учитывает вызовы следующих управляемых членов, когда они происходят между вызовом метода вызова неуправляемого кода и вызовом GetLastWin32Error.Эти члены не изменяют кода ошибки и полезны для определения успешности некоторых вызовов метода вызова неуправляемого кода.
Устранение нарушений
Чтобы исправить эту ошибку, переместите вызов GetLastWin32Error так, чтобы он следовал сразу за вызовом метода вызова неуправляемого кода.
Отключение предупреждений
Предупреждение из этого правила можно отключить без последствий, если код между методом вызова неуправляемого кода и вызовом метода GetLastWin32Error не может явным или неявным образом вызвать изменение кода ошибки.
Пример
В следующем примере показан метод, который нарушает данное правило, и метод, удовлетворяющий ему.
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);
}
}
}
}
Связанные правила
CA1060: переместите P/Invokes в класс NativeMethods
CA1400: необходимо наличие точек входа P/Invoke
CA1401: методы P/Invoke не должны быть видимыми
CA2101: укажите тип маршалинга для строковых аргументов P/Invoke