CA1060:將 P/Invokes 移到 NativeMethods 類別

屬性
規則識別碼 CA1060
標題 必須將 P/Invokes 移到 NativeMethods 類別
類別 設計
修正程式是中斷或非中斷 中斷
預設在 .NET 8 中啟用 No

原因

方法會使用 Platform Invocation Services 來存取 Unmanaged 程式碼,而且不是其中一個 NativeMethods 類別的成員。

檔案描述

平台叫用方法,例如使用 System.Runtime.InteropServices.DllImportAttribute 屬性標記的方法,或是使用 Declare Visual Basic 中的 關鍵字所定義的方法,存取 Unmanaged 程式碼。 這些方法應位於下列其中一個類別中:

  • NativeMethods - 此類別不會隱藏 Unmanaged 程式碼許可權的堆疊逐步解說。 ( System.Security.SuppressUnmanagedCodeSecurityAttribute 不得套用至這個類別。這個類別適用于可在任何地方使用的方法,因為將會執行堆疊逐步解說。

  • 保管庫NativeMethods - 此類別會隱藏 Unmanaged 程式碼許可權的堆疊逐步解說。 ( System.Security.SuppressUnmanagedCodeSecurityAttribute 套用至這個類別。這個類別適用于可讓任何人呼叫安全的方法。 這些方法的呼叫端不需要執行完整的安全性檢閱,以確保使用方式安全,因為任何呼叫端的方法都是無害的。

  • UnsafeNativeMethods - 此類別會隱藏 Unmanaged 程式碼許可權的堆疊逐步解說。 ( System.Security.SuppressUnmanagedCodeSecurityAttribute 套用至這個類別。這個類別適用于可能很危險的方法。 這些方法的任何呼叫端都必須執行完整的安全性檢閱,以確保使用方式安全,因為不會執行堆疊逐步解說。

這些類別會宣告為 internalFriend 在 Visual Basic 中),並宣告私人建構函式以防止建立新的實例。 這些類別中的方法應該是 staticinternalShared 在 Visual Basic 中為 和 Friend )。

如何修正違規

若要修正此規則的違規,請將 方法移至適當的 NativeMethods 類別。 對於大部分的應用程式,將 P/Invokes 移至名為 NativeMethods 的新類別就足夠了。

不過,如果您要開發程式庫以用於其他應用程式,您應該考慮定義另外兩個稱為 保管庫NativeMethods UnsafeNativeMethods 的 類別。 這些類別類似于 NativeMethods 類別;不過,它們會使用名為 SuppressUnmanagedCodeSecurityAttribute 的特殊屬性來標記。 套用此屬性時,執行時間不會執行完整的堆疊逐步解說,以確保所有呼叫端都有 UnmanagedCode 許可權。 執行時間通常會在啟動時檢查此許可權。 由於未執行檢查,因此可以大幅改善對這些 Unmanaged 方法呼叫的效能。 它也會啟用具有有限許可權的程式碼來呼叫這些方法。

不過,您應該非常小心使用這個屬性。 如果實作不正確,可能會有嚴重的安全性影響。

如需如何實作方法的資訊,請參閱 NativeMethods 範例、 保管庫NativeMethods 範例和 UnsafeNativeMethods 範例。

隱藏警告的時機

請勿隱藏此規則的警告。

範例

下列範例會宣告違反此規則的方法。 若要更正違規, RemoveDirectory P/Invoke 應該移至設計為只保留 P/Invokes 的適當類別。

' Violates rule: MovePInvokesToNativeMethodsClass.
Friend Class UnmanagedApi
    Friend Declare Function RemoveDirectory Lib "kernel32" (
   ByVal Name As String) As Boolean
End Class
// Violates rule: MovePInvokesToNativeMethodsClass.
internal class UnmanagedApi
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    internal static extern bool RemoveDirectory(string name);
}

NativeMethods 範例

