CA1060: P/Invoke を NativeMethods クラスに移動します
TypeName |
MovePInvokesToNativeMethodsClass |
CheckId |
CA1060 |
[カテゴリ] |
Microsoft.Design |
互換性に影響する変更点 |
あり |
原因
メソッドは、プラットフォーム呼び出しサービスを使用してアンマネージ コードにアクセスしていますが、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 のみの格納用に設計されている適切なクラスに移動する必要があります。
Imports System
NameSpace MSInternalLibrary
' Violates rule: MovePInvokesToNativeMethodsClass.
Friend Class UnmanagedApi
Friend Declare Function RemoveDirectory Lib "kernel32" ( _
ByVal Name As String) As Boolean
End Class
End NameSpace
using System;
using System.Runtime.InteropServices;
namespace DesignLibrary
{
// Violates rule: MovePInvokesToNativeMethodsClass.
internal class UnmanagedApi
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool RemoveDirectory(string name);
}
}
NativeMethods の例
Description
NativeMethods クラスは SuppressUnmanagedCodeSecurityAttribute を使用してマークできないため、このクラスに配置されている P/Invoke には UnmanagedCode アクセス許可が必要です。ほとんどのアプリケーションはローカル コンピューターから完全信頼で実行されるため、通常、これは問題になりません。ただし、再利用できるライブラリを開発している場合は、SafeNativeMethods クラスまたは UnsafeNativeMethods クラスを定義することを検討する必要があります。
user32.dll の MessageBeep 関数をラップする Interaction.Beep メソッドの例を次に示します。MessageBeep P/Invoke は NativeMethods クラスに配置されています。
コード
Imports System
Imports System.Runtime.InteropServices
Imports System.ComponentModel
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
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
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 の例
Description
アプリケーションに公開しても安全で、副作用がない P/Invoke メソッドは、SafeNativeMethods というクラスに配置する必要があります。アクセス許可を要求する必要も、呼び出し元に多大な注意を払う必要もありません。
kernel32.dll の GetTickCount 関数をラップする Environment.TickCount プロパティの例を次に示します。
コード
Imports System
Imports System.Runtime.InteropServices
Imports System.Security
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
using System;
using System.Runtime.InteropServices;
using System.Security;
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 の例
Description
呼び出しが安全でなく、副作用が発生する可能性がある P/Invoke メソッドは、UnsafeNativeMethods というクラスに配置する必要があります。これらのメソッドは、誤ってユーザーに公開されないように厳密にチェックする必要があります。これには、規則「CA2118: SuppressUnmanagedCodeSecurityAttribute の使用法を再確認します」が役立ちます。または、これらのメソッドを使用する場合に、UnmanagedCode ではなく、別のアクセス許可を要求する必要があります。
user32.dll の ShowCursor 関数をラップする Cursor.Hide メソッドの例を次に示します。
コード
Imports System
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.Permissions
Public NotInheritable Class Cursor
Private Sub New()
End Sub
' Callers do not require Unmanaged permission, however,
' they do require UIPermission.AllWindows
Public Shared Sub Hide()
' Need to demand an appropriate permission
' in place of UnmanagedCode permission as
' ShowCursor is not considered a safe method
Dim permission As New UIPermission(UIPermissionWindow.AllWindows)
permission.Demand()
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
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
public static class Cursor
{
// Callers do not require UnmanagedCode permission, however,
// they do require UIPermissionWindow.AllWindows
public static void Hide()
{
// Need to demand an appropriate permission
// in place of UnmanagedCode permission as
// ShowCursor is not considered a safe method
new UIPermission(UIPermissionWindow.AllWindows).Demand();
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);
}