CA1060: P/Invoke を NativeMethods クラスに移動します
プロパティ | 値 |
---|---|
ルール ID | CA1060 |
Title | 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 という 2 つのクラスを定義することを検討する必要があります。 これらのクラスは 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 クラスを定義することを検討する必要があります。
次の例では、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);
}
SafeNativeMethods の例
すべてのアプリケーションに安全に公開でき、副作用がない P/Invoke メソッドは、SafeNativeMethods という名前のクラスに入れる必要があります。 どこから呼び出されるかについて、あまり注意を払う必要はありません。
次の例では、kernel32.dll の GetTickCount 関数をラップする Environment.TickCount プロパティを示します。
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 という名前のクラスに入れる必要があります。 これらのメソッドは、意図せずにユーザーに公開されないよう、厳密にチェックする必要があります。
次の例では、user32.dll の ShowCursor 関数をラップする Cursor.Hide メソッドを示します。
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);
}
関連項目
.NET