前回:COM講座第1回 C++からC#のクラスを呼び出したいと思います C#側で使える.Netの膨大なライブラリと簡単に作成できるGUIモジュールを C++側で使う場合等に有効な手段だと思います 開発環境は Visual C++ 2010 Express Visual C# 2010 Express です MFCやATLは使いません (VC++の有償版でないと使えないうえに、利用すると中身が不透明になりがちなので・・・) サンプル: VC#側 &ref(Component.zip); VC++側 &ref(ComTest.zip); 手順としては 1.C#のクラスを、COMコンポーネント化します。 2.COMコンポーネントを登録します 3.C++側からCOMコンポーネントをインスタンス化し、クラスのメソッドを呼び出します まず、管理者権限でVC#を起動します &ref(uac.jpg); クラスライブラリでプロジェクトを作成します &ref(vcsproj.jpg); 以下のコードに変更します using System; using System.Runtime.InteropServices; namespace Component { public interface IVector3 { void SetVector3(float x, float y, float z); void GetVector3(out float x, out float y, out float z); } [ClassInterface(ClassInterfaceType.None)] [ProgId("Component.Vector3")] public class Vector3 : IVector3 { public float x, y, z; public Vector3() { } public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public void SetVector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public void GetVector3(out float x, out float y, out float z) { x = this.x; y = this.y; z = this.z; } } public interface ITestCom { int Add(int a, int b); Vector3 AddVec(Vector3 a, Vector3 b); } [ClassInterface(ClassInterfaceType.None)] [ProgId("Component.TestCom")] public class TestCom : ITestCom { public int Add(int a, int b) { return a + b; } public Vector3 AddVec(Vector3 a, Vector3 b) { return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z); } } } System.Runtime.InteropServices名前空間を省略します 参照設定は不要です 公開するクラスはインタフェースを継承し [ClassInterface(ClassInterfaceType.None)] を先頭に付けます さらにProgIDの指定をします [ProgId("Component.TestCom")] これは実はしなくても自動的に割り振られて C++側からは呼び出しのときには使わないのですが 第4回で説明するWSHからの呼び出しの時に使います プロジェクトのプロパティを開きます まず、アセンブリ情報を開きます &ref(vcsprop.jpg); アセンブリをCOM参照可にチェックを入れて、OKを押して閉じます &ref(asm.jpg); 次にビルドのタブ欄の下にある COM相互運用機能の登録の欄にチェックをいれます &ref(comreg.jpg); ビルドをしてください。 ビルド時につぎのようなエラーが出た場合は管理者権限でVC#を起動してないので 管理者権限で起動してからビルドしてください &ref(error.jpg); ビルドするとCOMコンポーネントが登録され DLLとタイプライブラリファイル(拡張子.tlb)がReleaseフォルダ以下に作成されます &ref(tlb.jpg); ------------------ ここからはVC++でのCOMコンポーネントの利用になります まず、VC++を起動し、コンソールアプリケーションで適当なプロジェクトを作成します (COMコンポーネントを利用するVC++側は管理者権限で起動する必要はありません) タイプライブラリファイルが必要になるのでこれをプロジェクトフォルダにコピーします &ref(copytlb.jpg); main.cppに以下のコードを書きます #include <tchar.h> #include <Windows.h> #include <Objbase.h> // インポート #import "Component.tlb" no_namespace named_guids raw_interfaces_only // メイン処理 int main(int argc, char* argv[]) { IVector3 *a,*b,*c; ITestCom* pTestCom; // COMインターフェイスポインタ // COMの初期化 ::CoInitialize(NULL); // インスタンスの作成(CLSIDとIIDを指定して、インスタンスを取得) CoCreateInstance((REFCLSID) CLSID_TestCom, 0, CLSCTX_ALL, (REFIID) IID_ITestCom, (LPVOID*)&pTestCom); // インスタンスの作成(CLSIDとIIDを指定して、インスタンスを取得) CoCreateInstance((REFCLSID) CLSID_Vector3, 0, CLSCTX_ALL, (REFIID) IID_IVector3, (LPVOID*)&a); // インスタンスの作成(CLSIDとIIDを指定して、インスタンスを取得) CoCreateInstance((REFCLSID) CLSID_Vector3, 0, CLSCTX_ALL, (REFIID) IID_IVector3, (LPVOID*)&b); // インスタンスの作成(CLSIDとIIDを指定して、インスタンスを取得) CoCreateInstance((REFCLSID) CLSID_Vector3, 0, CLSCTX_ALL, (REFIID) IID_IVector3, (LPVOID*)&c); long out; pTestCom->Add(10,15,&out); printf("%d\n",out); a->SetVector3(1.0f,2.0f,3.0f); b->SetVector3(3.0f,4.0f,5.0f); pTestCom->AddVec(a,b,&c); float x,y,z; c->GetVector3(&x,&y,&z); printf("%f,%f,%f\n",x,y,z); c->Release(); b->Release(); a->Release(); pTestCom->Release(); // COMの終了処理 ::CoUninitialize(); return 0; } まず、タイプライブラリをインポートします // インポート #import "Component.tlb" no_namespace named_guids raw_interfaces_only COMを使用するにはCoInitialize関数で準備して 使い終わったらCoUnInitialize関数で終了処理をします // COMの初期化 CoInitialize(NULL); // COMの終了処理 CoUninitialize(); インスタンスを生成するにはCoCreateInstance関数を使います クラスIDとインタフェースID CLSID_** (**はクラス名) IID_** (**はインタフェース名) を指定することでそのクラスのインスタンスを生成でき、 インタフェースを中継して、そのクラスのメソッドを呼ぶことができます インスタンスを使い終わったらRelease関数でメモリを破棄しなければなりません 実行結果は次のようになります &ref(result.jpg); ちゃんとCOMコンポーネント化したC#のクラスがC++側から呼べています #vote(合体はロマン[11],ktkr[1],Windowsでしか動かないのはMicrosoftの仕様です[3]) |合体はロマン|11| |ktkr|1| |Windowsでしか動かないのはMicrosoftの仕様です|3|