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 套用至這個類別。這個類別適用于可能很危險的方法。 這些方法的任何呼叫端都必須執行完整的安全性檢閱,以確保使用方式安全,因為不會執行堆疊逐步解說。
這些類別會宣告為 internal
( Friend
在 Visual Basic 中),並宣告私人建構函式以防止建立新的實例。 這些類別中的方法應該是 static
和 internal
( Shared
在 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);
}