C++ lib ファイルを使用する C# でゲーム エディターを開発しています。C# の C++ クラスに RTTI が必要です。C#でC++クラスのRTTIを取得することは可能ですか? はいの場合、どのように?
2 に答える
ネイティブ C++ 型またはコードを .NET プラットフォームに直接公開することはできません。
ただし、.NET (C#、VB.Net など) の「ネイティブ」C および C++ コードと対話する方法は 3 つあります。
- COM
- P/呼び出す
- CLI/C++
COM は、おそらく .NET 側から使用するのが最も簡単です。COM オブジェクトを参照として .NET プロジェクトに追加するだけで、インターフェイスとクラスの操作を開始できます。.NET での COM との対話の詳細については、次のような本を読んでください。
http://www.amazon.com/NET-COM-Complete-Interoperability-Guide/dp/067232170X
これにはもちろん、ゲーム エンジン オブジェクトを COM オブジェクトとして公開する必要があります。これは自明ではありません。
次に使いやすいのは P/Invoke です。ゲーム コードが C 呼び出し規則を使用して標準の Windows DLL にパッケージ化されている場合、P/Invoke を使用してその DLL 内の関数にアクセスできます。例えば:
public static class UserDll
{
[DllImport("user32.dll")]
private static extern bool FlashWindow(IntPtr hwnd, bool bInvert);
public static void FlashWindow(System.Windows.Forms.Form window)
{
FlashWindow(window.Handle, false);
}
}
P/Invoke を使用すると、多くのことができます。デリゲートを使用して C/C++ コードを C# にコールバックすることもできます。
私は過去に、P/Invoke を使用して DLL で公開された関数を呼び出すゲーム エンジン ツールを作成しました。ネイティブ リソースの管理には注意が必要です。ここでは、IDisposable インターフェイスとクラス ファイナライザーが友達になります。例えば:
public class Player : IDisposable
{
private IntPtr _thePlayer;
public Player()
{
_thePlayer = CreatePlayer();
}
~Player()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
// dispose of managed objects (ie, not native resources only)
}
if (_thePlayer != IntPtr.Empty)
{
DestroyPlayer(_thePlayer);
_thePlayer = IntPtr.Empty;
}
}
[DllImport("gameengine.dll")]
private static extern IntPtr CreatePlayer();
[DllImport("gameengine.dll")]
private static extern void DestroyPlayer(IntPtr player);
}
P/Invoke の使用には欠点があります。まず、ネイティブ呼び出しにマーシャリングのオーバーヘッドが大幅に追加される可能性があります (速度を上げる方法はありますが)。また、gameengine.dll に C API が必要です。エンジンが C++ クラスの場合、C++ クラスに C API を提供する必要があります。これにより、多くの作業が追加される可能性があります (またはコード ジェネレーターが必要になります)。
マネージド オブジェクト/データをネイティブ コードとの間でマーシャリングする際の厄介な詳細については、これ以上詳しく説明するつもりはありません。それが可能であり、MSDN があなたの味方であることを知っておいてください。
ネイティブ C++ コードを .NET に公開する 3 番目のおそらく最良の方法は、CLI/C++ 混合モード アセンブリを使用することです。CLI/C++ を使用すると、ネイティブ コードとマネージ コードを 1 つのアセンブリにかなりシームレスに混在させることができます。
CLI/C++ の構文はおかしなものですが、C++ プログラマーであれば、順応するのは難しくありません。例は次のようになります。
using namespace System;
// CLI/C++ "managed" interface
interface class IDog
{
void Bark();
};
#pragma managed(push off)
// Native C++
class Pet
{
public:
Pet() {}
~Pet() {}
const char* GetNativeTypeName()
{
return typeid(Pet).name();
}
};
#pragma managed(pop)
// CLI/C++ "managed" class
ref class Dog : IDog
{
private:
Pet* _nativePet;
public:
Dog()
: _nativePet(new Pet())
{}
~Dog()
{
delete _nativePet;
_nativePet = 0;
}
void Bark()
{
// Managed code talking to native code, cats & dogs living together, oh my!
Console::WriteLine("Bow wow wow!");
Console::WriteLine(new System::String(_nativePet->GetNativeTypeName()));
}
};
void _tmain()
{
Dog^ d = gcnew Dog();
d->Bark();
}
私の推奨事項 (あなたがやろうとしていることを正確に実行した結果) は、中程度に複雑なもの以上のものについては、ゲーム エンジンに CLI/C++ API を試して提供することが最善の解決策であるということです。CLI/C++ について知っておくべきことはすべて MSDN から学びましたが、分厚い本が好きならこの本が良いと聞きました。
http://www.amazon.com/Expert-Visual-CLI-Programmers-Experts/dp/1590597567
この記事では、そのプロセスについて説明します。
ランタイム型識別 (RTTI) を使用すると、プログラムの実行中にオブジェクトの型を判別できます。C# には、実行時の型識別をサポートする 3 つのキーワード (is、as、および typeof) が含まれています。
is
オブジェクトが必要なタイプであるかどうかを判断するために使用します。
if (myVariable is string)
{
// do stuff
}
as
あるオブジェクトから別のオブジェクトに変換するために使用します。変換が存在しない場合は、null
が返されます。
string myString = myVariable as string;
if (myString != null)
{
// do stuff
}
typeof
タイプに関する情報を取得するために使用します。
式の実行時の型を取得するには、.NET Framework メソッドGetTypeを使用できます。