カクタスソフトウェア
カクタスソフトウェア
サウンド MIDI マルチメディア アプリケーション

プラットフォーム呼び出し

このセクションではプラットフォーム呼び出しの手順について解説します。

マネージ環境からネイティブDLL内のAPIを呼び出す機能をプラットフォーム呼び出しといいます。英語では「PInvoke」と言います。C#コンパイラーはネイティブDLL内にどのようなAPIがあってどのような仕様なのか知りません。そこでプラットフォーム呼び出しを行うためにAPIの仕様を公開する必要があります。

DLLにあるAPIを参照するためにDllImportアトリビュートを用います。以下に一般的な書式を示します。

[DllImport(必要なディレクティブをカンマで区切りながら羅列)]
public static extern 戻り値 関数名(引数列をカンマで区切りながら羅列);

ディレクティブとして必要な情報は以下の通りです。

関数本体の定義に、戻り値・関数名・引数列を指定します。

ファイル名と関数名前の指定

以下の例はmidiOutGetNumDevsというMIDI出力デバイスの数を求める関数です。このAPIはwinmm.dllというDLLの中にあります。

UINT midiOutGetNumDevs(VOID);
[DllImport("winmm.dll")]
public static extern int midiOutGetNumDevs();

上の行でDLLのファイル名を指定します。下の行で関数名、引数、戻り値を定義します。

関数のエントリーポイント

ソースコードに記載される関数名と実際にコールされる関数名が異なる場合があります。C/C++のヘッダーファイルで使用する文字コードによって呼び出す関数を振り分けているケースがそれに当たります。以下に例を示します。

WINMMAPI HMMIO WINAPI mmioOpenA(
    LPSTR           pszFileName,
    LPMMIOINFO      pmmioinfo,
    DWORD           fdwOpen);
WINMMAPI HMMIO WINAPI mmioOpenW(
    LPWSTR          pszFileName,
    LPMMIOINFO      pmmioinfo,
    DWORD           fdwOpen);

#ifdef UNICODE
#define mmioOpen  mmioOpenW
#else
#define mmioOpen  mmioOpenA
#endif // !UNICODE

ソースコードには関数名をmmioOpenとしてコーディングしますが、使用する文字コードによって、Ansiの場合mmioOpenAがUnicodeの場合mmioOpenWがそれぞれコールされます。DllImportではどの文字コードを使うのかを決め、それに合わせた定義を行う必要があります。

文字コードにAnsiを使う場合

EntryPointディレクティブにAnsiを用いる関数mmioOpenAを設定し、ファイル名を指定するszFileNameにマーシャラーへの指示[MarshalAs(UnmanagedType.LPStr)]を設定します。

[DllImport("winmm.dll", EntryPoint="mmioOpenA")]
public static extern IntPtr mmioOpen(
    [MarshalAs(UnmanagedType.LPStr)] string     szFileName,
    ref MmIoInfo                                lpmmioinfo,
    int                                         dwOpenFlags);

文字コードにUnicodeを使う場合

EntryPointディレクティブにUnicodeを用いる関数mmioOpenWを設定し、ファイル名を指定するszFileNameにマーシャラーへの指示[MarshalAs(UnmanagedType.LPWStr)]を設定します。

[DllImport("winmm.dll", EntryPoint="mmioOpenW")]
public static extern IntPtr mmioOpen(
    [MarshalAs(UnmanagedType.LPWStr)] string    szFileName,
    ref MmIoInfo                                lpmmioinfo,
    int                                         dwOpenFlags);
プラットフォーム呼び出しでAnsiがデフォルトの設定になっていれば、string変数への[MarshalAs(UnmanagedType.LPStr)]指定を省略することができます。

引数の例

C/C++のヘッダーファイルにある関数の定義を、セクション「マーシャリング」「変数の型」などで解説した方法を用いながらC#へ移植します。

ブリッタブル型の変数はC/C++に対応するC#の型へ変更するだけです。ただしいくつかのケースで注意しなければならない事例があります。

C/C++の型のlongはC#ではintに対応します。C#のlongはC/C++ではlonglongつまり64ビット精度のintになります。そのためC/C++のソースコードを単にC#側にペーストすると不具合が発生する恐れがあります。

C/C++ではバッファをアロケートするとき符号付きのchar配列をよく用います。しかしC#では習慣として符号なしのbyte配列を用いることが多いようです。

C/C++では引数の型にWORDやDWORDなど符号なしの型が多用されています。これは負にならない変数はできるだけunsigned属性を用いるという習慣によると思われます。しかしC#ではunsigned型でなければならない理由がない限り、一般的に符号付きの型を用いる習慣のようです。

HRESULTやMMRESULTなどtypedefで定義された戻り値は、intで受けます。

DECLARE_HANDLEマクロで定義されたハンドル型は、IntPtrで受けます。

文字列はマーシャラーへの指示を付加してstringで受けます。

以下にいくつか例を示します。C/C++とC#の型の違いに注目して下さい。

mmioRead

LONG mmioRead(
    HMMIO   hmmio,
    HPSTR   pch,
    LONG    cch);
[DllImport("winmm.dll")]
public static extern int mmioRead(
    IntPtr                                      hmmio,
    [MarshalAs(UnmanagedType.LPArray)] byte[]   pch,
    int                                         cch);

mmioStringToFOURCC

FOURCC mmioStringToFOURCC(
    LPCSTR      sz,
    UINT        wFlags);
[DllImport("winmm.dll", EntryPoint="mmioStringToFOURCCA")]
public static extern FourCC mmioStringToFOURCC(
    [MarshalAs(UnmanagedType.LPStr)] string     sz,
    int                                         uFlags);

timeSetEvent

MMRESULT timeSetEvent(
    UINT                uDelay,
    UINT                uResolution,
    LPTIMECALLBACK      lpTimeProc,
    DWORD               dwUser,
    UINT                fuEvent);
[DllImport("winmm.dll")]
public static extern int timeSetEvent(
    int                 uDelay,
    int                 uResolution,
    TimeCallback        lpTimeProc,
    int                 dwUser,
    TimeEvent           fuEvent);

ドキュメントの先頭へ

カクタスソフトウェア 技術協力 資料室 資料室の広場 SourceForge.jp お問い合わせ