CA1404: вызывайте GetLastError сразу после P/Invoke

Товар Значение
Идентификатор правила CA1404
Категория Microsoft.Interoperability
Критическое изменение Не критическое

Причина

Вызов выполняется к System.Runtime.InteropServices.Marshal.GetLastWin32Error методу или эквивалентной функции Win32 GetLastError , и вызов, который происходит непосредственно перед тем, не является методом вызова платформы.

Описание правила

Платформа вызывает метод доступа к неуправляемого кода и определяется с помощью Declare ключевое слово в Visual Basic или атрибутеSystem.Runtime.InteropServices.DllImportAttribute. Как правило, при сбое неуправляемые функции вызывают функцию Win32 SetLastError , чтобы задать код ошибки, связанный с сбоем. Вызывающий функцию сбоем вызывает функцию Win32 GetLastError , чтобы получить код ошибки и определить причину сбоя. Код ошибки сохраняется на основе каждого потока и перезаписывается следующим вызовом SetLastError. После вызова метода вызова неудачной платформы управляемый код может получить код ошибки путем вызова GetLastWin32Error метода. Так как код ошибки может быть перезаписан внутренними вызовами из других методов библиотеки управляемых классов, GetLastError вызов или GetLastWin32Error метод должен вызываться сразу после вызова метода вызова метода платформы.

Правило игнорирует вызовы следующих управляемых членов, когда они происходят между вызовом метода вызова платформы и вызовом GetLastWin32Error. Эти члены не изменяют код ошибки и полезны для определения успешности вызовов метода вызова некоторых платформ.

Устранение нарушений

Чтобы устранить нарушение этого правила, переместите вызов GetLastWin32Error так, чтобы он сразу же следует вызову метода вызова платформы.

Когда лучше отключить предупреждения

Если код между вызовом метода вызова платформы и GetLastWin32Error вызовом метода невозможно явно или неявно вызвать код ошибки.

Пример

В следующем примере показан метод, нарушающий правило, и метод, соответствующий правилу.

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