다음을 통해 공유


CA1404: P/Invoke 다음에 바로 GetLastError를 호출하십시오.

TypeName

CallGetLastErrorImmediatelyAfterPInvoke

CheckId

CA1404

범주

Microsoft.Interoperability

변경 수준

주요 변경 아님

원인

Marshal.GetLastWin32Error 메서드 또는 이와 동일한 기능의 Win32 GetLastError 함수를 호출하였으며 직전의 호출이 플랫폼 호출 메서드에 대한 호출이 아닙니다.

규칙 설명

플랫폼 호출 메서드가 비관리 코드에 액세스하고 Visual Basic의 Declare 키워드 또는 System.Runtime.InteropServices.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/Invoke를 NativeMethods 클래스로 이동

CA1400: P/Invoke 진입점이 있어야 합니다.

CA1401: P/Invoke는 노출되지 않아야 합니다.

CA2101: P/Invoke 문자열 인수에 대해 마샬링을 지정하십시오.

CA2205: Win32 API에 있는 동일한 기능의 관리되는 항목을 사용하십시오.