ネイティブコードのHogeCクラスを含むライブラリHogeC.libを、C#で利用するためのCLIラッパライブラリHogeSharp.dllの作り方をメモします。
プロジェクトの作成
[ファイル]>[新しいプロジェクト]>[テンプレート]>[Visual C++]>[CLR]>[クラスライブラリ]で新規プロジェクトを作成します。ここでは HogeSharp という名前にします。
プロジェクトの設定
- ネイティブライブラリのヘッダ HogeC.h を任意のフォルダに置きます。ここではソースフォルダの下のIncフォルダとします。
- ネイティブライブラリ HogeC.lib を任意のフォルダに置きます。ここではソースフォルダの下のLibフォルダとします。
- [プロジェクト]>[プロパティ]>[構成プロパティ]>[VC++ディレクトリ]>[インクルードディレクトリ]に、Incを追加します。(;で区切ります)
- [プロジェクト]>[プロパティ]>[構成プロパティ]>[VC++ディレクトリ]>[ライブラリディレクトリ]に、Libを追加します。(;で区切ります)
- [プロジェクト]>[プロパティ]>[構成プロパティ]>[リンカ]>[入力]>[追加の依存ファイル]に、HogeC.libを追加します。(;で区切ります)
- その他、HogeC.libがさらに別のライブラリに依存している場合、それらも追加します。(たとえば、WinSock APIを使用していて ws2_32.libに依存しているなど)
ソースファイル一覧
resource.h
特に修正不要。
AssemblyInfo.cpp
名称、著作権表示、バージョン情報などの属性を適当に記入するだけです。
Stdafx.h, Stdafx.cpp
使用するインクルードファイルやライブラリをStdafx.hに列挙します。(べつに書かなくても良い?)
#include "HogeC.h" #pragma comment(lib,"HogeC.lib")
Stdafx.cppはただのダミーなので修正不要です。
HogeSharp.h, HogeSharp.cpp
おもにコードを書くべきはここです。
コードの記述
ラッパクラスの宣言
HogeSharp.h に Class1 というクラスが勝手に作られるので、これを Hoge に改名します。このクラスが、public ref classであることに注意してください。C#から使用するクラスは、public ref classでないといけません。ちなみにC#で構造体として使うのであれば、pubilc value classとします。また、クラスHogeは、名前空間HogeSharpに属します。
Hogeクラスには、publicなメンバとしてコンストラクタ、デストラクタ、ファイナライザ、および必要なメソッドを宣言します。ファイナライザというのはCLI特有のものです。C#のデストラクタ~Hoge()に対応するのが、CLIではファイナライザ!Hoge()であり、CLIのデストラクタ~Hoge()は、C#のHoge.Dispose()に対応します。
また、privateなメンバとして、ネイティブクラスHogeCへのポインタを宣言します。C#から呼び出すラッパクラスは、ネイティブクラス(アンマネージドクラス)の実体をメンバに持てないことに注意してください。
// CLIのラッパクラス public ref class Hoge { public: Hoge(); // コンストラクタ ~Hoge(); // デストラクタ !Hoge(); // ファイナライザ // その他、必要なメソッド int Piyo(); void Huga(int Foo); private: HobeC* hogeC; // ネイティブクラスへのポインタ };
ラッパクラスの実装
HogeクラスをHogeSharp.cppに実装します。
※ デストラクタではマネージドリソースを開放してからファイナライザを呼び出し、ファイナライザでアンマネージドリソースを解放するべきだそうです。
- 各メソッドは、ネイディブクラスの対応するメソッドを呼び出します。
using namespace HogeSharp; // コンストラクタ Hoge::Hoge() { hogeC = new HogeC(); // ネイティブオブジェクト生成 } // デストラクタ Hoge::~Hoge() { this->!Hoge(); // ファイナライザを呼ぶ } // ファイナライザ Hoge::!Hoge() { delete hogeC; // ネイティブオブジェクト解放 } // ラッパ int Hoge::Piyo() { return hogeC->Piyo(); } void Hoge::Huga(int Foo) { hogeC->Huga(Foo); }
.NETライブラリの使用
ウィザードが吐くコードの中に、すでに using namespace System; があるので、System名前空間のクラスは利用できます。
using namespace System; String^ hoge;
それ以外の名前空間のクラス、例えばSystem.Drawing.Bitmapを使用したい場合は、下記のように書きます。
#using <system.drawing.dll> using namespace System::Drawing; Bitmap^ piyo;
型と参照
ハンドル
C#の参照型に相当する型の変数宣言には、^ をつけます。これをハンドルと呼びます。
Bitmap^ piyo;
この場合のオブジェクト生成(C#でのnewに相当)は、newではなくgcnewを用います。
String^ foo = gcnew String("FOO");
追跡参照
C#のrefに相当する引数宣言には、% をつけます。これを追跡参照とよびます。
// C# からは hoge.piyo(ref foo);のように呼ぶ void Hoge::piyo(int% foo)
また、C#のoutに相当する引数宣言は、下記のようにします。
// C# からは hoge.piyo(out foo);のように呼ぶ void Hoge::piyo([Runtime::InteropServices::Out]int% foo)
文字列の変換
C#のSystem.Stringと、C++のstd::string、Cのchar*の変換は下記のようにします。
#include <msclr/marshal_cppstd.h> using namespace msclr::interop; // C#のString => C++のstring, Cのchar* System::String^ s_cs = L"文字列"; std::string s_cpp = marshal_as<std::string>(s_cs); const char* s_c = s_cpp.c_str(); // C++のstring => C#のString std::string s_cpp = "文字列"; System::String^ s_cs = marshal_as<System::String^>(s_cpp); // Cのchar* => C#のString const char* s_c = "文字列"; System::String^ s_cs = marshal_as<System::String^>(s_c);
ビルド
ビルドするとHogeSharp.dllができます。