「ライブラリの基礎 - C++DLL」の版間の差分
編集の要約なし |
|||
73行目: | 73行目: | ||
|} | |} | ||
</center> | </center> | ||
<br><br> | |||
== サンプルコード == | |||
C++ DLLの作成方法は[[http://raspberrypi/mediawiki/index.php/DLLを作成する(MFC)#DLLの作成方法(defファイルを作成する)|コチラを参照]]する。<br> | |||
念のため、下記にもC++ DLLを記述する。<br> | |||
<source lang="c++"> | |||
SampleDLL.h | |||
double __stdcall SampleFunc01(int a); | |||
</source> | |||
<source lang="c++"> | |||
SampleDll.cpp | |||
double __stdcall SampleFunc01(int a) | |||
{ | |||
printf(--<SampleDll:SampleFunc01>--\r\n"); | |||
printf("a = %d\r\n", a); | |||
printf("-------------------------\r\n"); | |||
return 3.14; | |||
} | |||
</source> | |||
<source lang="c++"> | |||
SampleDll.def // モジュール定義ファイル | |||
LIBRARY SampleDll | |||
EXPORTS | |||
; 公開する関数名をリストアップ | |||
SampleFunc01 @1 | |||
</source> | |||
<br><br> | |||
次に、 | |||
<source lang="c#"> | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace SampleEXE | |||
{ | |||
class Program | |||
{ | |||
/// <summary> | |||
/// 最も基本的な関数のインポート例 | |||
/// </summary> | |||
/// <param name="a">4 バイト符号付き整数を指定します。</param> | |||
/// <returns>倍精度浮動小数を返します。</returns> | |||
[DllImport("SampleDLL.dll")] | |||
private static extern double SampleFunc01(int a); | |||
static void Main(string[] args) | |||
{ | |||
var dRet= Sample01(1); | |||
Console.WriteLine(dRet); | |||
Console.WriteLine(); | |||
Console.ReadKey(); | |||
} | |||
} | |||
} | |||
</source> | |||
<br> | |||
DllImport属性には、DLLファイルのパスを指定する以外に次のような引数を与えることもできる。<br> | |||
これ他にも細かい設定をするための引数が用意されているので、DllImportAttributeクラスで検索すること。<br> | |||
{| class="wikitable" | |||
|- | |||
! 名称 !! 説明 | |||
|- | |||
| CallingConvention || エントリポイントの呼び出し規約を明示的に指定できる。<br>指定なしの場合は__stdcallとなる。 | |||
|- | |||
| CharSet || 文字列パラメータをメソッドにマーシャリングし、名前マングルを制御する方法を指定する。<br>文字コードの相互変換する時に指定する。 | |||
|- | |||
| EntryPoint || 呼び出すDLLエントリポイントの名前または序数を指定する。<br>DLLの関数名とC#上で使用する関数名を異なる名前にする時に指定する。 | |||
|} | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C_Sharp]] | [[カテゴリ:C_Sharp]] |
2019年11月2日 (土) 05:24時点における版
概要
C# EXEからC++ DLLへ様々なデータ型の変数を渡したいときがある。
例えば、C++ DLLから次のような関数がエクスポートされているとする。
void WINAPI ConvertToShort(char *pstr, short *pret);
上記の関数において、C# EXEから使用するときは、char*型は文字列なのでstring型を渡す。
short*型はIntPtr型を渡す。(IntPtr型は汎用ポインタを表す型であり、void*型とほぼ同義)
但し、C#は厳しい型付け言語なので、曖昧さを解決するために変換メソッドを経由する必要がある。
具体的には、IntPtr型の変数にMarshal.AllocHGlobal関数で必要なサイズのメモリを確保して、それをC++ DLLに渡した後、
Marshal.ReadInt16関数(型によって異なる)等で変換した後、確保したメモリをMarshal.FreeHGlobal関数で解放するというプロセスを経る必要がある。
サンプルコード
下記に、C++ DLLを呼ぶC# EXEのソースコードを記載する。
// DllImportを使用するために必要
using System.Runtime.InteropServices;
[DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")]
extern static void _ConvertToShort(string pstr, IntPtr pret); //呼び出し元の名前変えちゃう
public static short ConvertToShort(string str)
{
IntPtr buffer = new IntPtr();
buffer = Marshal.AllocHGlobal(2); // 2バイトのメモリ確保
_ConvertToShort(str, buffer); // C++/DLLの関数を呼ぶ
short sval = Marshal.ReadInt16(buffer); // 変換
Marshal.FreeHGlobal(buffer); // メモリ開放
return sval;
}
IntPtr型の変数は様々なものが入るので、例えば、構造体を取得することも可能だが、C# EXEで構造体を定義しなければいけない。
WindowsのDLL(Win32 API)と.NET Frameworkでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。
尚、BOOL型の実体はLONG型と同じなので、boolの代わりにintを指定することも可能である。
APIの型名 (括弧内は対応するC言語の型) |
対応するC#の型 (括弧内は.NET Frameworkでの型名) |
備考 |
---|---|---|
HANDLE (void *) | System.IntPtr System.UIntPtr |
x86は4バイト x64は8バイト |
BYTE (unsigned char) | byte (System.Byte) | |
SHORT (short) | short (System.Int16) | |
WORD (unsigned short) | ushort (System.UInt16) | |
INT (int) LONG (long) |
int (System.Int32) | |
UINT (unsigned int) DWORD, ULONG (unsigned long) |
uint (System.UInt32) | |
BOOL (long) | bool (System.Boolean) | |
CHAR (char) | 文字を渡すとき char (System.Char) 文字を受け取るとき StringBuilder |
|
WCHAR(wchar_t) | 文字を渡すとき char (System.Char) 文字を受け取るとき StringBuilder |
|
LPSTR (char *, char[]) LPWSTR (wchar_t *, wchar_t[]) |
文字を渡すとき string (System.String) 文字を受け取るとき System.Text.StringBuilder |
|
LPCSTR (const char *, const char[]) LPCWSTR (const wchar_t *, const wchar_t[]) |
文字を渡すとき string (System.String) 文字を受け取るとき System.Text.StringBuilder |
|
FLOAT (float) | float (System.Single) | |
DOUBLE (double) | double (System.Double) |
サンプルコード
C++ DLLの作成方法は[[1]]する。
念のため、下記にもC++ DLLを記述する。
SampleDLL.h
double __stdcall SampleFunc01(int a);
SampleDll.cpp
double __stdcall SampleFunc01(int a)
{
printf(--<SampleDll:SampleFunc01>--\r\n");
printf("a = %d\r\n", a);
printf("-------------------------\r\n");
return 3.14;
}
SampleDll.def // モジュール定義ファイル
LIBRARY SampleDll
EXPORTS
; 公開する関数名をリストアップ
SampleFunc01 @1
次に、
using System;
using System.Runtime.InteropServices;
namespace SampleEXE
{
class Program
{
/// <summary>
/// 最も基本的な関数のインポート例
/// </summary>
/// <param name="a">4 バイト符号付き整数を指定します。</param>
/// <returns>倍精度浮動小数を返します。</returns>
[DllImport("SampleDLL.dll")]
private static extern double SampleFunc01(int a);
static void Main(string[] args)
{
var dRet= Sample01(1);
Console.WriteLine(dRet);
Console.WriteLine();
Console.ReadKey();
}
}
}
DllImport属性には、DLLファイルのパスを指定する以外に次のような引数を与えることもできる。
これ他にも細かい設定をするための引数が用意されているので、DllImportAttributeクラスで検索すること。
名称 | 説明 |
---|---|
CallingConvention | エントリポイントの呼び出し規約を明示的に指定できる。 指定なしの場合は__stdcallとなる。 |
CharSet | 文字列パラメータをメソッドにマーシャリングし、名前マングルを制御する方法を指定する。 文字コードの相互変換する時に指定する。 |
EntryPoint | 呼び出すDLLエントリポイントの名前または序数を指定する。 DLLの関数名とC#上で使用する関数名を異なる名前にする時に指定する。 |