C#プログラマリファレンス--プラットフォームコールチュートリアル
プラットフォーム呼び出しサービス(PInvoke)は、管理コードがDLLで実装される非管理関数を呼び出すことを可能にする.
このチュートリアルでは、C#から管理されていないDLL関数を呼び出す方法について説明します.このチュートリアルで説明したプロパティを使用すると、これらの関数を呼び出し、データ型を正しく閉じることができます.
チュートリアル
C#コードには、管理されていないコードを直接呼び出す方法が2つあります.は、DLLから導出された関数を直接呼び出す. COMオブジェクトのインタフェースメソッドを呼び出します(詳細は、COM Interop第1部:C#クライアントチュートリアルを参照).
この2つのテクノロジーでは、非管理関数の宣言をC#コンパイラに提供する必要があります.また、非管理コードとの間で伝達されるパラメータと戻り値をどのようにカプセル化するかの説明もC#コンパイラに提供する必要があります.
このチュートリアルは、次のトピックで構成されています. C#呼び出しDLLから を直接エクスポートデフォルトの封入処理および非管理方法のパラメータのカスタム封入処理 を指定する.は、ユーザ定義の構成に対してカスタム封入処理 を指定する.登録コールバック方法 このチュートリアルには、次の例があります.例1 DllImport を用いる例2デフォルト封入処理 を書き換える.例3カスタム封入処理 を指定する.
C#呼び出しDLLから直接エクスポート
DLLからエクスポートされたインプリメンテーションを持つメソッドを宣言するには、次の手順に従います. Cキーワードstaticおよびextern宣言メソッドを使用します. は、DllImportプロパティをメソッドに添付します.DllImportプロパティを使用すると、このメソッドを含むDLLの名前を指定できます.通常、C#メソッドの名前は、エクスポートされたメソッドと同じ名前で付けられますが、C#メソッドには異なる名前を使用することもできます. はまた、メソッドのパラメータおよび戻り値に対してカスタム封入処理情報を指定することができ、これは書き換えられる.NET Frameworkのデフォルトの封入処理.
例1
この例では、DllImportプロパティを使用して、
しゅつりょく
コードディスカッション
前の例では、非管理DLLで実装されるC#メソッドを宣言する最低要件を示した.
DllImportプロパティの構文の詳細については、DllImportAttributeクラスを参照してください.
デフォルトのパッケージング処理と管理されていないメソッドのパラメータのカスタムパッケージング処理の指定
C#コードから非管理関数を呼び出す場合、共通言語実行ライブラリはパラメータと戻り値をカプセル化する必要があります.
それぞれについて.NET Frameworkタイプには、デフォルトの非管理タイプがあり、共通言語実行ライブラリは、この非管理タイプを使用して、非管理に管理されている関数呼び出しでデータをカプセル化します.例えば、C#文字列値のデフォルトの封入処理は、LPTSTR(TCHAR文字バッファへのポインタ)タイプとして封入される.デフォルトの封入処理は、管理されていない関数のC#宣言でMarshalAsプロパティを使用して書き換えることができます.
例2
この例では、DllImportプロパティを使用して文字列を出力します.また、MarshalAsプロパティを使用して関数パラメータのデフォルトの封入処理を書き換える方法も表示されます.
しゅつりょく
この例を実行すると、文字列
コンソールに表示されます.
コードディスカッション
前述の例では、
MarshalAsプロパティは、メソッドパラメータ、メソッド戻り値、および構造およびクラスのフィールドに配置できます.メソッド戻り値の封入処理を設定するには、メソッド上のプロパティブロックにMarshalAsプロパティを戻りプロパティ位置書き換えとともに配置します.例えば、
MarshalAsプロパティの構文の詳細については、MarshalAsAttributeクラスを参照してください.
Inと
Outプロパティは、管理されていないメソッドのパラメータをコメントするために使用できます.MIDLソースファイルの
in和
out修飾子の動作は似ています.注意してください.
Out属性はC#パラメータ修飾子outとは異なります.に関係
In和
Outプロパティの詳細については、InAttributeクラスとOutAttributeクラスを参照してください.
ユーザー定義の構造にカスタム封入処理を指定する
管理されていない関数に渡すか、管理されていない関数から返される構造およびクラスのフィールドにカスタム・パッケージング処理プロパティを指定できます.これは、構造またはクラスのフィールドにMarshalAsプロパティを追加することによって行います.StructLayoutプロパティを使用して構造のレイアウトを設定する必要があります.また、文字列メンバーのデフォルトのカプセル化処理を制御し、デフォルトのカプセル化サイズを設定することもできます.
例3
この例では、構造にカスタム封入処理プロパティを指定する方法を示します.
以下のC構造を考慮してください.
C#では、StructLayoutおよびMarshalAsプロパティを使用して、次のように前の構造を記述できます.
StructLayoutプロパティの構文の詳細については、StructLayoutAttributeクラスを参照してください.
次に、この構造をC#コードに使用します.次のようにします.
実行例
コードディスカッション
前の例では、
コールバックメソッドの登録
管理されていない関数を呼び出す管理コールバックを登録するには、同じパラメータリストで依頼を宣言し、PInvokeを介してそのインスタンスを渡します.管理されていないエンドでは、関数ポインタとして表示されます.PInvokeとコールバックの詳細については、プラットフォームコールの詳細を参照してください.
たとえば、callbackをパラメータの1つとして必要とする非管理関数
管理コードから
また、委託インスタンスの生存期間が非管理コードの生存期間をカバーしていることを確認してください.それ以外の場合、委託はゴミ回収後に使用できなくなります.
このチュートリアルでは、C#から管理されていないDLL関数を呼び出す方法について説明します.このチュートリアルで説明したプロパティを使用すると、これらの関数を呼び出し、データ型を正しく閉じることができます.
チュートリアル
C#コードには、管理されていないコードを直接呼び出す方法が2つあります.
この2つのテクノロジーでは、非管理関数の宣言をC#コンパイラに提供する必要があります.また、非管理コードとの間で伝達されるパラメータと戻り値をどのようにカプセル化するかの説明もC#コンパイラに提供する必要があります.
このチュートリアルは、次のトピックで構成されています.
C#呼び出しDLLから直接エクスポート
DLLからエクスポートされたインプリメンテーションを持つメソッドを宣言するには、次の手順に従います.
例1
この例では、DllImportプロパティを使用して、
msvcrt.dll
のputs
を呼び出してメッセージを出力する方法を示します.// PInvokeTest.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Test");
_flushall();
}
}
しゅつりょく
Test
コードディスカッション
前の例では、非管理DLLで実装されるC#メソッドを宣言する最低要件を示した.
PlatformInvokeTest.puts
メソッドはstaticおよびextern修飾子で宣言され、デフォルト名puts
を使用してコンパイラにこのインプリメンテーションがmsvcrt.dll
から来たことを通知するDllImportプロパティを有する.putstring
のようにC#メソッドに異なる名前を使用するには、次のようにDllImportプロパティでEntryPointオプションを使用する必要があります.[DllImport("msvcrt.dll", EntryPoint="puts")]
DllImportプロパティの構文の詳細については、DllImportAttributeクラスを参照してください.
デフォルトのパッケージング処理と管理されていないメソッドのパラメータのカスタムパッケージング処理の指定
C#コードから非管理関数を呼び出す場合、共通言語実行ライブラリはパラメータと戻り値をカプセル化する必要があります.
それぞれについて.NET Frameworkタイプには、デフォルトの非管理タイプがあり、共通言語実行ライブラリは、この非管理タイプを使用して、非管理に管理されている関数呼び出しでデータをカプセル化します.例えば、C#文字列値のデフォルトの封入処理は、LPTSTR(TCHAR文字バッファへのポインタ)タイプとして封入される.デフォルトの封入処理は、管理されていない関数のC#宣言でMarshalAsプロパティを使用して書き換えることができます.
例2
この例では、DllImportプロパティを使用して文字列を出力します.また、MarshalAsプロパティを使用して関数パラメータのデフォルトの封入処理を書き換える方法も表示されます.
// Marshal.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(
[MarshalAs(UnmanagedType.LPStr)]
string m);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Hello World!");
_flushall();
}
}
しゅつりょく
この例を実行すると、文字列
Hello World!
コンソールに表示されます.
コードディスカッション
前述の例では、
puts
関数のパラメータのデフォルト封入処理は、デフォルト値LPTSTRからLPSTRに書き換えられている.MarshalAsプロパティは、メソッドパラメータ、メソッド戻り値、および構造およびクラスのフィールドに配置できます.メソッド戻り値の封入処理を設定するには、メソッド上のプロパティブロックにMarshalAsプロパティを戻りプロパティ位置書き換えとともに配置します.例えば、
puts
メソッドの戻り値を明示的に設定する封止処理は、次のようになります....
[DllImport("msvcrt.dll")]
[return : MarshalAs(UnmanagedType.I4)]
public static extern int puts(
...
MarshalAsプロパティの構文の詳細については、MarshalAsAttributeクラスを参照してください.
Inと
Outプロパティは、管理されていないメソッドのパラメータをコメントするために使用できます.MIDLソースファイルの
in和
out修飾子の動作は似ています.注意してください.
Out属性はC#パラメータ修飾子outとは異なります.に関係
In和
Outプロパティの詳細については、InAttributeクラスとOutAttributeクラスを参照してください.
ユーザー定義の構造にカスタム封入処理を指定する
管理されていない関数に渡すか、管理されていない関数から返される構造およびクラスのフィールドにカスタム・パッケージング処理プロパティを指定できます.これは、構造またはクラスのフィールドにMarshalAsプロパティを追加することによって行います.StructLayoutプロパティを使用して構造のレイアウトを設定する必要があります.また、文字列メンバーのデフォルトのカプセル化処理を制御し、デフォルトのカプセル化サイズを設定することもできます.
例3
この例では、構造にカスタム封入処理プロパティを指定する方法を示します.
以下のC構造を考慮してください.
typedef struct tagLOGFONT
{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;
C#では、StructLayoutおよびMarshalAsプロパティを使用して、次のように前の構造を記述できます.
// logfont.cs
// compile with: /target:module
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class LOGFONT
{
public const int LF_FACESIZE = 32;
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
public string lfFaceName;
}
StructLayoutプロパティの構文の詳細については、StructLayoutAttributeクラスを参照してください.
次に、この構造をC#コードに使用します.次のようにします.
// pinvoke.cs
// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateFontIndirect(
[In, MarshalAs(UnmanagedType.LPStruct)]
LOGFONT lplf // characteristics
);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(
IntPtr handle
);
public static void Main()
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 9;
lf.lfFaceName = "Arial";
IntPtr handle = CreateFontIndirect(lf);
if (IntPtr.Zero == handle)
{
Console.WriteLine("Can't creates a logical font.");
}
else
{
if (IntPtr.Size == 4)
Console.WriteLine("{0:X}", handle.ToInt32());
else
Console.WriteLine("{0:X}", handle.ToInt64());
// Delete the logical font created.
if (!DeleteObject(handle))
Console.WriteLine("Can't delete the logical font");
}
}
}
実行例
C30A0AE5
コードディスカッション
前の例では、
CreateFontIndirect
メソッドは、LOGFONTタイプのパラメータを使用する.MarshalAsとInのプロパティを使用して、このパラメータを定義します.プログラムは、この方法で返される数値を16進数の大文字文字列として表示します.コールバックメソッドの登録
管理されていない関数を呼び出す管理コールバックを登録するには、同じパラメータリストで依頼を宣言し、PInvokeを介してそのインスタンスを渡します.管理されていないエンドでは、関数ポインタとして表示されます.PInvokeとコールバックの詳細については、プラットフォームコールの詳細を参照してください.
たとえば、callbackをパラメータの1つとして必要とする非管理関数
MyFunction
を考慮します.typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyFunction(PFN_ MYCALLBACK callback);
管理コードから
MyFunction
を呼び出すには、委任を宣言し、関数宣言にDllImportを添付し、必要に応じてパラメータまたは戻り値をカプセル化します.public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
public static extern void MyFunction(MyCallback callback);
また、委託インスタンスの生存期間が非管理コードの生存期間をカバーしていることを確認してください.それ以外の場合、委託はゴミ回収後に使用できなくなります.