C#でCOMを作る(とりあえず動くところまで)

★ご注意


CLR 2.0、CLR 1.xが同じプロセス空間にロードできない問題と互換性の問題があるため、マネージコードでCOMを作ることはお薦めしません。

 

0.はじめに

@ITでサンプルよこせーというようなニュアンスの発言を受け取ったのでのんびりと書いていきましょう。
環境はVisual Studio 2008 Professionalです。

 

1.プロジェクトを作る

「クラスライブラリ」を選択して、プロジェクトを作ります。
.NET Frameworkのバージョンの選択はお好みでどうぞ。

 

2.インターフェースを作る

プロジェクトメニュー等から新しい項目を追加でインターフェースを追加します。

 

3.インターフェースにCOMとして公開するために必要な属性をつける

ComVisible属性でCOMに公開するかどうかを設定でき、trueだと公開になります。
ClassInterface属性で遅延バインディングを認めるという意味でAutoDispatchにしています。必要に応じて変えて下さい。
最後のGuid属性でインターフェースに対してGUIDを設定しますが、このGUIDはツールでインターフェースやクラス毎に必ず違うものを生成して下さい。
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)]
[System.Runtime.InteropServices.Guid("")]
public interface ITestClass
{
}
Visual Studio 2005/2008 Professionalであれば、ツールメニューに「GUIDの作成」があるので、
このツールで「4.Registry Format」で「Copy」ボタンを押して、クリップボードにコピーした文字列をGuid属性のところに入れます。
この際に、{ }の部分は外して下さい。
連続して生成する場合は、「New GUID」ボタンを押して下さい。
例:{D62189BE-C74D-46f5-8223-E0B8EC508BE5}が出てきたら、Guid属性には”D62189BE-C74D-46f5-8223-E0B8EC508BE5”を設定する。
 

 

4.インターフェースにメソッド・プロパティを追加

ひとまず、こんなところで、作ってみました。

[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)]
[System.Runtime.InteropServices.Guid("")]
interface ITestClass
{
    void HelloWorld();
    int Plus(int left, int right);
    int Number
    {
        get;
        set;
    }
}

 

5.インターフェースを実装するクラスを追加

毎度おなじみのプロジェクトの新しい項目の追加からクラスを作成します。

 

作成されたクラスで、インターフェースを実装するというように書きます。

public class Test : ITestClass
{
}
さらにクラス側にも色々と属性をつけておきます。
[System.Runtime.InteropServices.ComVisible(true)]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDispatch)]
[System.Runtime.InteropServices.Guid("")]
public class Test : ITestClass

 

ここでITestClassと書いた部分にカーソルを合わせると下線が出てくるのでクリックして、メニューからインターフェースを実装しますを選択する。

そうすると、インターフェースを実装するスケルトンコードがずらずらと出てくるので中身を埋めていきます。
(プロパティのコードは何となく書いていますが、C#3.0あたりからはget;set;といった感じに省略できるはず)

public void HelloWorld()
{
    Console.WriteLine("Hello! World!");
}

public int Plus(int left, int right)
{
    return left + right;
}

private int number;

public int Number
{
    get { return this.number; }
    set { this.number = value; }
}

 

6.レジストリ登録するためにプロジェクトの設定を変えておく

順番が前後しましたが、プロジェクトの設定を変えておかないと、COMとして利用できる形に登録されません。
プロジェクトメニューから ~ のプロパティを選択します。
もしくはソリューションエクスプローラでプロジェクトを右クリックしてプロパティを選択しても構いません。

表示された画面から「ビルド」タブを選択して、下の方にある「COM相互運用機能の登録」にチェックを入れておきます。

この設定をしておくことで、ビルドしたときに自動的にレジストリに登録されるようになります。
(管理者権限が必要ですので、Vistaの場合はVisual Studioを管理者権限で起動する必要があるかもしれません。)

7.ビルドする

構文エラーとか特に問題がなければ、さっくりとビルドが成功するはずです。

 

8.使ってみる

Rubyは普段触りません。なので、検索で引っかかった情報を元に適当に組みました。

require 'win32ole'
instance = WIN32OLE.new('ComTestLibrary.Test')

instance.HelloWorld()

i = instance.Plus(1, 2)
print(i, "\n")

instance.Number = i
i = 25
print(instance.Number, "\n")
i += instance.Number
print(i, "\n")
instance.Number = 9
print(instance.Number, "\n")

このファイルをruby test.rbにような形で食わせると、次のように出力されます。

Hello! World!
3
3
28
9

ちゃんとCOMが呼び出せている様子です。
とりあえず、導入部分としては目的を達成できたかと思います。

 

9.留意点

・今回WIN32OLEモジュールを使っています。Dispatchなインターフェースがないとこの方法では使えないので属性の設定を無闇に変更しないようにしましょう。
・「とりあえず動いた」の状態ですので、これをスケルトンコードにしないようにしましょう。
・ビルドした環境以外で今回のDLLをレジストリに登録するには regasm コマンドを使用します。
・属性の意味はMSDNを見て調べましょう。

 

10.サンプルソース

http://cid-9e1932af4be9e15d.skydrive.live.com/embedrowdetail.aspx/.Public/ComTestLibrary.zip

※GUIDはそのまま使用しないようにしましょう。