CA1060:将 P/Invoke 移动到 NativeMethods 类
属性 | 值 |
---|---|
规则 ID | CA1060 |
标题 | 将 P/Invoke 移动到 NativeMethods 类 |
类别 | 设计 |
修复是中断修复还是非中断修复 | 重大 |
在 .NET 8 中默认启用 | 否 |
原因
方法使用平台调用服务访问非托管代码,不是 NativeMethods 类之一的成员。
规则说明
平台调用方法(如使用 System.Runtime.InteropServices.DllImportAttribute 属性标记的方法)或在 Visual Basic 中使用 Declare
关键字定义的方法可访问非托管代码。 这些方法应是处于以下一个类中:
NativeMethods - 此类不会对非托管代码权限取消堆栈审核。 (System.Security.SuppressUnmanagedCodeSecurityAttribute 不得应用于此类。)此类用于可在任何位置使用的方法,因为会执行堆栈审核。
SafeNativeMethods - 此类会对非托管代码权限取消堆栈审核。 (System.Security.SuppressUnmanagedCodeSecurityAttribute 应用于此类。)此类用于可供任何人安全调用的方法。 这些方法的调用方不需要执行完整安全评审以确保使用是安全的,因为这些方法对于任何调用方都无害。
UnsafeNativeMethods - 此类会对非托管代码权限取消堆栈审核。 (System.Security.SuppressUnmanagedCodeSecurityAttribute 应用于此类。)此类用于有潜在危险的方法。 这些方法的任何调用方都必须执行完整安全检查,以确保使用是安全的,因为不会执行任何堆栈审核。
这些类声明为 internal
(在 Visual Basic 中为 Friend
),并声明一个私有构造函数来阻止创建新实例。 这些类中的方法应是 static
和 internal
(在在 Visual Basic 中是 Shared
和 Friend
)。
如何解决冲突
若要解决与此规则的冲突,请将方法移动到合适的 NativeMethods 类中。 对于大多数应用程序,将 P/Invoke 移动到名为 NativeMethods 的新类便足够了。
但是,如果要开发在其他应用程序中使用的库,应考虑定义两个名为 SafeNativeMethods 和 UnsafeNativeMethods 的其他类。 这些类与 NativeMethods 类相似;但是,它们使用名为 SuppressUnmanagedCodeSecurityAttribute 的特殊属性进行标记 。 应用此属性时,运行时不会执行完整堆栈审核来确保所有调用方都具有 UnmanagedCode 权限。 运行时通常会在启动时检查是否具有此权限。 由于未执行此检查,因此可极大地提高对这些非托管方法的调用的性能, 还使具备有限权限的代码可以调用这些方法。
不过,应非常小心地使用此属性。 如果未正确实现,则可能会产生严重的安全隐患。
有关如何实现这些方法的信息,请参阅 NativeMethods 示例、SafeNativeMethods 示例和 UnsafeNativeMethods 示例 。
何时禁止显示警告
不禁止显示此规则发出的警告。
示例
下面的示例声明了违反此规则的方法。 若要更正该冲突,应将 RemoveDirectory P/Invoke 移动到设计为仅保存 P/invoke 的适当类。
' 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/invoke 需要 UnmanagedCode 权限 。 由于大多数应用程序从本地计算机运行并随完全信任一起运行,因此这通常不会成为问题。 但是,如果要开发可重用的库,则应考虑定义 SafeNativeMethods 或 UnsafeNativeMethods 类 。
下面的示例演示了一个 Interaction.Beep 方法,它可包装来自 user32.dll 的 MessageBeep 函数 。 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);
}
SafeNativeMethods 示例
可以安全地向任何应用程序公开并且没有任何副作用的 P/Invoke 方法应置于名为 SafeNativeMethods 的类中。 不必过多注意从何处调用它们。
下面的示例演示了一个 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);
}