因為 NativeMethods 類別不應該使用 SuppressUnmanagedCodeSecurityAttribute 標記,所以放入它的 P/Invokes 需要 UnmanagedCode 許可權。 由於大部分的應用程式都會從本機電腦執行,並且與完全信任一起執行,因此這通常不是問題。 不過,如果您要開發可重複使用的程式庫,您應該考慮定義 保管庫NativeMethods UnsafeNativeMethods 類別。

下列範例顯示 從 user32.dll 包裝 MessageBeep 函式的 Interaction.Beep 方法。 MessageBeep P/Invoke 會放在 NativeMethods 類別中

Public NotInheritable Class Interaction

    Private Sub New()
    End Sub

    ' Callers require Unmanaged permission        
    Public Shared Sub Beep()
        ' No need to demand a permission as callers of Interaction.Beep                     
        ' will require UnmanagedCode permission                     
        If Not NativeMethods.MessageBeep(-1) Then
            Throw New Win32Exception()
        End If

    End Sub

End Class

Friend NotInheritable Class NativeMethods

    Private Sub New()
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Friend Shared Function MessageBeep(ByVal uType As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

End Class
public static class Interaction
{
    // Callers require Unmanaged permission        
    public static void Beep()
    {
        // No need to demand a permission as callers of Interaction.Beep            
        // will require UnmanagedCode permission            
        if (!NativeMethods.MessageBeep(-1))
            throw new Win32Exception();
    }
}

internal static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool MessageBeep(int uType);
}

保管庫NativeMethods 範例

可以安全地公開給任何應用程式的 P/Invoke 方法,而且沒有任何副作用,應該放在名為 保管庫NativeMethods 的 類別中。 你不必十分注意他們從哪裡被叫來。

下列範例顯示 Environment.TickCount 屬性,該屬性會包裝 kernel32.dll 中的 GetTickCount 函式。

Public NotInheritable Class Environment

    Private Sub New()
    End Sub

    ' Callers do not require Unmanaged permission       
    Public Shared ReadOnly Property TickCount() As Integer
        Get
            ' No need to demand a permission in place of               
            ' UnmanagedCode as GetTickCount is considered               
            ' a safe method               
            Return SafeNativeMethods.GetTickCount()
        End Get
    End Property

End Class

<SuppressUnmanagedCodeSecurityAttribute()>
Friend NotInheritable Class SafeNativeMethods

    Private Sub New()
    End Sub

    <DllImport("kernel32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)>
    Friend Shared Function GetTickCount() As Integer
    End Function

End Class
public static class Environment
{
    // Callers do not require UnmanagedCode permission       
    public static int TickCount
    {
        get
        {
            // No need to demand a permission in place of               
            // UnmanagedCode as GetTickCount is considered              
            // a safe method              
            return SafeNativeMethods.GetTickCount();
        }
    }
}

[SuppressUnmanagedCodeSecurityAttribute]
internal static class SafeNativeMethods
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    internal static extern int GetTickCount();
}

UnsafeNativeMethods 範例

無法安全地呼叫且可能導致副作用的 P/Invoke 方法應該放在名為 UnsafeNativeMethods 的 類別中。 這些方法應該經過嚴格檢查,以確保不會不小心公開給使用者。

下列範例顯示 Cursor.Hide 方法,這個方法會包裝 user32.dll 中的 ShowCursor 函式。

Public NotInheritable Class Cursor

    Private Sub New()
    End Sub

    Public Shared Sub Hide()
        UnsafeNativeMethods.ShowCursor(False)
    End Sub

End Class

<SuppressUnmanagedCodeSecurityAttribute()>
Friend NotInheritable Class UnsafeNativeMethods

    Private Sub New()
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)>
    Friend Shared Function ShowCursor(<MarshalAs(UnmanagedType.Bool)> ByVal bShow As Boolean) As Integer
    End Function

End Class
public static class Cursor
{
    public static void Hide()
    {
        UnsafeNativeMethods.ShowCursor(false);
    }
}

[SuppressUnmanagedCodeSecurityAttribute]
internal static class UnsafeNativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    internal static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)] bool bShow);
}

另請參閱