C#プログラマリファレンス--プラットフォームコールチュートリアル

7865 ワード

プラットフォーム呼び出しサービス(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プロパティを使用して、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);

    また、委託インスタンスの生存期間が非管理コードの生存期間をカバーしていることを確認してください.それ以外の場合、委託はゴミ回収後に使用できなくなります.