Excel から使うマネージDLL を作る。

 きょうは、マネージDLLを Excel から使う方法についてあれこれやってみます。ほぼ、自分のための備忘録です♪ やった時に書いておかないと、すぐに忘れちゃいますから~♪
 で、話がそれますが・・・自分は日頃、C# を使っています。が!Excel を使っている方にはVB.Net のほうがわかりやすいと思いますので、VS でなく、Excel-DNA と同じく CodePlex で公開されている SharpDevelop という IDE を使って実験していきたいと思います。SharpDevelop は、例えば C# で書いたコードや VB で書いたコードを、相互に変換するだけでなく、Boo/Ruby/Python といった言語に簡単に変換してくれるところが便利です。VS 互換ですので、VS で作成した既存のソリューションを開くこともできますし、SharpDeverop で作成したソリューションを VS で開くこともできます。動作も軽いです^^
なので、ここではC# で記述していきますが馴れた言語に変換して実験することができます。

 さっそく、クラスライブラリの、新規ソリューションを「MyClass」という名前で作成します。
(こんな感じ↓)

ソリューションが作成されて、コードを記述する準備が出来ました。
(こんな感じ↓)

さっそく、簡単なメソッドを記述してみます。
(こんな感じ↓)

using System;

namespace MyClass
{
	/// <summary>
	/// Description of MyClass.
	/// </summary>
	public class MyClass
	{
		public string Hello(string name)
		{
			return "Hello!" + name;
		}
	}
}

ビルドして、完成です(笑)
[MyClass]>[MyClass]>[bin]>[Debug] に、MyClass.dll が作成されました。

 さっそく使ってみたいのですが、マネージDLL はRegasm を使って登録する必要があります。コマンドプロンプトから登録/解除するのが面倒なので、DLL 登録用に reg.cmd (名前はなんでもいいのですが) というファイルを、作成した dll と同じフォルダに作り、下記のような処理を記述します(XPでないとダメ)。
(こんな感じ↓)

%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm /tlb MyClass.dll
pause

ついでに、登録解除用に unreg.cmd というファイルも作成しておきます。
(こんな感じ↓)

%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm /tlb /u MyClass.dll
pause

Regasm をフルパスで呼び出し、/tlb オプションを使って、タイプライブラリを生成・登録(解除)しているだけです(笑) (.Net4.0 の Regasm を利用するディレクトリ構成になっています。.Net2.0 を使うなら、v4.0.30319 の部分を、v2.0.50727 のように変更します。Regasm のコマンドオプションについては こちらに詳細があります。)
 reg.cmd をクリックすると、MyClass.dll のタイプライブラリが同じフォルダに生成され、登録されます。
(こんな感じ↓)

さっそく Excel を開き、VBE の「ツール」> 「参照設定」 を開いて見ると、MyClass がちゃんと表示されています。
(こんな感じ↓)

参照設定したら、オブジェクトブラウザを開いて、MyClass を見てみると・・・
(こんな感じ↓)

あれ?作成したはずの Hello メソッドが見当たりません・・・。どうしてかというと、COMオブジェクト はインターフェースを通してやりとりするので、インターフェースを実装しなければならないようです。(「COM オブジェクトとは」参照)

さっそく実装してみます♪
(こんな感じ↓)

using System;

namespace MyClass
{
    public interface IMyclass
    {
        string Hello(string name);
    }
	/// <summary>
	/// Description of MyClass.
	/// </summary>
	public class MyClass:IMyclass
	{
		public string Hello(string name)
		{
			return "Hello!" + name;
		}
	}
}


ビルドして登録すると、今度はHelloメソッドが見えています。
(こんな感じ↓)

でも、クラスのところに、「IMyClass」と「MyClass」とインターフェースもクラスも表示されていてちょっと変です(笑) 「MyClass」のほうは中身も空っぽですし・・・。
 MSDN の「COM への.NET Framework コンポーネントの公開」のなかの、「相互運用固有の属性の適用」を見ると、System.Runtime.InteropServices 名前空間にマネージコードの「デザイン時属性」を設定する機能の解説を参考に、属性を適用してみます。
