Поделиться через


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

CA2205: используйте управляемые эквиваленты API Win32