C++のクラスを、C#で利用します
「num個のint配列srcから最大値のインデックスと最大値を探すメソッド」という機能のメソッドを含む、「FuncClass」というC++のクラスを、C#から利用します。
「マネージドコードのクラスが、そのメンバにアンマネージドコードのクラスを持つことは許されていない」というルールがあるので、それをどう解決するかがポイントとなります。
1. 準備
C#、C++/CLR(CLI)、C++の各プロジェクトは、(03)C#から、C++の関数の実行(関数)と同様に作ります。
C#のクラスを「test03」などとした場合は、「test02」の部分を「test03」としてください。
2. C++のコード
- NativeFunc.hの内容を、以下に置き換えます。
#ifdef NATIVEFUNC_EXPORTS
#define NATIVEFUNC_API __declspec(dllexport)
#else
#define NATIVEFUNC_API __declspec(dllimport)
#endif
namespace Native
{
class NATIVEFUNC_API FuncClass
{
public:
// num個のint配列srcから、最大値とそのインデックスを探す関数です
void Max(int* src, int num, int* mx, int* mxIndex);
};
}
- NativeFunc.cppの内容を、以下に置き換えます。
#include "stdafx.h"
#include "NativeFunc.h"
void Native::FuncClass::Max(int* src, int num, int* mx, int* mxIndex)
{
*mx = src[0];
*mxIndex = 0;
for (int i = 0; i < num; i++) {
if (src[i] > *mx) {
*mxIndex = i;
*mx = src[i];
}
}
}
- ポイントは特にありません。
3. C++/CLR(CLI)ラッパークラスのコード
- WrapperClass.hの内容を、以下に置き換えます。
#pragma once
#include "..\NativeFunc\NativeFunc.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;
namespace Wrapper
{
public ref class WrapperClass
{
private:
Native::FuncClass* _na0; //★ここがポイント
public:
WrapperClass();
~WrapperClass();
!WrapperClass();
void Max(array<int>^ src, int num, int% mx, int% mxIndex);
}; //end class
} //end namespace
- WrapperClass.cppの内容を、以下に置き換えます。
#include "stdafx.h"
#include "WrapperClass.h"
//★ここがポイント
Wrapper::WrapperClass::WrapperClass()
{
_na0 = new Native::FuncClass();
}
//★ここがポイント
Wrapper::WrapperClass::~WrapperClass()
{
delete _na0; _na0 = nullptr;
}
//★ここがポイント
Wrapper::WrapperClass::!WrapperClass()
{
this->~WrapperClass();
}
void Wrapper::WrapperClass::Max(array<int>^ src, int num, int% mx, int% mxIndex)
{
// 実行中、ガベージコレクションされないように、pin_ptrを使って固定する
pin_ptr<int> p_src = &src[0];
pin_ptr<int> p_mx = &mx;
pin_ptr<int> p_mxIndex = &mxIndex;
// 自作関数実行
_na0->Max(p_src, num, p_mx, p_mxIndex);
// 固定解除
p_src = nullptr;
p_mx = nullptr;
p_mxIndex = nullptr;
}
- ポイント
Native::FuncClass* _na0
で、FuncClassクラスを呼び出します- 「マネージドコードのクラスが、そのメンバにアンマネージドコードのクラスを持つことは許されていない」というルールは、ポインタを利用して回避します
- コンストラクタ、デストラクタ、ファイナライザを定義します
- コンストラクタで、FuncClassクラスのインスタンスを作成します
- デストラクタやファイナライザで、インスタンスを破棄します
- ちなみに、.NETではデストラクタは必ず呼び出されるとは限りません
4. C#のコード
- Form1.csの内容を、以下に置き換えます。
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Wrapper;
namespace test03
{
public partial class Form1 : Form
{
WrapperClass wr = new WrapperClass(); //WrapperClassのインスタンスを作成
public Form1()
{
InitializeComponent();
int[] src = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2 };
int mx = 0;
int mxIndex = 0;
int num = 10;
MessageBox.Show("int[] a = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2 }");
wr.Max(src, num, ref mx, ref mxIndex); //処理実行
MessageBox.Show("Max = " + mx.ToString() + ", MaxIndex=" + mxIndex.ToString());
Environment.Exit(0);
}
}
}
結果
結果は、(03)C#から、C++の関数の実行(関数)と同じですので省略します。
上の手順で作成されたソリューションは、ここににあります(サンプルプログラムにおける警告について)。