(こんな感じ↓)

using System;
using System.Runtime.InteropServices;

namespace MyClass
{
    [ComVisible(true)]
    public interface IMyclass
    {
        string Hello(string name);
    }
	/// <summary>
	/// Description of MyClass.
	/// </summary>
    [ClassInterface(ClassInterfaceType.None)]
	public class MyClass:IMyclass
	{
		public string Hello(string name)
		{
			return "Hello!" + name;
		}
	}
}


今度は、ちゃんと見えています!
(こんな感じ↓)

さっそく、VBA から使ってみます。こんな、コードを書いてみました。
(こんな感じ↓)

Dim MyClassObject As New MyClass.MyClass

Sub Test()
    Debug.Print(MyClassObject.Hello("mab")
End Sub


ちゃんと、インテリセンスも効いてます^^
(こんな感じ↓)

さっそく実行してみると・・・? あれ?・・・実行できませんね(笑)
(こんな感じ↓)

 クラスオブジェクトが、オブジェクトブラウザから見えているのに実行するとエラーになってしまいました。
MSDNの Regasm の解説ではちょっと分かりにくいのですが、COMでアセンブリを使用するためにはグローバルアセンブリキャッシュにインストールするか、レジストリに登録する必要があるようです。 /codebase オプションの解説に
登録しようとしているアセンブリを、後でグローバル アセンブリ キャッシュにインストールする場合は、このオプションを指定する必要はありません。
とありますが、裏を返せばグローバルキャッシュにインストールしない場合は/codebase オプションが必要ということになります。そこで、 /codebase を追加して登録してみます。
(こんな感じ↓)

%WINDIR%\Microsoft.NET\Framework\v4.0.30319\regasm MyClass.dll /codebase /tlb
pause

実行すると、警告が表示されますが、登録されます。
(こんな感じ↓)

こんどは、VBA からもちゃんと実行できます^^
(こんな感じ↓)

 さて、先ほどRegasmでDLLを登録した際に表示された警告が気になります^^ /codebase オプションの解説に
/codebase オプションと共に指定する assemblyFile 引数は、厳密な名前付きのアセンブリである必要があります。
とありましたので、素直に厳密な名前を付けてみます。とはいっても、IDE を使って署名するだけです(笑) SharpDeverop ですと、プロジェクトのプロパティーを開いて「署名」タブの中にある、「アセンブリ署名」にチェックを入れて、「キーファイル選択」テキストボックスから新規に作成します。
(こんな感じ↓)

ビルドして、Regasm で登録すると、今度は警告もなく登録されました。
(こんな感じ↓)

 さて、もうひと息(笑) こんどは、MyClass を Excel のシートから関数として直接使えるように登録してみます。
下記のコードを追加するだけです(笑)
 (コードの詳細については「COM アクセスに対するアプリケーションの配置」を参照してください。)
(こんな感じ↓)

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace MyClass
{
    [ComVisible(true)]
    public interface IMyclass
    {
        string Hello(string name);
    }
	/// <summary>
	/// Description of MyClass.
	/// </summary>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
	public class MyClass:IMyclass
	{
		public string Hello(string name)
		{
            return "Hello!" + name;
		}

#Region レジストリ登録処理
        [ComRegisterFunctionAttribute]
        private static void RegisterFunction(Type type)
        {
            Registry.ClassesRoot.CreateSubKey(
              GetSubKeyName(type, "Programmable"));
            RegistryKey key = Registry.ClassesRoot.OpenSubKey(
              GetSubKeyName(type, "InprocServer32"), true);
            key.SetValue("",System.Environment.SystemDirectory + @"\mscoree.dll",
              RegistryValueKind.String);
        }

        [ComUnregisterFunctionAttribute]
        private static void UnregisterFunction(Type type)
        {
            Registry.ClassesRoot.DeleteSubKey(
                            GetSubKeyName(type, "Programmable"), false);
        }
        private static string GetSubKeyName(Type type,
          string subKeyName)
        {
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            s.Append(@"CLSID\{");
            s.Append(type.GUID.ToString().ToUpper());
            s.Append(@"}\");
            s.Append(subKeyName);
            return s.ToString();
        }
#endregion

	}
}

 ビルドし、同じように Regasm で登録します。登録したら Excel を開いて、
「Excel のオプション」⇒「アドイン」⇒「設定」⇒「オートメーション」ボタンをクリックして、MyClass を選択します。
(こんな感じ↓)

 さて、シート上のどこかのセルに関数を挿入してみます。
「数式タブ」→「関数の挿入」→「関数の分類」リストの一番下に、MyClass.MyClass が追加されています。
(こんな感じ↓)

 
Hello メソッドも利用できますね^^
(こんな感じ↓)

さて・・・

これまでの実験でわかった事をまとめると・・・

1.マネージDLL(.Net を利用したDLL)を利用するには、Regasm をつかって登録する。
2.Excel VBA から参照設定して利用するには、/tlb オプションをつけて登録する
 (つけないとDLLエラーになって参照できない)。
3.クラスにはインターフェースを実装する(インターフェースがないとメソッドが公開されない)。
4.クラス本体は、ClassInterfaceType.None属性をつける(空のクラスが公開されないように)。
5.Excel VBA から利用するには、/codebase オプションをつける必要がある
 (つけないとオートメーションエラーになる)。
6./codebase で登録するアセンブリは厳密な名前付きでないといけないので、署名する
7.ComRegisterFunctionAttribute 属性をつけると、Regasm 実行時に実行するメソッドを作成できる。

 というわけで、Excel から利用するマネージDLL を作成・登録する実験を行ってみました。
やはり、VBA からもシートからも利用できるオートメーションCOMは便利です。
VBA から利用する場合、Test コードに示したように

Dim xxx As New xxx.xxx


といった具合に、あらかじめオブジェクトを生成しておくことが出来、利用しやすい利点もありますから♪

しかし、登録の手間が・・・。
VS についているインストーラは、動作が見えなくて、わけわからないし・・・(笑)

それにしても、バッチコマンドで Regasm を実行すると、登録/解除 が一発で出来ますが・・・Win7 だと上手くない。
作ったバッチファイルを右クリックして管理者権限で実行すると、ルートが変わってしまう(泣)
Vista ではどうだろう? なにか良い方法は無いものか・・・。

などと悩む、今日この頃です^^
( 追記: kinuasa さんが、スクリプトを作って公開されました!※コメント参照 )

追記:
ワークシート関数の拡張に関しては、 Excel-DNA で決まり!
ですが、
主に VBA から利用するクラスの場合には、オブジェクトが生成できるCOMのほうが使いやすい^^

用途に応じて使い分けるのがよろしいようです(笑)

では、また(笑)

追記:(2011/06/15)
Excel-DNA(0.29) から、ComServer がサポートされました!
Distribution > Samples > ComServer フォルダにサンプルがあります。

このサンプルでは、バッチファイルにより C# と VB で記述した二つのファイルをコンパイル(DLL&TLB 生成)し、ComServerPacked.xll を生成しています。これを動作確認用の ComServerTest.xls にドラッグすると、アドインコマンドから ComServerSample.dna で冒頭に定義されている DLL を 同一のAPPドメインにて、登録・解除 するが出来ます。Register Types for Com ボタンで DLL を登録すると、VBA で記述されている TestLateBound() と TestReference() にあるように、COM オブジェクトを生成することが出来ます。

複数のマネージDLLをパックして、同一ドメインにオートメーションCOM 登録できるという機能・・・。
いやはや凄いことを考えたもんです^^

[googlead]

This entry was posted in Excel, .NetFramework and tagged , . Bookmark the permalink.

12 Responses to Excel から使うマネージDLL を作る。

  1. たぶろ says:

    すごく参考にさせてもらってます。

    さらに、このオートメーションの関数やパラメタに説明をつけるにはどうしたらいいのでしょうか。

    • supermab says:

      たぶろ さん、はじめまして~♪
      >すごく参考にさせてもらってます。
      嬉しいです^^
      >さらに、このオートメーションの関数やパラメタに説明をつけるにはどうしたらいいのでしょうか。
      むむ・・・
      当然の疑問ですね~♪
      でも、解りません(笑)
      調べてみなくちゃいけませんね~^^
      分かったら、報告しますね♪
      たぶろ さんも分かったら教えてください^^

      • supermab says:

        MSDN に、残念な記述を見つけてしまいました ><

        Excel ワークシート関数用の Visual Basic オートメーション アドインを作成する方法
        によると、

        オートメーション アドインと関数ウィザード
        各オートメーション アドインは、Excel の関数ウィザードでそれぞれ独自の分類に属します。分類名はアドインの ProgID です。オートメーション アドインの関数用に異なる分類名を指定することはできません。また、関数ウィザードでオートメーション アドインの関数の説明、引数の説明、またはヘルプを指定することもできません。

        との事です・・・。

        アドインの関数の説明、引数の説明、またはヘルプを指定したい時には、
        Excel-DNA を利用するのが良いようです^^

        でも、どうしてもやりたい場合は・・・
        VBA から、DLL の関数を呼び出す Function を作成して、
        作成したFunction の属性を VBA 上で、MacroOption をつかって定義する方法があるようです。

        Assign functions to categories

        ちょっと、マニアックですけど(笑)

  2. kinuasa says:

    こんばんは。

    この手の情報をここまで丁寧に解説しているサイトは他に無いですね~!
    さすがです(^^)

    たしかにRegAsmによる登録面倒くさいんですよね・・・(^^;

    そんなわけで、ちょっと方法を考えてみました(笑)↓

    http://wp.me/p1o9bj-32

    • supermab says:

      おはようございます♪

      >そんなわけで、ちょっと方法を考えてみました(笑)↓
      おおおお~!さすが師匠!
      マネージDLL登録スクリプト~♪

      ドラッグ&ドロップ ってのも ナイスです!

  3. y sakuda says:

    こういうのよー分からんw
    しかし、きぬあささん作るの早いですね。
    私も登録とかやるのはバッチじゃなくVBSのドラッグ&ドロップの方が好きです。

  4. noname says:

    はじめまして。
    C#使いなら、xla + VSTOってのもありな気がします。

    • supermab says:

      noname さん

      はじめまして^^
      ご来店ありがとうございます(笑)

      >C#使いなら、xla + VSTOってのもありな気がします。

      なるほど・・・
      VSTOだと、クリックワンスで配布出来て便利そうですよね♪
      xla + VSTO のわかりやすい説明があったら、教えてくださいね^^

  5. noname says:

    ごく簡単なサンプルですが。
    VSTO側
    http://ideone.com/BXpzP
    Xla側(標準モジュール)
    Function GetNumber()
    Dim addIn As COMAddIn
    Dim automationObject As Object
    Set addIn = Application.COMAddIns(“ExcelAddInTest”)
    Set automationObject = addIn.Object
    GetNumber= automationObject.GetNumber
    End Function
    xlaはラッパーとして使い、VSTOで関数を定義します。
    標準モジュールにコードを書くことでUDFとして使えます。
    どうでしょうか?

  6. おおくぼ says:

    はじめまして、
    参考にさせていただきました。おかげさまで、「オートメーションエラー 指定したファイルが見つかりません」というエラーが無くなり。動作するようになりました。

  7. Pingback: VBAから扱えるDLLをC#で書いてみる。 | 初心者備忘録

  8. Pingback: SharpDevelopでExcel用COMアドインを作成する方法 | 初心者備忘録

Leave a Reply

Your email address will not be published. Required fields are marked *