Mathematica から.NETを呼び出す

はじめに

.NET/LinkMathematica ユーザが任意の.NETの型と Mathematica から直接インタラクトすることができるようにしてくれる.ユーザは Mathematica 言語の中で直接オブジェクトを作成し,メソッドやプロパティを呼び出すことができる..NETコードを書いたり,使いたい.NETの型を特別な方法で準備したりする必要はない.また MathLink についての知識も全く必要ない.実際,.NETのすべてが Mathematica のトランスペアレントな拡張となり,現存あるいは将来の.NETの型がすべて Mathematica 言語自体で書かれたもののようにさえ思えるようになる.

この便利さをここでは「インストールできる.NET」と呼ぶ.というのは .NET/Link は,Mathematica がこれまででも常にInstall関数を通して他の言語で書かれた拡張をプラグインすることができている能力を一般化するものだからである.しかし,CあるいはC++といった他の言語に比べて,.NET/Link は中間段階を完全になくしてしまうものであり,このために.NETが Mathematica のトランスペアレントな拡張になったということができるのである.

.NETはインタープリタ型環境と呼ばれることがあるが,これは実際は誤った呼び名である..NETを使うためには, C#等の言語で完全なプログラムを書き,それをコンパイルしてから実行する必要がある.Mathematica ユーザは,関数を試してみたり1度に1行ずつプログラムを構築してテストしてみたりすることができる真の意味でのインタープリタ型でインタラクティブな環境で作業をするという贅沢を味わうことができる..NET/Link はこの同じ生産的な環境を.NETプログラマーに提供する.Mathematica が.NETのスクリプト言語になったとさえ言える.

Mathematica ユーザにとっては, .NET/Link の「インストールできる.NET」の機能が Mathematica の拡張としての.NETの型の世界へのドアを開いてくれる..NETユーザにとっては,.NETプログラムをインタラクティブに開発,実験,そしてテストする際のシェルとして,.NET/Link が大変強力で用途の広い Mathematica 環境を使うことを可能にしてくれる.

このユーザガイドでは Mathematica から.NETランタイムに呼出しをかけることについて触れる..NETのアセンブリや型を Mathematica にロードし,これらの型のオブジェクトを作成し,メソッドとプロパティを呼び出す等のやり方を見ていく.また .NET/Link を使って標準のCスタイルDLL関数だけでなくCOMオブジェクトを呼び出す方法についても触れる.

簡単な例:

ProcessPriority.nb

GUIの例:

Circumcircle.nb

PackageHelper.nb

SimpleAnimationWindow.nb

RealTimeAlgebra.nb

AsteroidsGame.nb

DLLの呼出し:

BZip2Compression.nb

EnumWindows.nb

WindowsAPI.nb

COMオブジェクトの呼出し:

ExcelPieChart.nb

NETLink`パッケージをロードする

.NET/Link を使う前に .NET/Link パッケージをロードする必要がある.

Click for copyable input

.NETランタイムを起動する

InstallNET関数を使って.NETランタイムを起動する.

Click for copyable input

Mathematica で使用するために.NETクラスやその他の型を積極的に開発しているのであれば,変更したバージョンのクラスをもう1度ロードする前に.NETランタイムを再起動する必要がある.ReinstallNET関数を使って.NETランタイムを終了してから再起動するとよい.ほとんどのユーザについては,.NETを終了したり再起動したりすることが必要になることは全くないはずで,ReinstallNETあるいはUninstallNETを呼び出すことは避けるべきである..NETランタイムが Mathematica のセッションの多くのプログラムで共有されている可能性があることに注意する必要がある..NETランタイムを終了したり再起動したりすることによって,これらのプログラムに予期せぬ結果をもたらすことにもなり得る.

InstallNET[].NETランタイムを起動し,Mathematica から使用できるように準備する
ReinstallNET[].NETランタイムを終了してから再起動する
NETLink[].NETランタイムと交信するのに使われているLinkObjectを与える

.NETランタイムを起動する.

.NETのアセンブリと型をロードする

.NETアセンブリ

.NETのためのプログラムとライブラリはアセンブリと呼ばれる構成単位にパッケージされている.アセンブリは,バージョン付きの自己記述的なバイナリ(DLLあるいはEXE)であり,型(クラス,インターフェース,ストラクト等)とオプショナルなリソースの集合が含んでいると定義することができる.アセンブリは複数のファイルにまたがる(マルチファイルアセンブリ),あるいは単独のファイルの中に1つ以上のアセンブリを含めることが可能であるが,通常の場合アセンブリは単独のDLLまたはEXEのファイルからなる..NETアセンブリはDLL拡張子を取ることができるが,これらのアセンブリの内部は従来のCスタイルのDLLとは大きく異なる.しかし,どちらも他のプログラムによってロードされ呼び出されることを意図したコードのライブラリであるという点で,.NET DLLとCスタイルのDLLは概念的には似ている. EXEアセンブリは直接起動することができる実行可能プログラムであるが,このアセンブリは他のプログラムで使用するDLLのような型をエキスポートすることもできる.

アセンブリはシステムのどこに置いてもよい.システム上のすべての.NETプログラムが容易にアセンブリを見付けられるように,.NET Frameworkはアセンブリを保持しておくことができる特別の場所をシステム上に維持する.この場所はグローバルアセンブリキャッシュ(GAC:Global Assembly Cache)と呼ばれ,Windowsのルートディレクトリのアセンブリサブディレクトリにある..NET Framework自体の一部であるアセンブリは,GACに置かれ,ユーザがインストールする多くの.NETプログラムはそこにアセンブリを置く.アセンブリをGACに置く必要はなく,.NET/Link はシステム上のどこに置かれているアセンブリでも使用することができる.

すべての.NETの型はアセンブリにまとめられるため,Mathematica に型をロードする際にはまずそのアセンブリをロードする必要がある.LoadNETAssembly関数を使うとアセンブリを .NET/Link にロードすることができる.アセンブリは,アセンブリファイルへの完全パス等のさまざまなタイプのアセンブリについての情報を指定して,あるいはアセンブリの名前のすべてあるいは一部を使って,ロードすることができる.アセンブリ名については下でより詳しく見ていくが,ここではアセンブリ名はその作成者によって割り当てられるものであって,実際のアセンブリファイルの名前と全く似ても似つかない名前であることもあると言えば十分であろう.簡単なアセンブリの名前の例としてSystem.XMLが挙げられるが,これはXML関係の機能を処理する.NET Framework内のアセンブリである.アセンブリはしばしばそれが定義するもっとも重要なネームスペースにちなんで名付けられる.System.XMLアセンブリの実際のファイル名はSystem.XML.dllであり,GAC内部の奥深くにネストされたどこかに置かれている.

.NET/Link にすべてのアセンブリをロードするように明示的に指示する必要はない..NET Frameworkアセンブリならどれでも,つまり名前がSystemで始まる型(例えばSystem.Windows.Forms.FormSystem.Drawing.RectangleSystem.Data.DataSet等)を含むアセンブリすべてが自動的にロードされる. .NET/Link プログラミングで使用する大部分の型については,システムアセンブリ内に見付けることができる。その他のアセンブリについては,手動でロードする必要がある.このチュートリアルで後ほど説明するLoadNETAssembly関数は,アセンブリを .NET/Link にロードし,それらのアセンブリを Mathematica から使えるように準備するために使う Mathematica 関数である.

.NETの型

型は.NETプログラミングの基本的な構成単位である.すべての型はクラス,インターフェース,ストラクト(「値の型」),列挙,代表のカテゴリのいずれかに必ず入る..NETのすべてのオブジェクトはいずれかの型のインスタンスである.型の集合の範囲はクラスだけの範囲よりも広いが,型をクラスと考えたほうが分かりやすいこともある.

型はアセンブリで定義される..NET/Link 内で型をロードして使用する場合は,型が入っているアセンブリをまずロードしてから型そのものをロードする必要がある.これらのステップはしばしば1つの操作としてまとめて行うことが可能である.下で説明するLoadNETTypeは型を .NET/Link にロードするために使用する Mathematica 関数で,この関数によって型が Mathematica から使えるようになる.

LoadNETAssembly

LoadNETAssemblyはアセンブリを .NET/Link にロードしてこのアセンブリに含まれている型を Mathematica から使えるようにするための関数である.LoadNETAssembly には数多くの異なる引数のシーケンスが含まれる.これだけ異なった種類の引数がすべて使えるということは混乱を招くことのように思われるかも知れないが,その基本的な考えにはアセンブリに関する十分な情報を事実上あらゆる方法で特定できるようにすることによって,.NET/Link がそのアセンブリを見付けられるようにするということがある.

LoadNETAssembly[assemblyName]"System.Web"等の名前に基づいて指定されたアセンブリをロードする
LoadNETAssembly[path]その完全ファイルパスに基づいてアセンブリをロードする
LoadNETAssembly[url]このURLによって提示されたアセンブリをロードする
LoadNETAssembly[assemblyName,dir]アセンブリをその名前とそれが属するディレクトリに基づいてロードする
LoadNETAssembly[assemblyName,context`]アセンブリをその名前とそれが属するアプリケーションコンテキストに基づいてロードする
LoadNETAssembly[dir]このディレクトリ内のアセンブリをすべてロードする
LoadNETAssembly[context`]このアプリケーションコンテキスト内のアセンブリをすべてロードする

アセンブリをロードする.

以下はLoadNETAssemblyを使って.NET Frameworkの部分であるアセンブリをロードする例である.

In[3]:=
Click for copyable input
Out[3]=

LoadNETAssemblyの戻り値はNETAssemblyの式である.これは .NETオブジェクト自体ではなく,アセンブリの指定を引数として取るようなさまざまな関数内にロードされたアセンブリを参照するために .NET/Link で使うことができる特別な式に過ぎない.

上の例で使われた名前はアセンブリの単純名である.アセンブリの実際のフルネームあるいは表示名はもっと長く,バージョン情報やその他の情報が含まれている.特定のバージョンがロードされるように強制したい場合にフルネームを使うとよい.(ただし,下のコマンドを今実際に実行してもご使用のマシンにインストールされている.NET Frameworkがここに書かれたバージョンと全く同じものでない限り不成功に終ることに注意.)

In[4]:=
Click for copyable input
Out[4]=

このアセンブリはGACにあるので,その他の位置に関する情報がなくてもその名前だけに基づいてロードすることができる.

LoadNETAssemblyのもう1つの例として例えば,あるアセンブリを手に入れたか,あるいはある.NET 言語を使って自分でアセンブリを作成したかして,そのアセンブリを Mathematica から使えるように .NET/Link にロードしたいとしよう.アセンブリはファイルc:\MyProgram\Bin\Debug\MyAssembly.dllの中に入っているとする.以下がそれをロードする方法である.

Click for copyable input

アセンブリをそのパスを指定することによってロードする方法は,自分で作成したアセンブリをロードする際に便利である.

LoadNETAssemblyを使って,Mathematica アプリケーションディレクトリ内のアセンブリのサブディレクトリからアセンブリをロードすることも可能である.これは .NET/Link を使うアプリケーションを作成している開発者を対象とした関数である.MyAppと呼ばれる Mathematica アプリケーションディレクトリがあって,それが Mathematica アプリケーションが通常インストールされる場所のいずれか(例えば<Mathematica dir>\AddOns\Applications )にインストールされている場合には,MyAppディレクトリに「assembly」という名のサブディレクトリを作り,アセンブリのディレクトリの中にアプリケーションで必要となる余分のアセンブリをすべて入れておくことができる.そうするとアセンブリの名前とアプリケーションに対応するコンテキストを与えてやることによってアプリケーションの Mathematica コードがアプリケーションアセンブリの1つをロードすることができる.

Click for copyable input

この方法で,アプリケーション開発者は自分たちのアプリケーションレイアウト内のprivateアセンブリをバンドルすることができ,アプリケーションのユーザにGACにアセンブリをコピーする等の特別のインストールのステップを踏むように求める必要がなくなる.

LoadNETType

LoadNETTypeは.NETの型をロードして Mathematica から使えるようにする Mathematica 関数である.明示的にLoadNETTypeを呼び出す必要がない場合も多い..NETオブジェクトが Mathematica に返されるたびにその型がロードされる.これはつまり,特定の型の新しいオブジェクトを作成したい場合に,NETNewを呼び出すだけで,オブジェクトが Mathematica に返されたときに型がロードされるようになる.直接LoadNETTypeを呼び出す最大の理由は,静的メソッドあるいはプロパティを型から使いたいような場合である.そのような場合,NETNewでオブジェクトを作成しているわけではないので,型を手動でロードする必要がある.

LoadNETType[typeName]指定された型をロードする
LoadNETType[typeName,assemblyName]指定されたアセンブリから型をロードする
LoadNETType[typeName,NETAssembly]NETAssembly式で特定されたアセンブリから型をロードする
LoadNETType[typeName,assemblyName,context`]指定されたアプリケーションコンテキストに属する指定されたアセンブリから型をロードする

型をロードする.

以下はLoadNETTypeを使った簡単な例である.

In[5]:=
Click for copyable input
Out[5]=

LoadNETTypeの戻り値はNETTypeの式である.これは.NETオブジェクトそのものではなく,引数として型の指定を取るさまざまな関数における.NETの型を参照するために .NET/Link で使うことができる特別の式に過ぎない.

ネームスペース接頭辞を含めた型のフルネーム(この場合はSystem.Windows.Forms)を供給する必要があることに注意されたい.ロードを成功させるためには,型が属するアセンブリはすでにLoadNETAssemblyを使ってロードされた状態になければならない.先に述べたようにSystemのすべての型に対するアセンブリは .NET/Link で必要とされる場合に自動的にロードされるので,上の例でSystem.Windows.Formsアセンブリを手動でロードする必要がないのである.

ロードされたアセンブリと型を見る

ユーティリティ関数であるLoadedNETAssembliesLoadedNETTypesを使って,現在の Mathematica セッションにどのアセンブリと型がロードされているかをチェックすることができる.これらは主にデバッグのための関数である.

LoadedNETAssemblies[]Mathematica にロードされるすべてのアセンブリのリストを返す
LoadedNETTypes[]Mathematica にロードされるすべての型のリストを返す

ロードされたアセンブリと型を見る.

静的な型のメンバのコンテキストと可視性

LoadNETTypeには静的なメソッドとフィールドのネーミングと可視性を制御することができる2つのオプションがある.これらのオプションを理解するためには,これらのオプションが解決してくれる問題を理解する必要がある.このことを説明するために,まだどのようにして.NETメソッドを呼び出すのかということについて触れていないので,少し先走りして話を進める必要がある.型がロードされると,Mathematica 内に定義が作成されてそのクラスのオブジェクトのメソッド,プロパティ,そしてフィールドを呼び出すことができる.静的メンバは非静的メンバとは全く異なる形で扱われる.例えばネームスペースMyCompany.UtilitiesMyCompanyという名前のクラスがあって,このクラスにはFooという名前の静的メソッドが含まれているとする.このクラスをロードするときにFoo[args]のような名前で呼び出すことができるように,Foo に対する定義を設定する必要がある.それでは,一体どのようなコンテキストで,シンボル Foo を定義して,このコンテキストが可視である(つまり$ContextPath上にある)ようにすることが望ましいのであろうか.

.NET/Link は常にFooの定義をその完全修飾クラスネームであるのミラーとなるようなコンテキストで作成する.これは別のコンテキストにあるかも知れないという名前のシンボルとの衝突を避ける目的で行われる.しかし,MyCompany`Utilities`MyClass`Foo[args]のように毎回コンテキストのフルネームをタイプしてFooを呼び出さなければならないのは要領が悪いことのように思えるかも知れない.オプションAllowShortContext->True(デフォルト設定)を使うと,階層的なネームスペース接頭辞を必要としないクラスネームだけからなる短縮されたコンテキストにアクセスできるようなFooの定義を .NET/Link が作成することにもなる.上の例では,つまりFooを単にMyClass`Foo[args]として呼び出すことができるということである.Mathematica セッションにすでに同じ名前を持つコンテキストが存在するために,コンテキストの短縮名を使うことを避けなければならないような場合には, AllowShortContext->Falseを使うとよい.こうすることによって,すべての名前は「深い」コンテキストにのみ置かれるようになる.AllowShortContext->Trueを使った場合でも,静的なものの名前も深いコンテキストに置かれるため,シンボルを参照する際に常に深いコンテキストを使うことも可能である.

つまり,AllowShortContextでシンボル名が定義されるコンテキストを制御することができる.もう1つのオプションであるStaticsVisibleはこのコンテキストが可視になっている($ContextPath上に置かれている)かどうかを制御する.このデフォルトはStaticsVisible->Falseであるので,MyClass`Foo[args]の場合のようにシンボルを参照するときにコンテキスト名を使う必要がある.StaticsVisible->Trueと設定されると,$ContextPath上に置かれるので,Foo[args]と書くだけでよい.デフォルト設定を Trueとすることは少し危険である.というのは,クラスをロードするたびに数多くの名前が突然 Mathematica セッションの中で作成され可視にされるようになる可能性が潜在的に存在し,同じ名前のシンボルがすでに存在する場合にさまざまな「シャドーイング」の問題が起る可能性があるからである.(コンテキストとシャドーイングの問題についてはコンテキストを参照のこと.)

このため,StaticsVisible->Trueは自分が書いたクラス,あるいは使い慣れたコンテンツのクラスに対してだけ使用する方がよい.そのような場合には,タイプしなければならない量が減り,コードがもっと読みやすいものとなり,クラスネームの接頭辞をタイプし忘れるというような安易なバグを防ぐことができる.典型的な例としては由緒ある「addtwo」の MathLink の例題プログラムを実装することが挙げられる.C#では,これは下のような形になる.

デフォルト設定のStaticsVisible->Falseを使うと,AddTwoとして呼び出す必要がある. 設定StaticsVisible->Trueを使うことによってもっと分かりやすくと書くことができるようになるのである.

これらのオプションは静的なメソッドとフィールドにだけ使えるものである.後に触れるように,非静的なものはコンテキストや可視性の問題が完全になくなるような方法で処理される.

StaticsVisible->True特別なコンテキストの中でではなく,静的なメソッドとフィールドをその名前だけでアクセスできるようにする
AllowShortContext->False階層的なネームスペースのフルネームをミラーするコンテキストの中でのみ静的なメソッドとフィールドにアクセスできるようにする

LoadNETTypeのオプション.

.NETと Mathematica の間で型を変換する

.NETオブジェクトを作成してメソッドを呼び出す操作を行う前に,Mathematica と.NETの間の型のマッピングを調べる必要がある..NETメソッドが結果を Mathematica に返す際に,結果は自動的に Mathematica 式に変換される.例えば,.NETの整数の型(例えばInt32Byte等)は Mathematica の整数に変換され,.NETの実数の型(SingleDouble)は Mathematica の実数に変換される.下の表では変換のすべての事例を示している.これらの変換は両方向に行えるものであり,例えば Mathematica の整数がByteの値を必要とする.NET メソッドに送られると,整数は自動的に.NET Byteに変換される.

この表では.NET Frameworkで使用される型の名前が挙げられている.異なる言語ではこれらの内在する型をマップするのにそれ独自のキーワードを使うことが多い.例えばC#では,キーワードintInt32の型のエイリアスであり,Visual Basic .NETではInt32の型はIntegerと呼ばれる.

.NETの型
Mathematica の型
Byte, SByte, Char, Int16, UInt16, Int32, UInt32, Int64, UInt64Integer
DecimalIntegerあるいはReal
Single, DoubleReal
BooleanTrueあるいはFalse
StringString
ArrayList
controlled by userComplex
ObjectNETObject
Expr任意の式
nullNull

.NETと Mathematica で対応する型.

.NETの配列は適切な深さの Mathematica リストにマップされる.したがって,double[](C#表記)を取るメソッドを呼び出す際には,それに{1.0, 2.0, N[Pi], 1.23}を渡すかも知れない.同様に,整数の深さ2の配列(C#表記ではint[,])を返すメソッドは Mathematica に式を返すかも知れない.

オブジェクトを作成する

.NETオブジェクトを構築するためにはNETNew関数を使う.NETNewの第1引数はLoadNETTypeから返されるNETType式として,あるいは型の完全修飾名(つまりネームスペース接頭辞を含む)を与える文字列として指定されるオブジェクトの型である.オブジェクトのコンストラクタに任意の引数を供給したいような場合には,引数は型の後ろに数列として続く.

NETNew[typeName,arg1,...]指定されたクラスの新しいオブジェクトを構築して,それを Mathematica に返す
NETNew[NETType,arg1,...]指定されたクラスの新しいオブジェクトを構築して,それを Mathematica に返す

.NETオブジェクトを構築する.

例えば,以下は新しいFormを作成する.

In[6]:=
Click for copyable input
Out[6]=

NETNewからの戻り値は,山カッコの中に入っているということを除いて,頭部NETObjectを持つように見える変な式である.山カッコは,式が表示されている形式はその内部表示形式とは全く異なるということを示している.これらの式はNETObject式と呼ばれる.NETObject式は型の名前を示すような形で表示されるが,ばらばらにしたり中をのぞいたりすることができないという意味で不透明であると考えられるべきものである.これらの式はNETObject式を取る .NET/Link 関数でのみ使うことができる.

NETNew は渡されようとしている引数の型に適切な.NETのコンストラクタを呼び出し,事実上オブジェクトの参照となるものを Mathematica に返す.NETObject 式はこのように,C#あるいはVisual Basic .NETのような.NET言語におけるオブジェクトの参照と大変よく似ている.NET オブジェクトへの参照であるとして考えられるべきである.Mathematica に返されるものは,どの型のオブジェクトを構築しているかにかかわらず,大きなものではない.特にオブジェクトのデータ(つまりそのフィールド)は Mathematica に送り返されない.実際のオブジェクトは.NET側に残され,Mathematica はそれに対する参照を得る.

上の例ではクラスはその名前を文字列として与えることによって指定された.代りにNETType式を使うこともできる.NETType式はクラスを特定するLoadNETTypeによって返される特別式である.クラスネームを文字列として指定すると,すでにロードされていない場合にはクラスがロードされる.

In[7]:=
Click for copyable input

NETNewだけが Mathematica で.NETオブジェクトへの参照を与える方法ではない.多くのメソッドとプロパティがオブジェクトを返し,そのようなメソッドやプロパティを呼び出すときはNETObject式が作成される.そのようなオブジェクトはNETNewを使って明示的に構築するオブジェクトと同じ方法で使うことができる.

メソッド,プロパティ,フィールドを呼び出す

構文

.NET メソッドを呼び出してフィールドにアクセスするための Mathematica 構文は,C#およびVisual Basic .NETで使用される構文に大変よく似ている.下のボックスは. Mathematica とC#でコンストラクタ,メソッド,プロパティ,フィールド,静的メソッド,静的プロパティ,静的フィールドを呼び出す方法を比べたものである..NETを使う Mathematica プログラムはC#(あるいはVB .NET)プログラムとほとんど全く同じように書かれていることが分かる.ただし,Mathematica では引数に()ではなくを使い,「メンバアクセス」演算子として「.」(ドット)ではなくを使う.

例外は,静的メソッドに Mathematica ではコンテキストマークをC# およびVBで使われるドットの代りに使うということである.これらの状況におけるドットの使用は実際(C++における「::」のような)スコープ変換演算子ようなものであるので,この用法はこれらの言語における使用方法に匹敵するものである.Mathematica ではこの用語を使用しないが,Mathematica のスコープ変換演算子はコンテキストマークである..NET のネームスペースの名前は直接 Mathematica の階層的なコンテキストにマップする.

コンストラクタ
C#:MyClass obj = new MyClass(args);
Mathematica:obj = NETNew["MyClass", args];
メソッド
C#:obj.MethodName(args);
Mathematica:obj@MethodName[args]
プロパティとフィールド
C#:obj.PropertyOrFieldName = 1; value = obj.PropertyOrFieldName;
Mathematica:obj@PropertyOrFieldName = 1; value = obj@PropertyOrFieldName;
静的メソッド
C#:MyClass.StaticMethod(args);
Mathematica:MyClass`StaticMethod[args];
静的なプロパティとフィールド
C#:MyClass.StaticPropertyOrField = 1; value = MyClass.StaticPropertyOrField;
Mathematica:MyClass`StaticPropertyOrField = 1; value = MyClass`StaticPropertyOrField;

C#と Mathematica の構文比較.

読者の中には,関数を引数に適用するための Mathematica 演算子としてを使うことになじみがある方もおそらくいらっしゃるであろう. は通常もっとよく使われると同じである..NET/Link は何か特別の操作のためのを奪うわけではなく,単に普通の関数アプリケーションが少し形を変えたというだけのものである.つまりを全く使う必要がないということである.以下はメソッドを呼び出すための全く同じ方法である.

Click for copyable input

最初の形式は,ほとんどの.NET言語の構文を Mathematica にマップする自然な方法をそのまま維持するもので,このチュートリアルではもっぱらこの形式を使用する.

メソッド,プロパティ,フィールドのいずれかを呼び出し,結果を返すようにする場合,.NET/Link は上に示す表に従って引数と結果をその Mathematica 表記へ,そして表記から自動的に変換する.

オブジェクト指向の言語では,メソッドとフィールドの名前はそれらが呼び出されるオブジェクトによってスコープされる.つまりobj.Meth()と書く際に 他のクラスにもMethという名前の他のメソッドがあるような場合にでも.NET 言語はobjのクラスに属するMethという名前のメソッドを呼び出しているということを知っている..NET/LinkMathematica シンボルへのこのスコープを保存し,同じ名前の既存のシンボルとの衝突が決して起らないようにする.obj@Meth[]と書くと,システム中のMethという名前の他のシンボルとの衝突が起らない.つまり,この呼出しの評価で Mathematica が使うシンボルMethは,このクラスのために .NET/Link が設定したものである.以下はフィールドを使った例である.まずPointオブジェクトを作成する.

In[9]:=
Click for copyable input
Out[9]=

PointクラスはXYという名前のフィールドを持ち,フィールドがその座標を持つ.しかしユーザのセッションの中にもXあるいはYという名前のシンボルがあるかも知れない.評価されたときにそれが分かるXの定義を設定することができる.

In[10]:=
Click for copyable input

今度はXという名前のフィールドの値を設定する.(これはC#あるいはVBではpt.X = 42と書かれる.)

In[11]:=
Click for copyable input

ここでは"gotcha"が出力されていない.Print定義を持つGlobal`コンテキストの中のシンボルXと,このコードの行の評価中に使用されるシンボルXとの間には衝突はない..NET/Linkの右辺にあるメンバの名前にプロテクトをかけるので,これらの名前が可視のコンテキストでこれらのシンボルに対して存在するかも知れない任意の定義と衝突したり,この定義に頼ったりすることがない.

要約すると,非静的なメソッド,プロパティ,フィールドについては,どのようなコンテキストにあっても,つまり現在どの$ContextPathであっても,名前の衝突とシャドーイングを心配する必要は全くない.しかし,静的メンバについてはこの限りではない.静的なメソッドとフィールドはオブジェクトへの参照なしにフルネームで呼び出されるので,名前をスコープするオブジェクトが前面に出ることはない.以下は.NETのガベージコレクタを呼び出す静的メソッドの簡単な例である.静的メソッドを呼び出す前にLoadNETTypeを呼び出して,クラスがきちんとロードされるようにする必要がある.

In[12]:=
Click for copyable input

静的メンバは自身のコンテキスト(この例ではGC`)で定義されるため,名前をスコープすることは通常静的メンバでは問題ない.これらのコンテキストは通常$ContextPathにはないので,Global`コンテキスト,あるいは読み込まれているパッケージに同じ名前のシンボルが存在することを心配する必要はない.セッションにすでにGC`という名前のコンテキストが存在し,自身の関数Collectを持つ場合は,静的メンバの型のフルネームに対応する完全に階層的なコンテキストの名前を使って常に衝突を避けることができる.

In[14]:=
Click for copyable input

.NETの名前に含まれるアンダースコア

.NETの名前には Mathematica シンボルでは使ってはいけない文字を含めることができる.唯一共通の文字はアンダースコアである..NET/Link はアンダースコアを型,メソッド,プロパティ,フィールドの名前で「U」とマップする.ただし,このマッピングは必要な場合のみ,つまり名前が文字列としてではなく記号形式で使われる場合にだけ使われる.例えば,My_Classという名前のクラスがあるとしよう.文字列としてこのクラスの名前を参照するときはアンダースコアを使う.

Click for copyable input

しかし,そのようなクラスで静的メソッドを呼び出す場合には,階層的なコンテキストの名前が記号的であるので,アンダースコアをUに変換する必要がある.

Click for copyable input

同じ規則がメソッドとフィールドの名前にも適用される.コードでそのような名前を参照する場合には,Uを使う.以下はSome_Propertyという名前のプロパティ名を呼び出す方法である.

In[1]:=
Click for copyable input

型とオブジェクトに関する情報を得る

NETTypeInfo

特定の.NETの型に存在するメソッド,プロパティ,フィールド等の情報を素早く表示できると便利なことがよくある..NET/Link はこの情報を得るためにNETTypeInfo関数を提供する.

NETTypeInfo[typeName]特定の型のメンバすべてについての情報を出力する
NETTypeInfo[typeName,members]特定の型のメンバの希望の型についての情報だけを出力する
NETTypeInfo[typeName,members,"pat"]名前が文字列パターンにマッチするメンバの希望する型についての情報を出力する
NETTypeInfo[obj]オブジェクトの型のメンバすべてについての情報を出力する
NETTypeInfo[NETAssembly]特定のアセンブリの型すべてについての情報を出力する

型とオブジェクトについての情報を得る.

NETTypeInfoは型がすでにロードされていない場合にはその型をロードする.

以下はProcessクラスに関する数多くの情報を表示する.

Click for copyable input

NETTypeInfoの第2引数は表示したいメンバのオプショナルのリストである.可能な値は,"Type" (型自体について一般的な情報を与える),"Constructors""Methods""Properties""Fields""Events"である.以下はプロパティとメソッドだけを表示する.

Click for copyable input

以下では"Peak"で始まる名前を持つプロパティだけを表示する.

Click for copyable input

デフォルトの動作ではC#構文でメンバを表示する. Visual Basic .NETの構文で表示したい場合はLanguageSyntaxオプションを使う.

Click for copyable input
オプション名
デフォルト値
LanguageSyntax"CSharp"出力がフォーマット化される言語構文は"CSharp"あるいは"VisualBasic"でなければならない
InheritedTrue継承メンバを含むかどうか
IgnoreCaseFalse文字列パターンにマッチする名前の大文字小文字の違いを無視するかどうか

NETTypeInfoのオプション

NETTypeInfoはアセンブリにどの型があるのかを見るのにも便利である.アセンブリを調べるためには, NETAssembly式を第1引数として渡す.NETAssembly式を得るもっとも簡単な方法は(アセンブリがすでにロードされている場合でも)LoadNETAssemblyを呼び出す方法である.以下の行を使うとたくさんの型が表示される.

Click for copyable input

NETAssembly式を実行している場合には,NETTypeInfoの第2引数は表示したい型のオプショナルなリストである.可能な値としては,"Classes""Interfaces""Structures""Delegates""Enums"がある.以下では名前に"Data"という言葉が含まれているクラスかインターフェースだけを表示する.

Click for copyable input

その他の便利な関数

NETObjectQ[expr]expr が.NETオブジェクトの有効な参照である場合はを返し,その他の場合はを返す
InstanceOf[obj,type]このオブジェクトが type のインスタンスの場合はを,その他の場合はを返す
GetTypeObject[NETType]NETType式に対応するTypeオブジェクトを返す
GetAssemblyObject[NETAssembly]NETAssembly式に対応するAssemblyオブジェクトを返す

オブジェクトと型のユーティリティ関数.

NETObjectQは式が.NET オブジェクト参照であるかどうかをテストしなくてはならない場合に便利である.関数の定義の中でしばしばパターンテストとして使われる.

Click for copyable input

.NET/Link は頭部NETTypeLoadNETTypeで返される)およびNETAssemblyLoadNETAssemblyで返される)を持つ特別な式を使って,Mathematica の.NET の型とアセンブリを表示する.先に述べたように,型やアセンブリを引数として取る関数(例えばNETNew)にこれらの式を渡すことができる. 特定の型にNETType 式ではなく,実際の.NET Typeオブジェクト参照を使いたい,また同様に特性のアセンブリに使いたい場合があるかも知れない.その場合,GetTypeObjectを使えばNETType式に対応するTypeオブジェクトを,また GetAssemblyObjectを使えば.NET Assembly オブジェクトを得ることができる.

In[1]:=
Click for copyable input
Out[1]=
In[2]:=
Click for copyable input
Out[2]=

上で見るとNETType式の方がTypeオブジェクトよりもどの型を表示しているのかについてのずっと詳しい情報が得られる.これが .NET/LinkTypeおよびAssemblyのオブジェクトを単に使う代りに,特別なNETTypeおよびNETAssemblyの式を使う理由の1つである.

一旦Typeオブジェクトを得ると,Typeクラスのメソッドとプロパティを使ってそのオブジェクトについて知ることができるようになる.

In[3]:=
Click for copyable input
Out[3]=
In[4]:=
Click for copyable input
Out[4]=

参照カウントとメモリ管理

Mathematica でのオブジェクト参照

先ほどNETObject式の取扱いについて触れた際には,参照カウントや独自性等のよりつっこんだ問題については触れなかった.メソッドあるいはプロパティの結果として,またはNETNewへの明示的な呼出しの結果として,.NET のオブジェクト参照が Mathematica に返されるたびに,.NET/Link はこのオブジェクトへの参照がこのセッションの前に送られたかどうかをチェックする.もしも送られていなければ, Mathematica 内にNETObject式が作成され,それに対する定義が数多く設定される.これは比較的時間がかかるプロセスである.このオブジェクトがすでに Mathematica に送られた場合は,ほとんどの場合 .NET/Link は以前に作成されたものと同じNETObject式を単に作成する.このプロセスの方がずっと速い操作である.

この最後の規則には例外もいくつかある.つまり,オブジェクトが Mathematica に返されるときに,これと同じオブジェクトが以前 Mathematica に送られたような場合でも,新しく異なるNETObject式がオブジェクトに対して作られることがある.具体的にはオブジェクトのハッシュ値(オブジェクトの組込みのGetHashCode()メソッドで決定される)が前回 Mathematica で見られたときから変わった場合はいつも,作成されるNETObject式は異なるものになる.あまりこの点に関して細かいことを心配する必要はないが,NETObject 式を比べてこれらの式が同じオブジェクトを指しているのかどうかを判断するときには MathematicaSameQ関数が使えないということは覚えておいた方がよい.その場合は SameObjectQ 関数を使わなければならない.

SameObjectQ[obj1,obj2]NETObjectobj1 および obj2 が同じ.NETオブジェクトを参照する場合はを,それ以外の場合はを返す

NETObject式を比べる.

以下に例を挙げる.

In[1]:=
Click for copyable input
Out[1]=

変数ptは.NET Pointオブジェクトを指す.今度はこれをコンテナに入れて後から取り出せるようにする.

In[2]:=
Click for copyable input

今度は座標の1つの値を変更する.Pointオブジェクトについては,これでそのハッシュ値が変更される.

In[4]:=
Click for copyable input

ptで与えられたNETObject式とArrayListの第1要素を Mathematica に返すように要求する際に作成されるNETObject式を比べる.これらはどちらも同じ.NETオブジェクトを参照するものであるが,NETObject式は異なる.ArrayList クラスはインデクサ(C# の用語)を定義するため,インデックスで要素を参照する際に 表記が使えることを思い出していただきたい.

In[5]:=
Click for copyable input
Out[5]=

SameQ ()を使って Mathematica の2つのオブジェクト参照が同じ.NETオブジェクトを指しているのかを判断することはできないので,.NET/Link はこれを行うためにSameObjectQ関数を提供する.

In[6]:=
Click for copyable input
Out[6]=

SameObjectQ関数が何の役に立つのか,オブジェクトのEquals()メソッドを呼び出せばいいではないか,と思っておられる方がおそらくいらっしゃるだろう.確かにこの例では正しい結果が返される.

In[7]:=
Click for copyable input
Out[7]=

この方法の問題はEquals()がいつもオブジェクト参照を比べる訳ではないという点にある.任意のクラスが自由にEquals()をオーバーライドして,そのクラスの2つのオブジェクトを比べるための希望の動作を提供することができる.クラスの中には,Equals()Stringクラスのようにオブジェクトの「コンテンツ」を比べさせるものもある.このStringクラスは文字列の比較のために使用される.正しいテストを提供する関数は静的メソッドReferenceEquals()である.

In[8]:=
Click for copyable input
Out[8]=

SameObjectQは明示的にReferenceEquals()を呼び出す場合と同じ動作を行う便利な関数だと考えることもできる.

稀なケースとしてオブジェクト参照の等価性を何度も比較しなくてはならない場合に,SameQに比べてSameObjectQがゆっくりであるということが問題になる可能性がある.全く同じ.NETオブジェクトを参照する2つのNETObject式がSameQではないようにする唯一のものは,2つのNETObject式が作成される間にオブジェクトのハッシュ値が変わった場合である.これが起っていないことが分かっているのであれば,SameQを使って2つの式が同じオブジェクトを指しているかどうかをテストしても問題ない.

ReleaseNETObject

.NETランタイムには「ガベージコレクション」を呼ばれる組込みの施設があって,プログラムがもはや使わなくなったオブジェクトによって占められているメモリを解放する.オブジェクトへの参照が,おそらく同じように未参照の他のオブジェクトで参照されている以外ではどこにも存在しないときに,そのオブジェクトはガベージコレクションの候補となる.NETNewへの呼出しの結果として,あるいはメソッドかプロパティの戻り値としてオブジェクトが Mathematica に返されるときには,.NET/Link コードは.NET側のオブジェクトへの特別の参照を持ち,そのオブジェクトが Mathematica で使用されている間にガベージコレクトされないようにする.特定の.NETオブジェクトを Mathematica セッションで使わなくてもよくなったことが分かっている場合には,.NET/Link にその参照を解放するように明示的に伝えることができる.これを行う関数がReleaseNETObjectである.Mathematica に特定の参照を.NETで解放することに加えて,ReleaseNETObjectはオブジェクトのために Mathematica で作成された内部定義もクリアする.後からこのオブジェクトを Mathematica で使おうとしても使えない.

In[9]:=
Click for copyable input
Out[9]=

今度は.NETにもはやこのオブジェクトを Mathematica から使う必要がないことを伝える.

In[10]:=
Click for copyable input

Mathematica セッションからオブジェクトの記号表示が削除されたため,frmを参照するとエラーが出る.解放されたオブジェクトを使おうとすると以下のような出力が返される.

In[11]:=
Click for copyable input
Out[11]=
ReleaseNETObject[obj]Mathematicaobj をもう使わなくなったということを.NETに伝える
NETBlock[expr]expr の評価中に Mathematica に返された新しい種類の.NETオブジェクトはすべて expr が終了した際に解放される
BeginNETBlock[]今からマッチするEndNETBlock[]までの間に Mathematica に返される新しい種類の.NETオブジェクトはすべて解放される
EndNETBlock[]マッチするBeginNETBlock[]以来見られる新しい種類のオブジェクトをすべて解放する
LoadedNETObjects[]Mathematica で使用されているすべてのオブジェクトのリストを返す

メモリ管理関数.

ReleaseNETObjectを呼び出すことが必ずしもオブジェクトがガベージコレクトされることを引き起す訳ではない.そのオブジェクトへの他の参照が.NETに存在している可能性も大きい.ReleaseNETObjectが.NETにオブジェクトを捨てるように伝える訳ではなく,Mathematica のためだけにオブジェクトを残しておく必要がないことを伝えるだけである.

.NET/LinkMathematica に送られるオブジェクトのために維持する参照について重要なことは,何度そのオブジェクトが Mathematica に返されるかにかかわらず,それぞれのオブジェクトに対して参照は1つしか保存されないということである.ReleaseNETObjectを呼び出した後で,Mathematica セッションに存在するかも知れない任意の参照を通してそのオブジェクトを使おうとすることが決してないようにする必要がある.

In[3]:=
Click for copyable input

ReleaseNETObject[frm1]を呼び出すと,Mathematica シンボルの が影響を受けるのではなく, が参照する.NET オブジェクトが影響を受けるのである.このため,を使うこと(あるいはこの同じ Form オブジェクトを参照するこれ以外の方法)も誤りである.

一時的な使用にReleaseNETObjectを呼び出す必要はないことが多い.セッションで.NET を多用するのでなければ,どのオブジェクトが必要であるか,あるいは必要ではなくなったかを気にする必要は普通なく,オブジェクトが増えるまま放っておけばよい.しかし,.NET でメモリ使用が重要になったようなときには,ReleaseNETObjectが提供する追加的なコントロールが必要になることがあるかも知れない.

NETBlock

ReleaseNETObjectは主として他の人が使えるようにコードを書いている開発者のために提供されている.コードがどのように使用されるかを予想することは不可能なので,開発者はコードが作成する不要な参照をコードから取り除くように常に気を付けるべきである.おそらくこの操作に使える最も便利な関数はNETBlockである.

NETBlock は式の評価中に起るオブジェクトの解放のプロセスを自動化する.Mathematica プログラムにおいて,NETNewを使って.NETオブジェクトをいくつか作成し,それらのオブジェクトを操作しておそらく他のオブジェクトがメソッドの呼出しの結果として Mathematica に返されるようにして,最終的に数字や文字列等の結果を返すようにしなくてはならないことがよくある.この操作中に Mathematica が出会う.NETオブジェクトはすべてプログラムの存続期間中にだけ必要とされるものであり,これらのオブジェクトは Mathematica 内でBlockおよびModuleによって,そしてC#,C++,Java,およびその他の言語の中でブロックスコーピングコンストラクト(例えば {})によって提供される局所変数に大変似ている.NETBlockを使うと,Mathematica の評価中に返された新しいオブジェクトはどれでも一時的なものとして扱われ,NETBlockが終了したときに解放されるべきものであるというプロパティを持つものとして,一連のコードに印を付けることができる.

先の文で「新しいオブジェクト」と言っていることに注目されたい.NETBlockは評価中に出会ったオブジェクトをすべて解放する訳ではなく,初めて出会ったものだけを解放する.Mathematica がすでに出会ったことのあるオブジェクトは影響を受けない.つまり,真の意味でその評価で一時的に使われているのではないオブジェクトをNETBlockが積極的に解放してしまうことを心配する必要はない.

NETNewで作成するオブジェクトすべてに対してReleaseNETObjectを単に呼び出すだけでは十分ではない.多くの.NETのメソッドとプロパティがオブジェクトを返すからである.これらの戻り値は重要ではないかも知れない. 他の呼出しに(例えばobj@ReturnsObject[]@Foo[]での例のように)これらのオブジェクトが一緒に縛り付けられているために,これらに指名された変数を割り当てることが全くないかも知れないが,それでもオブジェクトを解放する必要はある.NETBlockの使用は,一連のコードが終了したときに新しい種類のオブジェクトがすべて確実に解放されるようにする簡単な方法なのである.

NETBlock[expr]expr が返されるときにいつも返される.

多くの .NET/Link Mathematica プログラムが以下の構造を持つ.

Click for copyable input

多くの式を作成し,残りは一時的なものであるのでその中の1つだけを返すというような関数を書くということは大変よくあることである.これを容易にするために,NETBlockの戻り値が単独のである場合にはこのオブジェクトは解放されない.

Click for copyable input

どのオブジェクトがNETBlockから逃げることができるかをもっと制御したい場合は,KeepNETObject関数を使うとよい.単独のオブジェクト,あるいは一連のオブジェクトに対してKeepNETObjectを呼び出すということは,囲んでいる最初のNETBlockが終了するときに,これらのオブジェクトは解放されないということである.しかし外側に囲んでいるNETBlockがある場合には,オブジェクトはその NETBlockが終了したときに解放されるので,オブジェクトが.ネストされた NETBlockの集合から逃げられるようにしたい場合には,各レベルにおいて KeepNETObjectを呼び出す必要がある.もう1つの方法として,KeepNETObject[obj, Manual]を呼び出すことができる.ここで引数は,囲んでいるどのNETBlockによってもそのオブジェクトが解放されるべきではないということを .NET/Link に伝える.そのようなオブジェクトが解放されるのは,手動でオブジェクトに対してReleaseNETObjectを呼び出した場合だけである.

KeepNETObject[obj1,obj2,...]NETBlockが終了する際に特定のオブジェクトを解放しない
KeepNETObject[obj1,Manual]任意のNETBlockが終了する際に特定のオブジェクトを解放しない

NETBlockの終了後も.NETオブジェクトを保持する.

以下はKeepNETObjectを使って,2つのオブジェクトを解放することなく返すことができるようにする例である.

Click for copyable input

BeginNETBlockおよびEndNETBlockを使って,2つ以上の評価を通じてNETBlockと同じ機能を提供することができる.EndNETBlockMathematica に前回のマッチするBeginNETBlock以来返された新しい種類の.NETオブジェクトをすべて解放する.これらの関数は開発中に,セッションに印を設定し,作業を行った後で,その時点以来 Mathematica に返された新しい種類のオブジェクトをすべて解放したいときに,おもに使用される.BeginNETBlockおよびEndNETBlockはネストすることができる.すべてのBeginNETBlockはマッチするEndNETBlockを持つべきであるが,ネストされたレベルのEndNETBlockを持つような場合にでも, EndNETBlockを呼び出すことを忘れることは大きなエラーとはならない.単にオブジェクトをいくつか解放できなくなるというだけのことである.

LoadedNETObjects

LoadedNETObjects[]は現在 Mathematica で参照されているすべての.NETオブジェクトのリストを返す.これにはNETNewで明示的に作成されたすべてのオブジェクトと,.NETのメソッドあるいはプロパティの結果として Mathematica に返されたすべてのものが含まれる.ReleaseNETObjectと一緒に,あるいはNETBlockを通して解放されたオブジェクトは含まれない.LoadedNETObjectsはおもにデバックのためのものである.作業中の関数の前後にこれを呼び出すと大変便利である.リストが大きくなると,関数は参照し忘れるので,NETBlockReleaseNETObjectの両方,あるいはどちらか一方の使用をチェックする必要がある.

Enum

.NETの列挙は特別な種類のクラスであり,列挙のそれぞれのメンバはそのクラスの静的定数フィールドとして表される. 列挙定数の値は整数であるが,.NET/Link はそれらを Mathematica に返すときに整数に変換することはしない.これはなぜならenumの値はおそらく別の.NETメソッドにもう1度返されることになるだけであるからである.だから,これらの値を Mathematica で整数として操作しようと考えることはほとんどあり得ない.この場合,Mathematica 内で単なる暗号的な整数値としてよりもクラスのオブジェクトとして表す方がもっと意味のあることなのである.

形の大きさを変えるときに1つ以上の辺に対して固定された位置に形を置くために,しっかり固定したいButtonオブジェクト btn があるとしよう.これを行うためには,ボタンのAnchorプロパティをAnchorStylesのenumからの値に設定する.まず型の静的メンバにアクセスしたい場合には必ず必要になるAnchorStyles型をロードする.

In[16]:=
Click for copyable input
Out[16]=

他の任意の静的フィールドと同じように,このenumのメンバを参照する.

In[17]:=
Click for copyable input
Out[17]=

enumは Mathematica では,生の整数値よりもプログラマーにとってはもっと意味のある強く定型化されたオブジェクト参照で表される.この場合の整数値は1である.NETObjectToExpression関数を使ってオブジェクト参照をその整数値に変換することができる.

In[18]:=
Click for copyable input
Out[18]=

今度はこれをボタンに適用する.

In[19]:=
Click for copyable input

enumとして定型化された任意の引数については,enumクラスのインスタンス,あるいは生の整数値を渡すことができる.つまり,上の行はもっと理解しがたいものにはなるが,以下のように書くことも可能である.

In[21]:=
Click for copyable input

enumsの中には,その値をビット論理和で結合させることが可能であることを示す[Flags]属性を持つものもある.AnchorStylesのenumは,要素を親コンテナの2つ以上の辺に固定させたい場合もあるので,この属性を持つ.C#ではどのように書かれるのかを以下の例で示す.

これを Mathematica で行うためには,enumの整数値を得て,NETObjectToExpressionを使えるようにする必要がある.(これがおそらくenumの値を Mathematica で整数として操作したい唯一のケースである.)

In[20]:=
Click for copyable input

Out」と「Ref」のパラメータ

.NETはパラメータが参照によって渡せるようにして,その値への変更が呼び出した人にもう一度帰っていくことができるようにする.このような「参照による」パラメータが.NET Frameworkのクラスの中で使われることはめったにないが,サードパーティライブラリの中にはこれをもっと頻繁に使うものもある.C#表記では,このようなパラメータはoutあるいはrefとして印を付けられる.この2つの違いは,refパラメータはメソッドの開始時に初期値が必要であるのに対して,outパラメータでは必要ないという点にある.Visual Basic .NETでは,キーワードByRefを使ってパラメータが参照によって渡されたことを示す.Visual BasicにおけるByRefパラメータはC#におけるrefパラメータのようなものである.Visual Basicでは,outのみのパラメータという考え方は存在しない.IDL表記では,refパラメータは[in, out]と書かれ,outのみのパラメータは[out]と書かれる.

以下はSystem.Uriクラスからのメソッドでのrefパラメータの例である.

このメソッドは文字列(patternパラメータ)と始動indexを取り,必要な場合は%xx形式の16進数表記を解読して文字列内の次の文字を読み取る.またindexの値を解読された文字の最後の少し後ろに進める.refあるいはoutのパラメータを使用するほとんどのメソッドと同様に,このメソッドは2つ以上の情報,つまり解読された文字と文字列の次の位置を返す必要がある.indexパラメータの初期値はメソッドによって使用されるため,ただ単にoutパラメータではなく,refパラメータでなければならない.

outあるいはrefのパラメータ付きのメソッドを,C#およびVisual Basic .NETを含めたほとんどの.NET言語から呼び出すのと全く同じ方法で,Mathematica からも呼び出す.refパラメータについては,正しい型(この場合は整数)の初期値を持つシンボルと一緒にメソッドを呼び出す.メソッドが返されると,シンボルに新しい値が割り当てられる.

以下は%20文字(URLでの空白文字[decimal 32]によく使われる符号化)を解読する.

In[50]:=
Click for copyable input
Out[52]=

posrefパラメータのスロットに渡されたため,新しい値,つまり%20の後ろにある最初の文字の指数の値が割り当てられる.

In[53]:=
Click for copyable input
Out[53]=

refパラメータを呼び出すときによく犯される間違いに,初期値を割り当てることを忘れるということがある.以下はこの誤りの例である.

In[54]:=
Click for copyable input
Out[55]=

パラメータがrefではなくoutと印を付けられている場合には,初期値は無視される.したがって,シンボルがメソッドに入ったときの値は,値があったとしても,関係ない.上でも述べたように,Visual Basic .NETではoutのみのパラメータという考え方は存在しない.そのため,Visual Basicで書かれたメソッド内のByRefパラメータは,メソッドが入ってくる値を使わない場合でも,常に正しい型の初期値を必要とする.

Visual Basic .NETと同様に(しかしC#とは違って) .NET/Link ではシンボルの代りに文字通りの値をoutあるいはrefパラメータに渡すことができる.このような場合,パラメータの値になされた変更はすべて失われる.以下はその例である.

In[56]:=
Click for copyable input
Out[56]=

「値で」そして「参照で」オブジェクトを返す

参照と値

.NET/Link は特定の Mathematica 式とその対応する.NET 式の間にマッピングを提供する.これはつまり Mathematica と.NETとの間を移動する際に,これらの Mathematica 式とその対応する.NET式がお互いに相手の形式に自動的に変換されるということである.例えば,.NETの整数の型(Int32Int16Byte等)は Mathematica の整数に変換され,.NETの実数の型(SingleおよびDouble)は Mathematica の実数に変換される.もう1つのマッピングでは,.NETオブジェクトは Mathematica式に変換される.これらの式は.NET オブジェクトへの参照である.つまり,これらは Mathematica の中では .NET/Link で操作されるということを除いて何の意味も持たない.しかし,.NET オブジェクトの中には Mathematica の中で重要な値を持つものもあり,これらのオブジェクトはデフォルトで値に変換される.このようなオブジェクトの例は文字列と配列である.

それでは,.NETオブジェクトはいくつかの特別なケースを除いてデフォルトで Mathematica に「参照で」返されると言える.これらの特別なケースには数字,文字列,配列,そしてブーリアンがある.これらの例外的なケースは「値で」返されると言える.「.NETと Mathematica の間で型を変換する 」に掲載の表では,これらの特別な.NETオブジェクトの型がどのように Mathematica の値にマップされるかを示している.

要約すると,Mathematica で意味のある値の表記を持つ.NETオブジェクトはすべてこの値に変換される.これは単にそれが最も便利は動作であるからである.しかし,このデフォルトの動作をオーバーライドしたいと思う場合もときにはあるかも知れない.オーバーライドしたい理由として最も一般的なものに,MathLink 上で大きな式の不要なトラフィックを避けたいということがある.

ReturnAsNETObject[expr]expr で返される.NETオブジェクトは参照の形式である
NETObjectToExpression[obj].NETオブジェクトの値を Mathematica 式として与える

「参照で」と「値で」のコントロール.

ReturnAsNETObject

クラスMyClassarrayAbs()という名前の静的メソッドがあり,このメソッドがdoubleの配列を取って,それぞれの要素が引数配列において対応する要素の絶対値である新しい配列を返す場合を考えてみよう.C#構文によるこのメソッドの宣言はしたがってdouble[] ArrayAbs(double[] a)のようになる.以下はこのようなメソッドを Mathematica から呼び出した場合である.

In[1]:=
Click for copyable input
Out[2]=

上の例がおそらくメソッドが作動するようにしたい場合に取る方法であろう.つまり Mathematica リストを渡してリストをもらうという方法である.今度はArraySqrt()という名前のメソッドで,Abs()関数の代りにSqrt()関数を実行することを除いてはArrayAbs()のように作動する別のメソッドを考えてみよう.

In[3]:=
Click for copyable input
Out[3]=

この計算では,もとのリストは MathLink を通して.NETに送られ,.NET配列がこれらの値を使って作成される.その配列はArrayAbs()への引数として渡され,ArrayAbs()自体が別の配列を作って返す.その後この配列はリストを作成するために MathLink を通して Mathematica に送り返され,リストはすぐにArraySqrt()の引数として.NETに送り返される. 配列データを Mathematica に送り返すのは時間の無駄であることは明らかである.というのは,もうすでに.NET側に完璧な配列(ArrayAbs()メソッドによって返された配列)が存在していて, ArraySqrt()に渡されるばかりになっているにもかかわらず,代りにその内容と同じ値を持つ新しい配列として再び.NETにすぐ返すためだけに Mathematica へ送り返すことになるからである.この例では,そのコストはたいしたことがないが,これが20万個の要素を持つ配列であったらどうであろうか.

必要なのは,配列データを.NETに留めた状態で,実際のデータ自体ではなく配列への参照だけを返すことができる方法である.これはReturnAsNETObject関数で行うことができる.

In[4]:=
Click for copyable input
Out[4]=

以下はReturnAsNETObjectを使った計算がどのようになるかを示している.

In[5]:=
Click for copyable input
Out[5]=

上ではArraySqrt()Mathematica の実数リストである引数と一緒に呼び出されていたが,ここではdoubleの一次元配列である.NET オブジェクトへの参照と一緒に呼び出されている.すべての引数は Mathematica の値,あるいは適切な型の.NET オブジェクトへの参照と一緒に Mathematica から呼び出すことができる.

要約すると,ReturnAsNETObject関数は通常 Mathematica の値に変換されるオブジェクトを返すメソッドとプロパティが代りに参照を返すようにする.これは Mathematica と.NETの間で多量のデータが不必要に渡したり渡されたりすることを避けるための最適化として使用されることが多く,そのため大変大きな配列や文字列に主として役立つ..NET のほとんどの型のオブジェクトは Mathematica の「値で」の表示では意味を持たず,常に「参照で」返されるものである.ReturnAsNETObjectはこれらの場合不必要である.

NETObjectToExpression

前のセクションでは,ReturnAsNETObject関数を使って通常 Mathematica に値で返されるオブジェクトを参照で返されるようにする方法を見た.これと反対の操作を行う関数,つまり参照を取ってそれをその値の表現に変換する関数が必要である.これを行うのが関数NETObjectToExpressionである.

Mathematica で意味のある「値」を持つような.NETオブジェクトを扱っている場合には,Mathematica に送られたときにオブジェクトは自動的にその値にほとんどかならず変換される.しかしこの規則にも例外がいくつかあり,そのような場合に便利なのがNETObjectToExpressionである.上でReturnAsNETObject関数を使ってオブジェクトを強制的に参照として返すことができることを見たが,参照を得るもう1つの方法として,NETNewあるいはMakeNETObjectを呼び出す方法がある.これらの関数は常にオブジェクト参照を返すからである.以下でStringオブジェクトを明示的に作成する.

In[1]:=
Click for copyable input
Out[1]=

以下は文字列参照を Mathematica 文字列へ変換する.

In[2]:=
Click for copyable input
Out[2]=

オーバーロードされた演算子 」のセクションでMakeNETObject関数を紹介するが,この関数は,NETNewを使う場合よりも簡単に,Mathematica の文字列,数字,配列から.NETオブジェクトを構築することができる.

NETObjectToExpressionも列挙およびコレクション(ICollectionインターフェースを実装するオブジェクト)のように通常参照で返されるオブジェクトの型をその値の表現に変換する.列挙の型については上の「 Enum 」セクションで説明されている.コレクションは Mathematica 上ではリストとして有効に操作されることができるが,配列と違ってコレクションは反復するのに高くつくことがあるため,.NET/Link はそれらを参照として残し,自動的にリストに変換するということはしない.リストが必要な場合は,NETObjectToExpressionを使うとよい.

以下はコレクションオブジェクトを作成する:

In[1]:=
Click for copyable input
Out[1]=

今度はこれに値を投入する.

In[2]:=
Click for copyable input

NETObjectToExpressionはオブジェクト参照をリストに変換する.

In[3]:=
Click for copyable input
Out[3]=

オーバーロードされた演算子

.NET言語の中には,+,>等のオーバーロードされた演算子をクラスに定義することができるものもある..NET言語では演算子のオーバーロードをサポートすることは必要ではない. C#とC++ではサポートすることができるが,Visual Basic .NETではできない.数多くのオーバーロードされた演算子を定義するクラスの例としてSystem.TimeSpanがある.例えば,これは+演算子を定義して,2つのTimeSpanオブジェクトを数字であるかのように足すことができる.以下はC#コードでの例である.

.NET言語でオーバーロードされた演算子をサポートすることは必須ではないので,これらの演算子を定義するクラスは常に同じ操作を別の方法でも行うことができるようにしておくべきである.一般的にこれはメソッドの呼出しを通して行われる.TimeSpanクラスは,オーバーロードされた演算子をサポートしないVisual Basic .NETのような言語で使えるAdd()メソッドを提供する.

.NET/LinkMathematica 構文でのオーバーロードされた演算子はサポートしないので,同じ操作を行うメソッドを探す必要がある.以下は2つのTimeSpanオブジェクトを足すためのAdd()メソッドである.

In[63]:=
Click for copyable input
Out[66]=

クラスの作成者が.NETのガイドラインを無視して,オーバーロードされた演算子としての操作と同じ操作を行う別のメソッドを提供しなかったような場合でも,その操作を Mathematica から呼び出すことは可能である.これはC#およびC++でのオーバーロードされた演算子は,XXXが操作の名前であるop_XXXのような名前を持つ特別な静的メソッドを通して内部で作成されるからである.クラスの作成者がこれらのメソッドを直接書くのではなく,コンパイラが作成するのである.しかし,これらのメソッドは他の任意のメソッドと同様に Mathematica から直接呼び出すことができる.以下はTimeSpanクラスの中にあるあいまいな名前付きのメソッドすべてである.

TimeSpanクラスの中にAdd()メソッドがないような場合にでも,op_Addition()メソッドを呼び出して一緒に混合させることができる.Mathematica から.NETの名前を呼び出す場合は常にアンダースコアは Mathematica シンボルの中では適切な文字ではないので,アンダースコアの文字をUにマップしなくてはならないことに注意されたい.

In[62]:=
Click for copyable input
Out[62]=

キャスティング

はじめに

.NETプログラムにはよくキャストが含まれる.キャストでは1つの型のオブジェクトが別の型のものに変換される.典型的な例は,プログラマーがおそらくを返すためにタイプされたメソッドの呼出しの結果得られるような型の変数を持ち,その型からのメソッドが変数を呼び出せるように変数を生成された型にキャストしたいと考えるような場合である.これはコレクションクラスを扱っているときによく起る.というのは,コレクションクラスは任意の型のオブジェクトを持つことができて,そのメソッドは以上に特定のものを返すためにタイプするのではないからである.以下はプロパティのシグナチャであり,これは特定の指数でオブジェクトをリストから抽出する.

C#ではプロパティはクラスのインデクサなので,上記の珍しい構文で書かれるか,あるいはVisual Basicでのようにという名前のプロパティとして書かれるかすることができる. 例えば,文字列をに入れて,その後プロパティを使って1つ文字列を抽出するような場合には,その文字列を文字列変数に割り当てられるように文字列にキャストする必要がある.

Visual Basic .NETではキャストは関数を使って行われ,が設定される場合にのみ必要である.

この型のキャストは継承階層を下向きに(親の型から派生型へ)キャストしているためダウンキャストと呼ばれる.このようなダウンキャストはおそらくキャスティングの中では(からへの変換のように,1つの型の数字を別の型のものに変換するために使用されるキャスティングを除いては)最もよくある形のキャスティングである.

.NET/Link で複製しようと思っているようなC#およびVisual Basic .NETのプログラムの中全体にダウンキャストが散らばっているのを見かけるが,.NET/Link ではダウンキャストが重要になることはほとんど全くない.これは参照の型同士の間でのキャスティングはおもにコンパイルのときに行われる操作だからである.上記のサンプルコードでは,プログラマーはコンパイラにたった今から抽出したオブジェクトは文字列であることは分かっていて,コンパイラにそのようにプログラマーがオブジェクトを取り扱えるようにして欲しいと伝えている.しかし,.NET/Link ではオブジェクトは常にその真のランタイムの型として Mathematica に返されるので,プロパティを呼び出すときに,型のオブジェクトを受け取るのである. プロパティがを返すためにタイプされているということは全く意味を持たない.実際,すべてのオブジェクトはその真のランタイムの型を持っていて,継承階層の下のレベルにはオブジェクトをキャストする型が存在していないので, .NET/Link ではダウンキャストは意味をなさなく不可能でさえある.

この前置きの目的は,.NETプログラムで見かけるキャストの大部分が .NET/Link では無意味であるということをはっきりさせることにある.キャストは,数的な型の間の変換(これはもしも必要になったとしても,Mathematica では別の方法で容易に行うことが可能である),あるいはのような一般的な型からもっと特定の型へのダウンキャスト(これはコンパイラのために行われることであり,.NET/Link ではコンパイルの段階はないので意味がない)のいずれかである.

しかし .NET/Link 内でキャストが必要になる場所もある.その1つがCOMオブジェクトを使用する場合である.これは「COMを Mathematica から呼び出す」セクションで触れるので,ここでは扱わない.その他の .NET/Link でキャストが必要な場合はすべてアップキャストである.ここでは,オブジェクトは親のクラスかインターフェースにキャストされている.アップキャストが必要なのは次の3つの場合である.

  • メソッドの隠れた親クラスの実装を呼び出すため
  • いわゆる「明示的なインターフェース実装」を使って書かれたメソッドを呼び出すため
    • publicインターフェースを実装するprivateクラスについてのメソッドを呼び出すため

    上のような状況は比較的稀な状況で,多くのプログラマーはこれらの状況に出会うことは全くない.これらは.NETプログラミングの重要な面ではあるが, .NET/Link によってさらに複雑性が導入されるというようなことはない.まさにC#,Visual Basic .NET,そしてその他の.NET言語でアップキャストが必要となる場合であるからである.これら3つの場合については別々に以下のセクションで触れる.

    .NET オブジェクト参照をキャストする関数はCastNETObjectである.以下のセクションにその例を挙げる.CastNETObjectは新しいオブジェクト参照ではなく,現存のオブジェクト参照の「エイリアス」だけを作成する.つまり,オブジェクトに対してReleaseNETObjectを呼び出すと,そのオブジェクトとオブジェクトにキャストされた参照すべてが解放される.言い換えれば,オブジェクトとそのキャストされたバージョンは同じオブジェクトを別の方法で見ているだけであって,別々の参照を見ているわけではないということである.

    CastNETObject[obj,"type"]文字列として指定して,オブジェクト obj を特定の型にキャストする
    CastNETObject[obj,NETType]式として指定して,オブジェクト obj を特定の型にキャストする

    式をキャストする.

    CastNETObject関数は .NET/Link 1.1で導入された.

親クラスから隠れたメンバを呼び出す

子クラスは,newキーワード(C#)あるいはShadowsキーワード(Visual Basic .NET)を使って同じ名前を持つメンバを宣言することによって,その親クラスのメンバを隠すことができる.次のクラスを考えてみよう.

Childクラスのインスタンスを持っていて,Foo()Parent実装を呼び出したいとすると,ChildインスタンスをParentクラスにキャストすることによってこれを行うことができる.

この動作は,Foo()メソッドがvirtualと宣言されたか,それともParentクラスにないと宣言されたかにかかわらず同じである.newキーワード(Visual Basic .NETではShadows)は,もしも忘れるとコンパイラが警告を生成するが,厳密には必要ではない.

.NET/Link を使ってFoo()Parentクラス実装を呼び出すためには,CastNETObjectを使ってオブジェクトをParentクラスにC#コードでなされたのと全く同じ方法でキャストするとよい.

In[1]:=
Click for copyable input
Out[1]=
In[2]:=
Click for copyable input
Out[2]=
In[3]:=
Click for copyable input
Out[3]=

もちろん2つの参照は同じオブジェクトを参照する.

In[4]:=
Click for copyable input
Out[4]=

明示的インターフェースの実装

クラスが2つのインターフェースを実装し,そのそれぞれが同じ名前のメソッドを持つ場合,それぞれのインターフェースメンバに別々の実装を与えることを選ぶことも可能である.これは明示的インターフェースの実装と呼ばれる.この方法は一般に2つのインターフェースからのメソッドが概念的に全く異なっていて,両方のインターフェースのコントラクトを満足するような単独の実装を提供することができない場合にのみ使用される.以下は.NET SDKのドキュメントからの例を簡約したものである. Boxクラスはおよびを実装し,この両方がLength()メソッドを持つ.もちろん両方のインターフェースに使えるような単独のLength()の実装はない.

これはこれらのメソッドを呼び出す方法である.Length()メソッドを呼び出す前に,オブジェクトをあるいはのインターフェースにキャストしなくてはならない.

以下は同じことを .NET/Link を使って行った場合である.

Click for copyable input

以下は実例である.Arrayクラス(これは.NETのすべての配列に対する親クラスである)はIListインターフェースからのメソッドに明示的インターフェースの実装を使う.Arrayクラス等のクラスがそのメソッドのいくつかに明示的インターフェースの実装を使う場合には,ドキュメントの中でこれははっきり述べられているべきである.IListインターフェースからのメソッドの1つにContains()がある.ArrayIListを実装するにもかかわらず,Contains()を直接配列オブジェクトについて呼び出すことはできない.

In[6]:=
Click for copyable input
Out[6]=
In[9]:=
Click for copyable input
Out[9]=

IListにキャストするとうまくいく.

In[10]:=
Click for copyable input
Out[10]=

privateクラスとpublicインターフェース

.NET/Link でアップキャストが必要な最後のケースは,インターフェースを返すようにタイプされたメソッドがあって,そのメソッドの実装がインターフェースを実装する非publicのクラスのオブジェクトを返す場合に起り得る.これは完全に合法的であるが,.NET/Link. にとっては問題となり得る状況である.そのすべてのpublicメンバを獲得するために非publicクラスをリフレクトする(.NET/Link はすべてのメソッドをリフレクションを通して呼び出す)と,publicの親クラスのいくつかによって実装されているメソッドしか見ることができない.クラスがpublicインターフェースを実装するからと言って,それらのメソッドをそのクラスのインスタンスに対して呼び出すことができるということにはならないのである.クラス自体がpublicでない場合,そのメソッドがpublicであっても,publicの親クラスあるいはインターフェースとしてタイプされたクラスのインスタンスに対してのみメソッドを呼び出すことができるのである.

これらの説明は分かりにくいかも知れないので,次の例を考えてみよう.IFooを呼ばれるインターフェースとIFooを実装する内部クラス(内部クラスは同じアセンブリ内の別の型に対してのみ可視である)を想定する.

今度は,InternalIFooImplクラスのインスタンスを返すようなIFooを返すメソッドの型を持つクラスがいくつか他にあるとする.

In[1]:=
Click for copyable input
Out[2]=

Foo()メソッドは非publicクラスとしてタイプされているので,このメソッドをfooオブジェクトに対して呼び出してもうまくはいかない.

In[3]:=
Click for copyable input
Out[3]=

上のエラーメッセージはあまり正しいものではない. Foo()という名前のpublicメソッドがInternalIFooImplクラスにあることはあるのだが,クラス自体がpublicではないので,リフレクションを通して見ることができないのである.このようなことはC#あるいはVisual Basic .NETでは決して起らない.CreateIFoo()の結果を持つ変数はpublicインターフェースIFooとしてタイプされるからである.プログラマーはInternalIFooImplクラスを見る必要も,それについて知る必要もまったくない.コードは以下のようになる.

しかし .NET/Link では,オブジェクトはデフォルトでその真のランタイムの型として見られるので,非publicクラスとしてタイプされるオブジェクトのインスタンスが起る結果となる.これを解決するには,C#とVisual Basic .NETで行われていることを行わなくてはならない.つまり,オブジェクトをIFooインターフェースにアップキャストするのである.

In[3]:=
Click for copyable input
Out[4]=

上の例の「ファクトリ」デザインパターンは比較的よくあるものである.特別なオブジェクト作成メソッドは,あるインターフェースとしてだけタイプされるオブジェクトを返す.これでライブラリのデザイナはインターフェースのみをドキュメント化して,privateクラスの実装詳細を隠すことができる.ライブラリのクライアントはインターフェースのみに書き,実装クラスの実際の名前のような詳細から完全に切り離された状態で置かれる.つまり,非publicクラスをインターフェースの型にアップキャストする必要が .NET/Link プログラマーにとってまったくないというわけではないことが分かる.しかし実際には,非publicクラスがpublicの親クラスから少なくともそのメソッドのいくつかの実装を継承していることが多い.そのような場合には,それらのメソッドが見付けられ,キャストしなくても非publicの子クラスに対して呼び出されることが可能である.

インデクサ

.NET クラスの中には,配列と同じ方法でクラスのインスタンスにアクセスできるようにする特別のメンバを定義するものがある.この特別なメンバは,C#の用語ではインデクサ,Visual Basic .NET の用語ではデフォルトのパラメータ化されたプロパティと呼ばれる.以下はC#およびVisual Basic .NETのメンバ例のスケルトン定義である.

インデクサはクラスが配列ではない場合でもそうであるかのように動作できるようにする..NET コレクションクラス(ICollectionインターフェースを実装するクラス)の大部分は,要素を設定して単純な配列のような構文を使って取り出すことができるように,インデクサをサポートする.クラスが上の定義のどれかのような定義を持つ場合は,「i 番目の要素」に以下のようなコードを使ってアクセスすることができる.

クラスがインデクサを定義する場合は, Mathematica で関数用の角カッコを使ってインデクサを呼び出すことができる.

Click for copyable input

上のコードにはメソッド名もプロパティ名もない.オブジェクト自体の「引数」が関数であるかのように存在しているだけである.Partベースの構文(つまりobj[[0]]を使う構文)の方が,配列アクセスの際に Mathematica で使う同等のものであるので,.NET/Link のインデクサを呼び出すのにふさわしいが,この構文は哲学的理由と技術的理由から拒否されたのだという論議をすることができるかも知れない.

以下はBitArrayクラスを使ってインデクサを呼び出す別の例である.このクラスは大変コンパクトな方法で保持されている/の値の集合である.このクラスがインデクサを定義して配列のように扱えるようにする.

In[1]:=
Click for copyable input
Out[1]=

以下ではインデクサを呼び出して第2の要素を得る.(これがゼロベースの指数だからである.)

In[2]:=
Click for copyable input
Out[2]=

C#でクラスを書いてそれにインデクサを与えると,コンパイラがItemという名前のpublicプロパティを作成してくれる.これはパラメータ化されたプロパティである.つまり,メソッドの呼出しのような引数を取る.インデクサ構文はItemプロパティを呼び出すための省略表現にすぎない.Visual Basic .NETで書く場合には,慣例ではデフォルトのパラメータ化されたプロパティは通例Itemと名付けられるべきであるとされているが,これは必要条件ではない.デフォルトのパラメータ化されたプロパティにどのような名前を付けたとしても,インデクサのスタイルの構文を使わずに,希望するならば,Mathematica から直接プロパティを呼び出すこともできる.

In[3]:=
Click for copyable input
Out[3]=

例外

例外が処理される方法

.NET/Link は.NETの例外を自動的に処理する.捕獲されなかった例外が任意の呼出し中に.NETに投げられる場合は,Mathematica でメッセージが返される.以下は実数を整数としてフォーマットしようとする例である.

In[26]:=
Click for copyable input
Out[27]=

例外が投げられると,呼出しの結果は$Failedになる.

.NETコードがデバッグの情報を含めてコンパイルされると,例外の結果として得られる Mathematica メッセージは,それぞれのファイルに厳密な行番号を入れて,例外が起った点までをたどる完全なスタックを示す.

GetNETException

関数GetNETExceptionを使って Mathematica から.NETへの最終の呼出しに投げられた例外のExceptionオブジェクトを得ることができる.例外が投げられない場合はNullを返す.ほとんどのプログラマーはこの関数を使う必要がないが,プログラム中の特別な例外を処理する機能を実装するためにこれを使うことが可能である.以下ではInt32.Parse()への前回の呼出しで例外が投げられている.発生する例外のほとんどは「内部例外」の 標準.NETデザインパターンを使って投げられた実際の例外をラップする特別のオブジェクトでラップされて戻ってくることが分かる.

In[28]:=
Click for copyable input
Out[28]=

実際の例外が投げられるようにするためには,InnerExceptionプロパティを調べる必要がある.

In[29]:=
Click for copyable input
Out[29]=

この例では,.NET リフレクションシステムが例外を別の例外でラップしているので,「本当の」例外を見るためには,もう1レベル深く掘る必要がある.

In[30]:=
Click for copyable input
Out[30]=

カスタム例外の処理

非常に上級のプログラマーの中には,例外を処理したり報告したりするための自分のシステムを実装したいと考える方がいらっしゃるかも知れない.例えば,.NET 言語で使用されるのと同じプログラミングスタイルを使って, Mathematica コード内の.NET 例外を MathematicaThrowおよびCatchの関数で処理したい場合もあるかも知れない.さらに簡単な例としては,特定のコードブロック内の例外メッセージを黙らせたい場合が考えられる.

Mathematica におけるカスタムの.NET例外処理を実装するためには,シンボル$NETExceptionHandlerを使うとよい.$NETExceptionHandlerの値は,3つの引数を渡される関数として取り扱われる.この引数とは,メッセージに関連したシンボル(これは通常シンボルNETである),メッセージタグ(これは典型的には文字列"netexcptn"である),メッセージに関連したテキストの記述的文字列の3つである.

通常は$NETExceptionHandlerBlock内に設定して,メッセージを黙らせる次の例にあるように, $NETExceptionHandlerの影響がきっちりと定義されたコードのセグメントに限られるようにする.

Click for copyable input

ハンドラ関数内でGetNETExceptionを使うと,投げられた実際の.NET例外を得ることができる.以下はその例である.

Click for copyable input

$NETExceptionHandlerBlockの外に設定することは避けた方がよい.$NETExceptionHandlerの値がクリアされず,ユーザがどうして自分の思ったとおりに例外が処理されないのか分からず首をかしげるような状態をうっかりと作成してしまうことは確実だからである.

ネストされた型

.NETの型の中には別の型の宣言が型の中にネストされているものがある.以下はC#での例である.

.NETでは,+の文字は内側のクラスの名前をその外側のクラスから離すために,型の名前に使われる.上の例では,Innerクラスの実際の型の名前はSomeNamespace.Outer+Innerである.この名前をLoadNETTypeで使わなければならない.

In[1]:=
Click for copyable input
Out[1]=

また型の名前は,NETNewでも使われる.以下はInnerクラスのインスタンスの構築方法を示している.

In[2]:=
Click for copyable input
Out[2]=

+の文字は型の名前にのみ現れる.ネストされた型をコードで参照する場合は,標準スコープ解決演算子(C#およびVisual Basic .NETではピリオド)を使って内側のクラスを外側のクラスから離すとよい.

ほとんどの.NET言語で,ネストした型についての上の構文を使うことができるが,実際の型の名前は+の文字を使って,内側の型を外側の型から離すことに注意されたい..NET/Link では,型の名前を文字列として入力する場合は必ず+表記を使う必要がある.

ネストされた型のオブジェクトに対するインスタンスメソッドは,通常の方法で呼び出す.

In[3]:=
Click for copyable input
Out[3]=

以下は静的メンバをどのようにして呼び出すかを示している.C#およびVisual Basic .NETの場合と同じように,+の文字が消えてスコープ解決演算子(Mathematica では)に取って代られる.

In[4]:=
Click for copyable input
Out[4]=

上記の行についてもう1つ注意しなくてはならない点は,Inner`StaticInnerFoo[]でのように,内側のクラス名を単に使ってネストされたクラスの静的メンバを参照することはできないということである. 他の.NET言語においてもこれを行うことはできないので,これは当然予期されることである.必ず外側のクラス名を内側のクラス名の前に付ける必要がある.

以下は実例である.System.Environmentクラスは,SpecialFolderという名前のネストされたenumを持つ.このenumは,Windowsのオペレーティングシステム内の特別な場所を指定する定数(ProgramFilesRecentSystemStartMenu等を含む値を持つ)を含む.以下はユーザのFavoritesフォルダへのパスの決定方法である. System.EnvironmentクラスとSystem.Environment.SpecialFolderのenum(enumのメンバは静的)から静的メンバを呼び出す必要があるので,まずこれらの2つの型をロードする.型の名前に+が付いていることに注意されたい.

In[5]:=
Click for copyable input

GetFolderPath()メソッドはSpecialFolder列挙のメンバを取り,適切なパスを文字列として得る.SpecialFolderのenumのFavoritesメンバをどのように参照しているかに注意されたい.

In[7]:=
Click for copyable input
Out[7]=

要約すると,ネストされた型について注意しなくてはならないことのうちで最も重要な点は,文字列内でネストされた型の名前を参照しなくてはならない場合に,+の文字を使って内側の型から外側の型を切り離すということである.コードで型を参照する場合は,使い慣れた「」を使って外側と内側の名前を切り離す.

MakeNETObject

.NETオブジェクトを作成するときに最もよく使われる方法は,NETNewを通してコンストラクタを呼び出す方法である.しかし,.NETオブジェクトに変換したい Mathematica 式があるのだが,クラスが便利なコンストラクタを持っていないという場合もある.よくある例として,Mathematica リストから配列オブジェクトを作成したい場合がある.NETNewを通して配列コンストラクタを呼び出すことは可能だが,コンストラクタを通して値を持つ配列を初期化することはできない.以下の例ではNETNewで配列オブジェクトを作成し,それを手動で埋める.

In[1]:=
Click for copyable input
Out[1]=
In[2]:=
Click for copyable input

MakeNETObjectを使うと,これをもっと簡単に行うことができる.

In[5]:=
Click for copyable input
Out[5]=
MakeNETObject[val]Mathematicaval(数字,文字列,リスト等)を表すために適切な型のオブジェクトを構築する
MakeNETObject[val,type]指定の型のオブジェクトを構築する

MakeNETObject

MakeNETObjectを呼び出さなくてはならないことはほとんどない.例えば,配列を取るメソッドを呼び出す場合に,単に Mathematica リストを渡せば,.NET/Link が.NET配列を作成してくれる.しかし,Mathematica からのデータを投入しなくてはならない.NETオブジェクトを明示的に作成したいが,便利なコンストラクタがないという場合がある. MakeNETObjectが有用である状況の例は次のようなメソッドである.このメソッドは引数として渡されるリストを逆にする.これは逆にされたリストを返すのではなく,その場で逆にするだけである.

このメソッドは Mathematica リストで呼び出すことができるが,逆にされたリストを取り返すことはできない.この問題を回避するには,初期リスト値を投入された配列オブジェクトを作成してオブジェクト参照を渡し,その内部データを逆にしてからオブジェクト参照を Mathematica リストに変換し直すという方法がある.

In[7]:=
Click for copyable input
In[8]:=
Click for copyable input
Out[8]=

その他にMakeNETObjectが有用となるのは,オーバーロードされたメソッドの正しいシグナチャを選ぶ際に .NET/Link を少し助ける必要があるような場合である.次のようなメソッドの2つのオーバーロードを見てみよう.

Mathematica から整数でFoo()を呼び出すと,longパラメータを持つオーバーロードが呼び出される.これは,.NET/Link が一般にそれぞれのスロットに最も範囲の広い可能な型を持つメソッドを呼び出そうとする(しかし,特に複雑な場合には公式の保証は何もない)からである.byteバージョンを呼び出したい場合は,Byteの型の.NETオブジェクトを作成することによってこれを行うことができる.これは,.NET/Link が常に入ってくる引数の型に厳密にマッチするようなメソッドシグナチャを優先するからである.

Click for copyable input

MakeNETObjectはほとんど使用されることがない関数である.Mathematica の文字列,配列等から明示的に.NETオブジェクトを構築する必要はない. 単にこれらのオブジェクトを.NETメソッドに渡すだけで,.NET/Link がこの操作を自動的にやってくれる.上に述べたようないくつかの特別な状況ではこの関数が有用になる.

複素数

.NETの数の型(例えば,byteintdouble)は,Mathematica に整数および実数として返され,整数と実数は.NETに引数として送られるときに適切な型に変換される.複素数の場合はどうだろうか.Mathematica Complexの型に直接マップされた複素数を表すような.NETの型があると,Mathematica と.NETの間で複素数が渡したり渡されたりしたときに自動的に変換されるので便利である..NETには複素数用の標準の型がないので,.NET/Link でこのマッピングに参加させたい型に名前を付けることができる.

SetComplexType["classname"]Mathematica の複素数にマップされるようにクラスを設定する
GetComplexType[]複素数に現在使用されているクラスを返す

複素数用の型を設定する.

以下のようなプロパティを持つ限り,どのようなクラスあるいはストラクトを使ってもよい.

  • 2つのdoubleあるいは2つのfloat(実部,虚部の順で)を取るpublicコンストラクタ

.NETコンソールウィンドウ

.NET/Link は.NETの「コンソール」ウィンドウを表示する便利な方法を提供する.標準のConsole.OutおよびConsole.Errorのストリームに書かれた出力はすべてこのウィンドウに導かれる.診断情報をコンソールに書く.NETコードを呼び出している場合は,この出力をプログラムの起動中に見ることができる. ほとんどの .NET/Link の機能と同様に,コンソールウィンドウは Mathematica からでも.NETプログラム(.NETコードからのその使用については「Mathematica を.NETから呼び出す」に記載されている)からでも簡単に使用することができる. Mathematica からこれを使用する場合は,ShowNETConsole関数を呼び出すとよい.

ShowNETConsole[].NETコンソールウィンドウを表示してConsole.OutおよびConsole.Errorに書かれる出力を獲得し始める
ShowNETConsole["stream"].NETコンソールウィンドウを表示し,指定されたストリーム(Console.Outについては"stdout"で,Console.Errorについては"stderr")に書かれる出力を獲得し始める
ShowNETConsole[None]出力の獲得をすべて止める

コンソールウィンドウを示す.

In[1]:=
Click for copyable input
Out[1]=

出力の獲得はShowNETConsoleを呼び出すときにだけ始まる.ウィンドウが最初に現れるときには,このウィンドウには以前にConsole.OutあるいはConsole.Errorに書かれたかも知れないようなコンテンツは全く入っていない. ウィンドウがすでに開かれているときにShowNETConsoleを呼び出すと,ウィンドウが最前面に出てくる.

次の例では Mathematica から出力をいくつか書く.上記のShowNETConsole[]を実行すると,ウィンドウに"Hello from .NET"が出力される.

In[2]:=
Click for copyable input

このように Mathematica コードを使ってウィンドウに書くことを実演してみることは便利ではあるが,これは通常 Mathematica コードの代りに,コンソールに診断情報を書く.NET コードを使って行われることが多い.

.NET/Link を使うアプリケーションを配布する

このチュートリアルでは,Mathematica のアドオンを作成している .NET/Link 開発者に関係がある問題についていくつか触れる.

.NET/Link はアプリケーション開発者が.NETで一部の実装を行っているアプリケーションを流通させることが容易になるように設計されている.アプリケーションのディレクトリを正しく構築していれば,そのアプリケーションのユーザは Mathematica アプリケーションの任意の標準位置にそれをコピーするだけで,それをインストールすることができる.特に .NET/Link は,ユーザが特別な操作を行ったり,.NETランタイムを再起動したりしなくても,.NETアセンブリを見付けることができる.

Mathematica アプリケーションは,通常 Mathematica がそのディレクトリを見付けられるようないくつかの標準位置のうちのいずれかにインストールして,単独の(サブディレクトリ付きの)ディレクトリとして配備される.これらの標準位置は,$InstallationDirectory\AddOns\Applications$BaseDirectory\Applications$UserBaseDirectory\Applicationsのように書くことができる.ここで$InstallationDirectory$BaseDirectory$UserBaseDirectoryは,これらの組込みの Mathematica シンボルで与えられる場所を参照する.

.NET/Link アプリケーションには,.NET アセンブリあるいはレガシーWindows DLL(これは「Mathematica からDLLを呼び出す 」セクションで触れるように,.NETから呼び出すことができる)が含まれることがある.Mathematica アプリケーションが .NET/Link を使い,それ独自の.NETアセンブリを含む場合は,アプリケーションディレクトリ内に「assembly」サブディレクトリを作成するべきである.アプリケーションで必要なアセンブリならどれでも,この「assembly」サブディレクトリに置くことができる.レガシーWindows DLL(いわゆる「アンマネージドの」DLL)は,アプリケーションディレクトリのLibraries\Windowsサブディレクトリに置くとよい.

以下は .NET/Link を使うアプリケーションのディレクトリ構造の例である.

上のディレクトリ構造を使った場合でも,アプリケーションコードは明示的にそのアセンブリをロードする必要があることに注意されたい..NET Framework自体を生成するアセンブリ以外はすべて,「.NETのアセンブリと型をロードする」セクションで述べたように,使用する前に手動でロードされなければならない.アセンブリが正しい場所に置かれているということは,ファイル名かアセンブリ名だけが与えられたときに,LoadNETAssemblyがこれらのアセンブリを見付けることができるということを意味するにすぎない.

Click for copyable input

バージョン情報

.NET/Link ではバージョン情報を与える3つのシンボルを提供する.これらのシンボルは,Mathematica 自体でそれに対応するものと同じ型の情報を提供する.ただし,これらのシンボルはNETLink`Information`コンテキストにあり,$ContextPathにはないので,そのフルネームで指定する必要がある.

NETLink`Information`$Version完全なバージョン情報を与える文字列
NETLink`Information`$VersionNumber現行のバージョン番号を与える実数
NETLink`Information`$ReleaseNumberリリース番号(完全なx.x.xのバージョン指定の最終桁)を与える整数
ShowNETConsole[]コンソールウィンドウが .NET/Link アセンブリのバージョン情報を示す

.NET/Link バージョン情報.

In[1]:=
Click for copyable input
Out[1]=
In[2]:=
Click for copyable input
Out[2]=
In[3]:=
Click for copyable input
Out[3]=

.NETコンソールウィンドウ」セクションで触れたShowNETConsole[]関数は,.NET/Link アセンブリファイルのバージョン番号を表示する.このバージョンは .NET/Link Mathematica 言語コンポーネントのバージョンにマッチしなくてはならない.

ユーザインターフェースを作成する

はじめに

.NET/Link のアプリケーションの1つに,Mathematica プログラムのユーザインターフェースを書くということがある.そのようなインターフェースの例としては,計算の完了を監視するプログレスバー,画像やアニメーションを表示するウィンドウ,ユーザに入力を促したりなじみがない関数への正しい呼出しを作成することを助けたりするダイアログボックス,ユーザに分析の段階を説明するミニアプリケーション等がある.このようなタイプのユーザインターフェースは,ユーザがある Mathematica コードを呼び出したときに出てくる,Mathematica を背景で使う.NETプログラムのために書くインターフェースとは異なっている.これらのユーザインターフェースは,ノートブックフロントエンドに取って代るものではなく,それを拡大するだけのものである.この意味では,これらのインターフェースは,フロントエンドで作成することができるパレットやその他の特殊ノートブック要素の拡張のようなものである.

.NET/Link と一緒に使う Mathematica は,ユーザインターフェースを作成する際に極めて強力で生産的な環境となる.ユーザインターフェースコードの複雑性は,.NET/Link 開発において1度にインタラクティブな行を1行ずつ書く性質に理想的に適合している.実行中にユーザインターフェースを構築,修正,実験してみることができるのである.

.NET/Link あるいは J/Link を使って,Mathematica プログラムのユーザインターフェースを構築することができる.J/Link はクロスプラットフォームなので,インターフェースを Mathematica システムのすべてで実行することができるという点で有利である..NET/LinkJ/Link よりももっとしっかりとWindowsに統合されているので,ユーザがインターフェースをWindowsのマシンだけで使う必要がある場合には,.NET/Link がおそらく最適の選択である.

J/Link を使って Mathematica のユーザインターフェースを作成した場合には,この分野においては .NET/LinkJ/Link の間に大きな違いがあることに注意されたい..NET/Link は一般に J/Link よりも簡単である.これは.NETがJavaよりも優れているからではなく,.NET/Link は2世代目のデザインであるからである..NET/Link におけるデザインの単純化はいずれは J/Link にももたらされるものである.

Mathematica プログラムのユーザインターフェースを書こうと考えている方は,GUIKit のアドオンも選択の1つとして見てみるとよい.このアドオンは,Mathematica 5.1以降の Mathematica にバンドルされていて, 古いバージョンの Mathematica のユーザもダウンロードして手に入れることができる.GUIKitJ/Link の上に構築されているもので,インターフェースを作成する上で大変高レベルの手段を提供する.

モーダルな操作とモードレスの操作

ウィンドウあるいはボタン等の.NETのユーザインターフェース要素を表示する Mathematica プログラムを書くことには, ノートブックフロントエンドだけを使ってカーネルと交信する典型的な Mathematica セッションには存在しない特別な問題に関してある程度の知識が必要である.これらの特別な問題を理解するには,カーネルが入力を手に入れ,それを評価し,任意の出力を送り出す「メインループ」について基本的部分を少し考慮することが役に立つ.

Mathematica カーネルがフロントエンドから使用されると,カーネルはほとんどの時間をフロントエンドと交信する際に使う MathLink に入力が到着するのを待つことに費やす.この MathLink$ParentLinkによって与えられるため,カーネルの注意を引く$ParentLinkである.$ParentLinkに入力が到着すると,入力は評価され,結果はすべてリンクに送り返され,カーネルはまた$ParentLinkにさらに入力が来るのを待つことに戻る. .NET/Link が使われていると,カーネルは.NETランタイムに接続するもう1つの MathLink を開くことになる..NETに呼出しをかけるコードを実行すると,カーネルは.NET に何かを送り,.NETからの戻り値を待つことをブロックする.カーネルが.NETからの戻り値を待つこの期間,.NETリンクはカーネルの注目を得る.これは,カーネルが.NETリンクに注目しているはこの期間中だけである.もっとこれを一般的な言い方で言うと,カーネルは特別に指示された場合にだけ入力が.NETから到着するのを耳を澄まして聞いているということである.その他の時間は,$ParentLinkにだけ耳を済ませている. $ParentLinkは通常ノートブックフロントエンドである.

ユーザが.NETウィンドウのボタンをクリックして,ボタンが Mathematica に呼出しをかけるためのコードを実行しようとする場合に何が起るのかを考えてみよう..NET側は Mathematica に何かを送り,その後結果を待つが,カーネルは.NETリンクではなくノートブックフロントエンドリンクにしか注目していないので,要求を決して受け取ることがない.カーネルに.NETリンクに到着する入力を探すように伝える何らかの方法が必要である..NET/Link は,カーネルが.NETリンクに注意を向けるように管理する2つの主な方法を提供し,それによってカーネルが.NET側で始められた評価への要求を進んで受けることを制御する.

これらの2つの方法を「モーダル」および「モードレス」と呼ぶことができる. Mathematica 関数DoNETModalの使用で特徴付けられるモーダルなインタラクションでは,カーネルは.NET側がそれを解放するまで .NETリンクに向けられる.カーネルは完全な.NET側のスレーブで,その他の計算には使用できない. Mathematica 関数DoNETModelessの使用で特徴付けられるモードレスのインタラクションでは,カーネルはノートブックフロントエンドと.NETの両方から到着する評価要求を受け入れられる状態に置かれ,これらの2つのプログラム間に等分に注意を払う.

ユーザインターフェース要素の普通の型は,モーダルなダイアログに似ている.つまり,これが一旦表示されると,Mathematica プログラムはユーザがウィンドウを解任するまで待ち続ける.典型的にこれは,ウィンドウが結果を Mathematica に返すので,ウィンドウが閉じられるまで Mathematica が継続することに意味を持たないからである.そのようなウィンドウの例としては,ユーザにある値を要求する簡単な入力ウィンドウがあり,値はOKボタンがクリックされると Mathematica に返される.

これらのウィンドウを表現するために用語「モーダル」が少し一般化されて使われていることを理解することが大切である.ユーザインターフェースで他に何かする前にこれらのウィンドウを解任しなければならないという点で,これらは従来の意味ではモーダルとは呼べないかも知れない.むしろ,カーネルはウィンドウが閉じられるまで何もできないという点で,これらのウィンドウは Mathematica カーネルについてはモーダルであると言える.作成した.NETウィンドウは,スクリーン上の他の.NETウィンドウについてはモーダルではないかも知れないが,それが解任されるまでカーネルの注意を縛り付けておく.

ユーザインターフェース要素のもう1つの型は,モードレスのダイアログに似ている.つまり,これが表示された後で,それを作成した Mathematica プログラムは終了し,ユーザがノートブックフロントエンド内で作業を続ける間ウィンドウを可視で使用できる状態のままにしておく.その例としては,ユーザがパッケージをスクロールリストから選んで Mathematica にそれをロードすることを可能にするウィンドウがある.このウィンドウを作成,表示,そして返す .NET/Link プログラムを書く.ウィンドウは,ユーザがそのクローズボックスをクリックするまで開いて使用できる状態のままで置かれる.この間,ユーザはフロントエンド内で自由に作業し続け,都合のいいときにいつでもこの.NETウィンドウをもう1度使うために戻れる.

このようなウィンドウは,フロントエンド内の別のタイプのノートブックあるいはパレットのウィンドウとほとんど同じようなものである.一度に任意の数のフロントエンド,あるいは.NETのモードレスウィンドウを開いてアクティブにしておくことができる.つまり,これらは Mathematica で計算を始めるのに使用することができ,それぞれが同じカーネルにおけるそれ自身の小さなインターフェースである..NETウィンドウが異なる点は,これはノートブックウィンドウよりももっと一般的で,重要なことにフロントエンドとは異なるアプリケーションの層に存在するということである.この最後の事実が実際.NETウィンドウをノートブックフロントエンドの延長というよりも第2のフロントエンドにしている.そのような第2フロントエンドを受け入れるためには,カーネルはノートブックフロントエンドあるいは.NETのいずれかから到着する評価要求を処理できる特別な場所に置かれる必要がある.

モーダルなウィンドウとモードレスのウィンドウの実装の仕方の例を示す前に,少し先に飛んで,.NETのユーザインターフェース要素がイベントを Mathematica に交信するメカニズムについて説明する.

イベントを処理する

ユーザインターフェース要素は一般に,ボタン,スクロールバー,メニュー,テキストフィールド等使用されるときに特定のアクションをトリガする必要があるアクティブな要素を持つ..NETイベントモデルでは,構成要素がユーザのアクションに応えてイベントを引き起し,他の構成要素は,イベントをそのハンドラに接続する代表を供給することによって,これらのイベントに対して興味があることを示す. 代表の概念については.NET Frameworkのドキュメントで詳しく触れているが,イベントが起るときに呼び出される Mathematica 関数を割り当てるのには大変簡単な構文を使うので,.NET/Link ユーザは代表についての詳細は一般に無視しても大丈夫である.

イベントハンドラ関数を割り当てるための .NET/Link での方法とC#およびVisual Basic .NETにおける方法を比較することは有益である.以下は,TextBoxKeyPressイベントにイベントハンドラを加えるためのC#およびVisual Basic .NETの構文を示す.

上の行のどちらかを実行した後で,myTextBoxコンポーネントに注意が向けられている間にキーが押されるといつでもMyKeyPressHandlerMethod()メソッドが呼び出される.イベントに代表を加えるのに+=演算子がオーバーロードされることから,C# 構文は少し暗号的である.

前述のコードではMyKeyPressHandlerMethod()の定義を示さない.このメソッドのシグナチャはKeyPressイベントに対応する代表と同じものでなければならない.C#コードに見られるように,代表の型はKeyEventHandlerであり,以下はその宣言である.

MyKeyPressHandlerMethod()は同じシグナチャを持たなくてはいけないので,以下のような形になる.

MathematicaMathematica 関数のKeyPressイベントに割り当てると次のようになる.

Click for copyable input

これはVisual Basic .NETバージョンとほとんど全く同じ形になることに注意されたい.一旦上の行を実行すると, myTextBoxが入力フォーカスを持つ間にキーが押されるといつも,.NETは Mathematica を呼び返し,関数を実行する.Mathematica 関数はKeyEventHandler の代表と同じ引数で呼び出され,同じ型の値を返すはず(ただし,ほとんどのイベントハンドラはvoidを返すので,戻り値は無視される)である.以下はそれがどのようになるかの例である.

Click for copyable input
AddEventHandler[obj@eventName,funcName]オブジェクト objeventName イベントを引き起すと呼び出される Mathematica 関数を設定する
RemoveEventHandler[delegate]AddEventHandlerへの前回の呼出しで割り当てられたイベントハンドラを削除する

イベントの通知に応えて呼び出される Mathematica 関数を割り当てる.

Mathematica 関数への呼出しを通してアプリケーションのイベント論理を繋ぐ方が,.NETの従来のアプリケーションを書くことよりもずっと融通が利く.コンパイルされた.NET言語で書いたり,あるいはドラッグアンドドロップのGUI ビルダーを使ったりすると,イベント論理をハードコード化することになる.コンパイルする際に,すべてのクリック,スクロール,キーストロークがそれぞれ何を行うかを決めなくてはならない.しかし,.NET/Link を使う場合は,ランタイムにプログラムをどのように配線するかを決定する.単に数行のコードを入力することによって,オンザフライで動作を変更することすら可能である.

RemoveEventHandler関数を使ってイベントハンドラを削除することができる.AddEventHandlerを呼び出すと,を返す.このオブジェクトは .NET/Link の内部でユーザ用に作成された代表である.このオブジェクトを保存して,後からRemoveEventHandlerに渡し,それが表す Mathematica コールバックを削除することができる.これがAddEventHandlerの戻り値を使うことになるほとんど唯一の場合である.

Click for copyable input

イベントに応えて呼び出される Mathematica のイベントハンドラ関数は,自動的にNETBlockでラップされる.これはつまり,関数に引数として送られるオブジェクト,および関数の実行中に作成された任意の新しいオブジェクトは,関数が戻った後解放される.NETBlockあるいはReleaseNETObjectを手動で使う必要はない.ハンドラ関数からのオブジェクトを関数が戻った後も Mathematica で持続したい場合は,KeepNETObjectを使って関数の呼出しをラップする目に見えないNETBlockを逃がす必要がある.以下は,後から検査するためにKeyCodeオブジェクトをリストに保持するの修正されたバージョンである.

Click for copyable input

AddEventHandlerはその動作を制御する2つのオプションを取る.SendDelegateArgumentsではどの代表引数をどの順番に Mathematica ハンドラ関数に送りたいかを指定することができる.デフォルトで .NET/Link は代表引数をすべて送るが,最適化という意味では,すべての引数を送らない方がよい場合もある.新しい式を Mathematica で作成することは,比較的高くつき,ほとんどのイベント代表の引数はオブジェクトである.上のKeyPressイベントの例では,第1引数はTextBoxオブジェクトであるが,これはおそらくすでに Mathematica 内に存在するものであるので,それを送ることを避けても有意な最適化にはならない.しかし,KeyEventArgsオブジェクトは確実に Mathematica にとって新しいオブジェクトであるので,必要ではない場合には送ることを避けた方がよいこともある.以下は,Mathematica のコールバック関数の第1引数だけを送るイベントハンドラを設定する例である.

Click for copyable input

これは関数がどのようになるかの例である.

Click for copyable input

SendDelegateArgumentsオプションに指定できる値は,All(デフォルト),None,あるいは送りたい引数の指数を与える整数のリストである.

オプション名
デフォルト値
SendDelegateArgumentsAllどの代表引数を Mathematica イベントハンドラ関数に送るか
Falseイベントハンドラ関数が拡張関数を呼び出すかどうか

AddEventHandlerのオプション.

オプションは,DoNETModelessを使う代りにおよびの関数を使って,手動でカーネル共有を制御している上級プログラマー用のオプションである.共有関数については「カーネルとフロントエンドを手動で.NETと共有する」で触れる. Mathematica コールバック関数が を呼び出す場合には,AddEventHandlerへの呼出しの中でTrueに設定する必要がある.

AddEventHandlerはイベントが起ったときに呼び出される Mathematica 関数を容易に割り当てることができる簡易関数である.その操作の一部として,AddEventHandlerはイベントに割り当てられ,そのアクションが指定の Mathematica 関数を呼び出すことにある,.NET代表オブジェクトを作成する.場合によっては,そのような代表オブジェクトを手動で作成したいけれども,それをイベントに添付したくはないこともある..NET/Link は,NETNewDelegate関数をこのために提供する.NETNewDelegateはそのアクションが指定された Mathematica 関数を呼び出すことにある指定の型の代表を作成する.NETNewDelegateを使う主な目的は,.NETのPInvokeの設備を通して呼び出される外部C関数に供給される代表オブジェクトを作成することにある. この目的にこの関数はしばしばDefineNETDelegateと一緒に使用される.EnumWindows.nbの例題ファイルは,DefineNETDelegateおよびNETNewDelegateを使って,引数としてコールバック関数ポインタを取るC関数を呼び出す例を示す.

NETNewDelegate[type,funcName]そのアクションが指定の Mathematica 関数を呼び出すことにある,指定の代表の型の新しいインスタンスを作成する
DefineNETDelegate[name, returnType,{argType,...}]適切なシグナチャの.NET代表がすでに存在しない場合に新しい代表の型を作成する

Mathematica を呼び出す代表オブジェクトを作成する.

Mathematica イベントハンドラのコールバックの特定の仕方を見てきたので,今度はカーネルが.NETのユーザイベントからの呼出しを受け取れる特別の状態になければならないことを思い出していただきたい.次のセクションでは,これを行う2つの主な方法である関数DoNETModalおよびDoNETModelessについて触れる.

モーダルなウィンドウ

モーダルとモードレスの .NET/Link インターフェースの基本的な概念については,上のセクション「モーダルな操作とモードレスの操作」で触れている.以下は簡単なモーダルウィンドウの例である.ウィンドウは,クリックされるたびに背景色が新しいランダムな色に変わる簡単なFormオブジェクトである.

In[1]:=
Click for copyable input
Out[1]=

ただ単に新しいFormオブジェクトを作成するだけでは,それは可視にはならない.ShowNETWindow関数を使ってウィンドウを可視にし,それを他のウィンドウの前に持ってくる.後で使用されるDoNETModal関数は,フォームを可視にするが,インターフェースを実際モーダルに実行する前にインターフェースをいじっているときにはShowNETWindowが便利である.

In[2]:=
Click for copyable input

この時点でスクリーンの中央に小さな枠のウィンドウが見えるはずである.それをドラッグしてスクリーンの端に持ってきて,それが現行のノートブックウィンドウを前面に持ってきたときに Mathematica ウィンドウで隠れないようにする.ユーザインターフェース開発に .NET/Link を使う大きな利点は,典型的なコンパイルされた.NET言語と比べて,.NET/Link ではインターフェースを実行している最中にいろいろ試してみることができるという点にある.今度は背景色を変えてみる.

In[3]:=
Click for copyable input

フォームのClickイベントに Mathematica ハンドラを加える.

In[5]:=
Click for copyable input

以下は関数の定義である.これは,フォームのBackColorプロパティをランダムな色に設定する.この関数はイベント引数を無視するが,これらの引数が何であるかは,Clickイベントのシグナチャから分かる.

In[6]:=
Click for copyable input

この時点でフォームをクリックすると,ビープが鳴り,色は全く変更されない.Clickイベントが起ると,.NETは Mathematica を呼び出して関数を実行しようとするが,Mathematica は.NETリンク上の入力の有無に注意を払っていないので,この呼出しを行うことは安全ではない.Mathematica への呼出しが永久にハングアップしてしまう..NET/Link はカーネルの準備ができていないことを知っているので,呼出しを行うことを拒否し,代りにビープ警告を鳴らすのである.

ここで必要なのは,カーネルが.NETリンクから継続して読み込むような状態にカーネルを置くことである.これがつまりウィンドウを「モーダル」にするということで,カーネルはウィンドウが閉じられるまでは他のことは何もできない.このモーダルな状態を実装する関数がDoNETModalである.DoNETModalの第1引数は,トップレベルのウィンドウオブジェクト(.NET FrameworkではこれはSystem.Windows.Forms.Form,あるいはそれから継承する任意のクラス)である.

DoNETModal[form]指定された form ウィンドウが閉じられるまでカーネルの注意が.NETリンクだけに向けられた状態になるようにカーネルを置く
DoNETModal[form, returnValue]form をモーダルに実行し, returnValue の計算(これはウィンドウが破棄される前に実行される)の結果を返す

モーダルなウィンドウを実行する.

これですべての準備が整ったので,モーダルな状態に入ってウィンドウを使用することができる.

In[7]:=
Click for copyable input

DoNETModalは.NET フォームウィンドウが閉じられるまで返らない.ウィンドウを何度がクリックして色が変化することを確かめ,その後タイトルバーのクローズボックスをクリックしてフォームを破壊しDoNETModalが返るように仕向ける.

テキストボックスからの値,あるいはフォームがOKあるいはCancelのボタンをクリックすることによって閉じられるかどうか等の情報をモーダルなダイアログボックスが閉じられるときにそこから得たい場合がよくある. DoNETModalが返るとき,フォームオブジェクトは破壊されるので,それに対してもはやメソッドを呼び出すことはできない.フォームが破壊される前にフォームから何らかの情報を得る必要がある場合は,オプショナルな第2引数であるDoNETModalを使うとよい.この引数はフォームが破壊される直前に実行される計算(計算はこのときまで評価されずに留め置かれる)を指定する.DoNETModalがこの計算の結果を返す.PackageHelper.nb の例題ファイルでは,フォームがOKあるいはCancelのボタンをクリックして閉じられたかどうかを決定するDoNETModalの第2引数の使い方を示す.

以下は,例全体が単独のプログラムにパッケージされた場合にどのようになるかを示している.

In[8]:=
Click for copyable input

.NET Frameworkのドキュメントは,ShowDialog()メソッドを使ってどのようにモーダルなウィンドウを実装するかについて述べている.DoNETModalの代りにこの技術を使うことについてはいくつかの利点と不利点が挙げられる.1つの不利点は,.NETウィンドウはShowDialog()を使う場合にノートブックウィンドウの前に必ずしも来るとは限らないという点である.しかし,このウィンドウが他のウィンドウの前に出てこないのは,セッションで一番最初に表示されるウィンドウに限られるようである. ShowDialog()で表示されるウィンドウは,モーダルなウィンドウが閉じられるまで他の.NET ウィンドウが使えないという面において真にモーダルである.ShowDialog()メソッドは,DialogResult列挙の値の1つを返すので,どのようにウィンドウが閉じられるか(OKあるいはCancelのボタンがクリックされるかどうか)を決定することを容易にする.同じ結果をDoNETModalの第2引数を使って得ることもできる.

Click for copyable input

PackageHelper.nbの例題ファイルは,DoNETModalOKおよびCancelのボタンが実装された典型的なモーダルのダイアログを示す.

DoNETModalは,ウィンドウがスクリーン上に現れる位置を指定する1つのオプションであるFormStartPositionを取る.可能な値は,Center(デフォルト),Automatic(フォームはWindowsのデフォルトの位置を持つ), Manual(フォームは別で指定された,例えばフォームのLocationプロパティで設定された場所に現れる)のいずれかである.

オプション名
デフォルト値
FormStartPositionCenterスクリーン上でウィンドウが表示される位置

DoNETModalのオプション.

モードレスのウィンドウ

上のセクションではDoNETModal関数を使って,ウィンドウが閉じられるまでカーネルが使用中のままになるモーダルなウィンドウを表示,実行する方法について示した.モードレスと呼べるもう1つのタイプのウィンドウは,カーネルを完全に不通にすることなく,開いていて使える状態に置いておく.モーダルとモードレスの .NET/Link のインターフェースの基本的な概念については,上のセクション「モーダルな操作とモードレスの操作」に詳細が掲載されている.

.NET/LinkDoNETModeless関数を提供してウィンドウをモードレスに実行する.DoNETModelessの第1引数はトップレベルのウィンドウ(特に,Formあるいはそこから継承される任意のクラス)である.ウィンドウは可視化され,すべてのノートブックウィンドウの前面に持ってこられる.

DoNETModeless[form]指定された Form ウィンドウを表示してすぐに返し,ウィンドウをアクティブのままにしておく

モードレスのウィンドウ.

以下は前セクションからのの例で,モードレスウィンドウとして実装されている.

In[1]:=
Click for copyable input

を実行すると,ウィンドウは可視になり,その後すぐに返る.ウィンドウをクリックしてその背景色を変えることができるし,ノートブックフロントエンドを通して他の計算にカーネルを使い続けることもできる.

In[2]:=
Click for copyable input

のコードには,DoNETModalの代りにDoNETModelessを呼び出すという明らかな違いの他に,重要な違いがいくつかある.これらの違いは,フォームが関数が返った後から実行されるという事実から来るものである.Moduleが終了したときに関数の定義がクリアされるので,関数はModuleにローカルであってはならない.が実行されるときにNETBlockを使って自動的にすべての作成された.NET オブジェクトが解放されるが,これはつまりのコードの中で名前でfrmを参照してはならないということである.これはなぜならが実行される頃(シンボルfrmModuleに対してローカルであるので,その値はどっちみちクリアされる)にはすでにNETBlockが終了しているからである.関数の第1引数はイベントを起すオブジェクト,つまりFormオブジェクトなので,シンボルfrmの代りに第1引数を使ってそれを参照することができる.

DoNETModelessは,開発中に大変便利で,最終的なフォームではモーダルに実行されるウィンドウについてさえも役に立つ.DoNETModalはウィンドウが閉じられるまで返さないので,実行中にウィンドウのイベント論理やその他の面について修正を入れることはできない.DoNETModelessを使って,カーネルを不通にすることなくイベントコールバックを「ライブ」にすることができ,ウィンドウが表示されている間でもウィンドウを操作することができる.希望通りにウィンドウが動作することを確認したら,DoNETModalで実行する完全なプログラムをパッケージすることができる.

オプション名
デフォルト値
FormStartPositionCenterスクリーン上でウィンドウが表示される位置
ActivateWindowTrueウィンドウを可視にするかどうか
Falseカーネルに加えてフロントエンドも.NETで共有できるようにするかどうか

DoNETModelessのオプション.

DoNETModelessはいくつかのオプションを取る.FormStartPositionはスクリーン上でウィンドウが現れる位置を指定する.可能な値は,Center(デフォルト),Automatic(フォームはWindowsのデフォルトの位置にある), Manual(フォームは別の場所で指定された位置,例えばフォームのLocationプロパティを設定することによって指定された位置に現れる)のいずれかである.ActivateWindowはウィンドウを可視して最前面に持ってくるかどうかを制御する.モードレスの状態に入るためにDoNETModelessを呼び出してからのみウィンドウを可視にしたいという稀な場合にはActivateWindowFalseに設定する.

最後のオプションであるは,.NETインターフェースがノートブックフロントエンドとインタラクトできるようにするために使用される.関数およびについては次のセクションで触れる.DoNETModelessを使っているプログラマーは一般にこのような低レベルの関数からはシールドされている.DoNETModelessは主におよびへの呼出しをカプセル化する方法で,ウィンドウが最初に現れたときに共有し始め,ウィンドウが閉じられたときに終了する.モードレスのインターフェースにノートブックフロントエンドの中で何らかのアクション(テキストを出力したりグラフィックスが表示されるようにしたりする等)を起させたい場合は,DoNETModelessにカーネル共有だけでなくフロントエンドもオンにするように強制する必要がある.これはオプションをTrueに設定することで行うことができる.

オプションが便利となる状況でよくあるのが,Print宣言をイベントハンドラ関数に挿入することによってプログラムをデバッグしたい場合である.モードレスのインターフェースでは,イベントハンドラ関数でトリガされるPrint出力と Mathematica の警告メッセージは.NET側に送られるので,ノートブックには現れない.この情報を見たい場合は,Trueに設定すると最前面のノートブックウィンドウにこれが現れる.開発中にこのオプションを使うが,プログラムの最終バージョンでは必要ないという場合は,忘れずにオプションを削除しなくてはならない.フロントエンド共有を付けたり消したりするのは高くつくし,ウィンドウが最初に現れるのが遅れることがあるからである.

カーネルとフロントエンドを手動で.NETと共有する

注意: Mathematica 5.1以降では,カーネルは常に.NETリンクと共有される.これはつまり関数およびは必要なく,実際何も行わないということである.Mathematica 5.1以降の Mathematica のみで実行する必要があるプログラムを書いている場合は,あるいはを呼び出す必要は全くない.プログラムがすべてのバージョンの Mathematica で使えなければならない場合は,以下に述べるようにこれらの関数を使う必要がある.

前セクションで触れたDoNETModalおよびDoNETModelessの関数は,.NETから始まるイベントからの呼出しを受け入れる状態にカーネルを置く方法である.ほとんどのプログラマーは.NETウィンドウを表示し実行するためだけにこの2つの関数を使う必要がある.DoNETModelessはカーネルが.NET あるいはノートブックフロントエンドからの入力を受け入れるようにするということを思い起こしていただきたい.実際,カーネルはフロントエンドと.NETの間で「共有」される.状況によってはカーネル共有を始めたいが,DoNETModelessがそれには適切ではないという場合もあるかもしれない.その場合,関数を呼び出すことによって,カーネル共有を直接制御することができる.

J/Link で紹介されたもので,コンテキストで定義される.をロードするとがロードされるので,パッケージの中であるいはその関連関数を使っているのでない限り,異なるコンテキストについて心配する必要がない.BeginPackageを呼び出すと,Mathematica は使用中のパッケージのコードにBeginPackage宣言の中で明示的に指定したコンテキストだけを使用できるようにして,これらのパッケージのその他のコンテキストを使えるようにはしない.これはつまり,パッケージ内で使用したいシンボルそれぞれに対して常にBeginPackage宣言の中でそのシンボルのコンテキストを明示的に含む必要があるということである.以下は .NET/Link を使い,またを直接呼び出すパッケージの概略である.

J/Link ユーザガイド」でおよびについて詳細に触れているので,完全な情報はそれらのセクションを参照されたい.あるいはを.NETで使う際に心に留めておく必要があるのは,リンクを.NETに引数として供給しなくてはならないということである.さもなければ,デフォルトでJavaリンクが使用される.

Click for copyable input

常にからの結果を保存し,その後でに渡さなくてはならない.

Click for copyable input

上で述べたように,DoNETModelessを呼び出して共有を開始し,ウィンドウが閉じられたときにが呼び出されるようにする.J/Link では,プログラマーはモードレスなウィンドウを作成するために直接およびを呼び出す必要がある.DoNETModelessのように自動的に共有をオンにしたりオフにしたりする特別の関数があるとずっと楽である.

直接を呼び出す必要があるプログラムの例が「COMイベントを処理する」に掲載されている. そのプログラムは Mathematica のイベントハンドラをInternet Explorerアプリケーションで引き起されたCOMイベントのために設定する. DoNETModelessに渡すトップレベルの.NETFormウィンドウはないので,明示的に共有を管理する必要がある.

Mathematica グラフィックスとタイプセット式を表示する

.NET/Link には標準PictureBoxクラスの特別のサブクラスが含まれている.このサブクラスはWolfram.NETLink.UI.MathPictureBoxと呼ばれ,Mathematica のグラフィックスあるいはタイプセット式を.NET ウィンドウで表示することを容易にしてくれる.例題ファイルSimpleAnimationWindow.nbにその使い方が示されている.このクラスについての完全なドキュメントは .NET/Link APIドキュメントを参照されたい.

.NETウィンドウを最前面に持ってくる

.NETウィンドウを Mathematica プログラムで作成している場合,おそらくそのウィンドウがユーザが作業をしているノートブックの前に出てくるようにして,そのウィンドウの存在が明らかになるようにしたいであろう.関数DoNETModalおよびDoNETModelessは自動的にフォームを可視化し,それを最前面に持ってくる.Formクラスの Show()あるいはActivate()メソッドもこれと同じことを行えそうであるが,.NETウィンドウはノートブックフロントエンドとは違うアプリケーションに存在しているので,これらのメソッドは必ずしもうまくいくとは限らない.

.NET/LinkMathematica 関数であるShowNETWindowを提供して,.NETウィンドウを可視化し,他のウィンドウの前に現れるようにするのに必要なステップをすべて行う.DoNETModalあるいはDoNETModelessを使っている場合は,ShowNETWindowは自動的に呼び出されるので,呼び出す必要はない.しかし, DoNETModelessを使っていても,そのウィンドウが最初に表示された後にユーザが他のウィンドウをその前面に持ってきた場合には,ウィンドウをもう1度最前面に持ってくるのにShowNETWindowが役に立つことがある.

ShowNETWindow[form]指定の.NET form ウィンドウを可視化し,それをノートブックウィンドウを含めたその他のウィンドウすべての前に持ってくるようにする

.NETウィンドウを最前面に持ってくる.

DoNETModalおよびDoNETModelessと同様に,ShowNETWindowはスクリーン上でウィンドウが現れる位置を指定するFormStartPositionオプションを取る.可能な値は,Center(デフォルト),Automatic(フォームはWindowsのデフォルトの位置にある),Manual(フォームはそれ以外で指定された位置,例えばフォームのLocationプロパティを設定することによって指定された位置に現れる)のいずれかである.

例題ファイル

以下のGUIの例題プログラムが .NET/Link に含まれている.

Circumcircle.nb

PackageHelper.nb

SimpleAnimationWindow.nb

RealTimeAlgebra.nb

AsteroidsGame.nb

Mathematica から使用するために独自の.NET の型を書く

はじめに

このドキュメントでは既存の.NETの型をどのようにしてロードして使うかということについて見てきた.これで Mathematica プログラマーは.NETの型の世界全体に即座にアクセスできる.しかし、既存の型だけでは足りなくて,独自の型を書く必要がある場合もある.

.NET/Link は基本的に.NETと Mathematica の間の境界線を消し去って,任意の型の式を渡したり受け取ったりすることと,.NETオブジェクトを Mathematica 内で意味のある形で使用することを可能にしてくれる.これはつまり, Mathematica から呼び出す独自の.NETの型を書く場合に,何も特別な操作を行う必要がないということである.型を.NETからのみ使いたい場合と全く同じようにコードを書いて,好きな.NET言語を使うことができる.

場合によっては,Mathematica とのインタラクションにもっと直接コントロールを及ぼしたい場合もあるかも知れない.例えば,メソッドが実際に返すものとは異なる結果を Mathematica に送るメソッドを希望する場合があるかも知れない.あるいは,メソッドがただ単に何かを返すだけでなく,Mathematica で何かを出力したり特定の条件下でメッセージを表示したりといった何らかの副作用を引き起すようにしたいかも知れない.メソッドが返る前に Mathematica と拡張された「対話」を行って,Mathematica 内で複数の計算を呼び出したり,その結果を読み込んだりといったことを行うことさえ可能である..NETで何らかのイベントの結果が引き起されるときに Mathematica で呼び出すコードを書きたいと思うこともあるかも知れない.

これらのことを全く行う予定がないのであれば,このチュートリアルを無視してもらっても大丈夫である..NET/Link の核心は, MathLink を通して行う Mathematica とのインタラクションについて心配することを不要にするということである.Mathematica から使用する.NETの型を書きたいと考えるプログラマーのほとんどは,Mathematica あるいは .NET/Link のことを考えずに,単に.NETの型を書く.もっとコントロールが欲しい,あるいは .NET/Link を使って行えることについてもっと知りたいと思っているプログラマーの方は,この先を読み進まれたい.

.NET/Link で使用する独自の型を作成する際に注意しなくてはならないのは,LoadNETAssemblyを使って型を含むアセンブリをロードする必要があるということである..NET/Link でアセンブリに含まれる型を使う前に必ずそのアセンブリをロードしなくてはならないが,このことは,.NET/Link が.NET Frameworkの一部であるアセンブリについてはそれが必要になったときに自動的にロードするので,忘れられがちである.

結果を Mathematica に手動で返す

Mathematica から呼び出された.NETのメソッドあるいはプロパティにデフォルトの動作は,メソッドあるいはプロパティ自体が返すものをそのまま Mathematica に返すということである.しかし,それ以外のものを返したいと思うような場合もある.例えば,ある状況では整数を返し,別の状況ではシンボルを返したいと考えるかも知れない.あるいは,メソッドが.NETから呼び出された場合と Mathematica から呼び出された場合で違うものを返すようにしたいことがあるかも知れない.このような場合に,メソッドが返す前に Mathematica に手動で結果を送る必要がある.

Mathematica から呼び出したいファイルを読み込むクラスを書いているとしよう.標準クラスのSystem.IO.StreaInlineCodeeaderとほとんどまったく同じ動作を行いたいので,作成するクラスはそのサブクラスとなる.唯一変更したい部分は,もっと Mathematica らしい動作をいくつか提供するようにしたいということである.1つの例としてRead()メソッドがファイルの終りに到着したときに-1ではなく,むしろ Mathematica の組込みのファイル読込み関数が返すシンボルEndOfFileを返すようにしたい場合がある.

ファイルが最後に到達すると,i-1になり,何かを手動で Mathematica に返したいとする.最初に行わなくてはいけないことは,Mathematica と交信するために使えるIKernelLinkオブジェクトを得ることである.これは静的なプロパティStdLink.Linkを呼び出して得る.インストールできる MathLink プログラムをCで書いたことがあるなら,ここで選択している名前に見覚えがあるであろう.Cプログラムはstdlinkという名前で Mathematica へ返すリンクを持っている大域的な変数を持つ..NET/Link はこのリンクオブジェクトに関連するメソッドをいくつか持つStdLinkクラスを持つ.

次にLinknullを返すかどうかを検証する.Mathematica からメソッドが呼び出されている場合には,これは決してnullにはならないので,このテストを使ってメソッドが Mathematica から呼び出されているのか,それとも標準の.NETプログラムの一部として呼び出されているのかを調べることができる.このようにして,Mathematica カーネルがどこにもない場合に,通常の方法で.NETから使えるメソッドを持つことができる.

一旦カーネルに返るリンクが存在していることを確かめたら,まず .NET/Link に結果を自分で Mathematica に送り返すので,自動的にメソッドの戻り値を送らないようにということを伝える.これは,IKernelLinkオブジェクトに対してBeginManual()メソッドを呼び出すことによって行える.

結果を Mathematica に送り返す前にBeginManual()を呼び出さなくてはならない.これを行わないと,リンクは同期しなくなって,今度 .NET/Link の呼出しを Mathematica から行った場合におそらくハングアップされてしまう.2度以上BeginManual()を呼び出しても問題ないので,自分のメソッドがBeginManual()をすでに呼び出した別のメソッドから呼び出されるかも知れないということを心配する必要はない.

例題のプログラムに戻って次にBeginManual()の後で行うことは,必要な「put」形式の呼出しを行って結果を Mathematica に送り返すようにするということ(この場合は単独のPutSymbol()だけ) である.すべてのメソッドの呼出しをラップする内部 .NET/Link コードがPutSymbol()の呼出し中に起り得る任意の MathLink エラーからのクリーンアップと回復を処理する.手動で結果を置いているときに起るMathLinkException例外に対して何も行う必要はない.メソッドの呼出しは自動的に$FailedMathematica に返す.

Mathematica による評価をリクエストする

これまで.NETメソッドが大変簡単なインタラクションを Mathematica と行う場合だけを見てきた.このメソッドは呼び出されて結果を自動あるいは手動で返す.しかし,もっと複雑なインタラクションを Mathematica と持ちたいと考えるような状況も多くある.メッセージあるいは何らかのPrint出力が Mathematica で現れるようにしたい,あるいは Mathematica が何かを評価してその答を返すようにしたいかも知れない.これはメソッドの最後に Mathematica に何を返したいかということとは全く異なる問題である.つまりメソッドが最終結果を手動で返すかどうかにかかわらず,メソッドのボディで評価を要求することができる.

ある意味で,このタイプのインタラクションを Mathematica で行うということは,Mathematica の立場を逆転させる,しばらくの間「マスター」と「スレーブ」の役割を逆にするということである.Mathematica が.NET内に呼出しをかけると,.NETコードがスレーブとして働き,計算を行ってからコントロールを Mathematica に返す.しかし.NETメソッドの最中に,Mathematica に呼出しをもう1度かけて,一時的にそれを.NET側の計算スレーブにすることができる.このため,基本的に「.NETから Mathematica を呼び出す」で触れた問題とすべて同じものに出会うことが予想され,.NETプログラマーが見るように完全な .NET/Link APIを理解する必要が出てくる.

IMathLinkおよびIKernelLinkのインターフェースの完全な取扱いについては,「.NETから Mathematica を呼び出す」で触れる.ここでは,「インストールされた」メソッドで使用することを特に意図したIKernelLinkインターフェースの特別なメソッドのいくつかについて触れる.そのうちの1つ,BeginManual()メソッドについてはすでに上で触れている.このセクションでは,Message()Print()Evaluate()のメソッドについて触れる.

Mathematica メッセージを.NETメソッドから発行したり,何らかのPrint出力を引き起したりするタスクは,大変頻繁に行われることであるので,IKernelLinkインターフェースはこれらの操作のために特別のメソッドを持つ.メソッドMessage()Mathematica メッセージを発行するためのすべてのステップを行う.

Print()メソッドは Mathematica Print関数を呼び出すのに必要なすべてのステップを行う.

以下は両方を使用する例題メソッドである.以下のメッセージは Mathematica で定義されるものとする.

以下はC#コードである.

Print()およびMessage()は,必要なコードを Mathematica に送り,結果(常にシンボルNullとなる)をリンクから読み込むことも行う.

以下はFoo()を呼び出すと何が起るかを示す.

Click for copyable input

.NETからの浮動小数点結果がNaN ("Not-a-Number",数ではない)であるときには,自動的にIndeterminateMathematica へ返させることに注意されたい.

メソッドのPrint()およびMessage()は簡易関数で,自分のメソッドが結果を返す前に Mathematica に即座の評価を送るというもっと一般的な考えの2つの特別な場合のための関数である.これを行うための一般的な手段は,MathematicaEvaluatePacketで送るものをすべてラップするという方法で,これは最終結果ではなく,むしろ評価して結果を.NETに送り返すべき何かであるということをカーネルに示す方法である.明示的にEvaluatePacketの頭部を送る,あるいはEvaluatePacketを使うIKernelLinkのメソッドの1つを使うことができる.これらのメソッドは,Evaluate()EvaluateToInputForm()EvaluateToOutputForm()EvaluateToImage()EvaluateToTypeset()である.これらの詳細については .NET/Link APIドキュメントを参照されたい.

以下は簡単な例である.

例外を投げる

メソッドが投げる例外は .NET/Link で巧みに処理されるので,結果として例外を表現するメッセージを Mathematica で出力する.これについては「例外」で触れている.計算を Mathematica に送っている場合は,前セクションで述べたように,例外が予期しないところでコードに割り込まないようにすることが必要である.つまり,Mathematica とのトランザクションを始めたら,それを必ず完了しなければならない.さもなければ,リンクが同期しないままになり,その後で.NETへ行う呼出しがおそらくハングアップしてしまう.

メソッドを割込み可能にする

完了するのに時間がかかりそうなメソッドを書いている場合は,これを Mathematica から割込み可能なものにすることを考慮すべきである.C MathLink プログラムでは,MLAbortという名前の大域変数がこの目的のために提供されている..NET/Link プログラムでは,WasInterruptedプロパティをIKernelLinkインターフェースで呼び出すとよい.

以下は長い計算を行う例題メソッドである.このメソッドは,ユーザが計算を放棄しようとしているかについて(評価メニューの評価を中断あるいは評価を放棄のコマンドを使って)100個の反復をすべてチェックする.

このメソッドは,ユーザが放棄しようとしていることを検知した場合には0を返すが,この値が Mathematica によって見られることは決してない.これは,.NET/Link がコードの中に放棄を検知するかどうかにかかわらず,放棄されるメソッド,プロパティ,コンストラクタの呼出しのいずれもをAbort[]に返させるからである.このため,放棄を検知し,ユーザの要求に従いたいと考えた場合には,何らかの値をすぐに返すだけでよい..NET/LinkAbort[]を返すと,ちょうどAbort[]Mathematica コードに埋め込まれているかのように,ユーザの計算全体が放棄されてしまう.これはつまり,Mathematica に放棄を伝えなおすことの詳細について心配する必要はないということである.放棄の要求を検知した場合は,時期尚早に返すことだけを行えばよく,残りについては何もしなくても処理される.

.NET/Link は割込み要求と放棄要求の区別を行わないので,どちらもWasInterruptedTrueを返させる. Mathematica には計算を中断する場合と放棄する場合とでは別々のコマンドがあることを思い出していただきたい. 「Abort(放棄)」操作(WindowsではAlt+. )は,計算全体をできるだけ早く終了して$Abortedを返す. 「Interrupt(中断,割込み)」操作(WindowsではAlt+,)は,さらに選択を行うためのダイアログボックスを表示する..NETメソッドの実行中にこの「Interrupt(割込み)」ダイアログボックスが引き起された場合には,通常の Mathematica コードの実行中に出てくるボックスのボタンとは異なるボタンがこのボックスには入っている.オプションの1つがSend Abort to Linked Program(リンクされたプログラムに放棄を送る)で,もう1つがSend Interrupt to Linked Program(リンクされたプログラムに割込みを送る)である.どちらを選んでも,.NET メソッドに対する効果は同じである.つまり,WasInterruptedtrueを返させ,呼出しが完了するとAbort[]を返させる.3つ目のボタンはKill Linked Program(リンクされたプログラムを止める)で,.NETランタイムを終了させる.割込みが可能ではない.NETメソッドを呼び出す場合は,このように.NETランタイムを止めることがメソッドの呼出しを終了させる唯一の方法である.Windowsのタスクマネージャを使っても .NETランタイムを止めることができる。

ときには.NETメソッドに放棄を検知させ,Mathematica の計算全体を放棄する以外の操作を行いたい場合もある.例えば,その点までの結果を止めたり返したりするループを作りたい場合があるかもしれない.ただし,この操作は通常お勧めする操作ではないことに注意されたい.ユーザは,放棄要求を出した場合にプログラムが放棄され,$Abortedが返されることを期待する.しかし,特にコードが大きなコミュニティで使われることを意図していないような場合には,ただ単に計算を放棄してしまう代りに,自分の.NETコードに何らかの情報を交信するための「メッセージ」として放棄を使うことが便利なこともある.この考え方は,放棄が全体に伝播して計算全体が放棄されてしまわないように,放棄を検知して吸収してしまう関数である Mathematica CheckAbort関数に似ている..NET コードで放棄を「吸収」して .NET/LinkAbort[]を返さないようにするためには,WasInterruptedプロパティをfalseにリセットするだけでよい.

以下はその例である.

独自のイベントハンドラのコードを書く

イベントを取り扱う」では,ボタンをクリックする等の.NETで引き起されたイベントに対する反応として Mathematica に呼出しを引き起すことについて紹介した.AddEventHandler関数は,Mathematica でイベントハンドラを設定するための簡単な手段を提供する.もちろん,AddEventHandlerを使わなければならないということではない.任意の.NET言語で独自の代表を作成して,イベントを処理したりコードに直接 Mathematica への呼出しを挿入したりすることができる.この方法を選ぶ場合は,Mathematica に呼び出すイベントハンドラのコードを書く際に必ず守らなければならない大変重要な規則がある.Mathematica に計算を送る前に必ずRequestTransaction()を呼び出さなくてはならないということである.RequestTransaction()は,Mathematica が.NETからの呼出しを引き受ける状態にない場合に,例外を投げる.希望するならこの例外を捕まえてもよい.あるいは,これを無視して,.NET/Link が捕まえて警告ビープを出すようにしてもよい.言い換えれば,カーネルと .NET/Link の準備ができていないときにカーネルを呼び出そうと試みて,.NET/Link の内部に干渉することを防ぐ.

正確には,RequestTransaction()は以下の条件の1つが満たされない限り例外を投げるということである.

  • Mathematica 5.1以降が使われている(5.1以降では,カーネルは必ず.NETと共有される)
  • MathematicaDoNETModelessを実行していて,カーネルがフロントエンドからの計算を行うことに忙しくない
  • あるいはを通してカーネル共有がオンにされており,カーネルは別の計算に忙しくない
  • Mathematica がすでに.NETへの呼出しの最中である

自分の.NETクラスをデバッグする

自分の好きなデバッガを使って Mathematica から呼び出された.NETコードをデバッグすることができる.唯一の問題は,これを行うためには一般にデバッガ内部で.NETプログラムを起動しなくてはならないということである.起動しなくてはならない.NETプログラムはInstallableNET.exeで,これは通常InstallNETを呼び出すと起動されるプログラムである.このプログラムはWolfram.NETLink.dllアセンブリファイルのすぐ隣のNETLinkディレクトリにある.

Visual Studio .NETを使っていて,.NET/Link で使っているクラスライブラリプロジェクトをデバッグしたい場合は,厳密なステップは使用している言語によって変わってくる.プロジェクトを選択し,ProjectメニューからPropertiesを選ぶ.Configuration PropertiesセクションのDebuggingパネルで,スタートアップのアプリケーションをInstallableNET.exeプログラムに設定する.コマンドラインの引数を-linkmode listen -linkname fooのようなものに設定する.その後デバッガを起動する.InstallableNETプログラムが起動され,Mathematica が接続されるのを待つ.Mathematica セッションの中で以下を実行する.

Click for copyable input

この操作は,InstallNETLinkObjectを引数として取ることができ,.NET自体を起動しようとしないので,うまくいく.これで MathLink 接続を手動で.NETと Mathematica の間に設立して,その後でそのリンクをInstallNETに与え,Mathematica と.NETの側をお互いがインタラクトできるように準備する残りの作業を行わせることができる.

DLLを Mathematica から呼び出す

はじめに

このセクションでは,.NET/Link を使ってDLL関数を Mathematica から呼び出す方法について触れる.これは従来のWindows DLLで,一般にC言語ライブラリ(ただし,他にもこのようなDLLを作成できる機能を持つ言語は多くある)である..NETの用語では,このような型のDLLは,.NETランタイム内で実行されないので,「アンマネージド」と呼ばれる.アンマネージドの関数を呼び出すタスクは,.NETとは全く関係ないように見えるが,.NET/Link はそのような関数を呼び出すために.NETの既存の施設を活用することができる.言い換えれば,.NETがDLLを呼び出せるので,Mathematica が.NETを呼び出せて,容易に Mathematica でDLLを呼び出すことができるようになるのである.

外部のC関数を容易に Mathematica から呼び出せるということは,Windowsプログラマーは,外部関数をラップして直接 MathLink 交信を処理するために,他のいわゆる「テンプレート」の MathLink プログラムを書く必要がほとんど全くないということである.別のチュートリアルでは,.NET/Link が.NETコードを呼び出すための特別のプログラミングの必要性をどのようにして排除するかを示した.このチュートリアルでは,.NET/Link がどのようにしてレガシーDLLを呼び出すこれらのステップも排除するかについて見ていく.

多くのプログラミング言語で,単に1行のコードを使ってDLL 関数を「宣言する」ことによって,これらの関数を呼び出すことができる.以下は,いくつかの言語によるそのような宣言の例である.関数はGetTickCount()であり,これはkernel32.dllで定義されるWindows APIの一部である.

Mathematica 関数のDefineDLLFunctionは上記の宣言に似ていて,DLLの名前,関数の名前,戻り値と引数の型を指定する.

In[142]:=
Click for copyable input
Out[142]=

DefineDLLFunctionは関数を返す.シンボルにこれを割り当てた後で,そのシンボルを関数の名前として使うとよい.

In[147]:=
Click for copyable input
Out[147]=

DefineDLLFunctionには4つの引数がある.第1引数はDLL内の関数の名前である.第2引数はDLLの名前で,DLLに完全パス名あるいは単にそのファイル名を与えることができる.(「DLLの見付け方」でどのようにしてDLLが .NET/Link で見付けられるかについて詳しく触れている.)第3引数は文字列として与えられる戻り型である.第4引数は引数の型のリストである.関数がGetTickCount()のように零個の引数を取る場合は,空のリストを指定する.型の指定は文字列であり,.NET/Link はさまざまな方法で型の指定をサポートする.型の指定については,「引数と戻り値を指定する」で詳しく触れる.

DefineDLLFunction["funcName","dllName",returnType,{argType,...}]指定のDLLから指定の関数を呼び出すのにふさわしい Mathematica 関数を作成する
DefineDLLFunction["declaration"]C#構文で与えられた完全な外部関数宣言から Mathematica 関数を作成する

DLL関数を定義する.

DefineDLLFunctionはいくつかのオプションをサポートしている.まず最初のCallingConventionは, DLL関数が標準慣例とは異なる呼出し慣例を使う場合に使用する必要がある関数である.ここで標準慣例とは,Windows CE以外のバージョンのWindowsにおける"stdcall" のことである.まれに関数は"cdecl"の呼出し慣例を使うこともある.呼出しメソッドがC++クラスの場合は,"thiscall"慣例を使うことができる.これらの値についての詳細は,System.Runtime.InteropServices.CallingConvention列挙についての.NET Frameworkドキュメントを参照されたい.ほとんどの場合,CallingConventionオプションはデフォルト設定のまま置いておかれる.

MarshalStringsAsオプションについては,セクション「文字列 」で,ReferencedAssembliesオプションについては,セクション「特別の属性を必要とする宣言」で触れている.

オプション名
デフォルト値
CallingConventionAutomaticDLL関数で予期される呼出し慣例(可能な値は,"StdCall","CDecl","ThisCall",Automaticである)
MarshalStringsAs
"ANSI"
文字列の引数 (char*, string,String) をどのようにしてDLL関数にそしてDLL関数からマーシャルされるべきか(可能な値は,"ANSI","Unicode",Automaticである)
ReferencedAssembliesAutomatic宣言で参照されるアセンブリの名前のリスト

DefineDLLFunctionのオプション.

DLLの見付け方

DefineDLLFunctionの第2引数は,関数が存在するDLLの名前である.DLLへの完全なパス名を指定することができるし,あるいは単にファイル名を与えて .NET/Link の自動検索機能がそれを見付けてくれることに頼ることもできる.DLLがシステムのPATHに置かれている場合,あるいは Mathematica アプリケーションディレクトリ内の特別なサブディレクトリに置かれている場合は,そのファイル名だけを使って見付けることができる.このアプリケーションディレクトリの自動検索のおかげで,ユーザにアプリケーションディレクトリ外の別の場所にDLLファイルをインストールさせなくても,2つ以上のDLLを含む Mathematica アプリケーションを配布できるようになる.

Mathematica アプリケーションは一般に単独のディレクトリとして(サブディレクトリと一緒に)配備され,Mathematica が見付けられる標準の場所のいずれかにインストールされる.これらの標準の場所は,$InstallationDirectory\AddOns\Applications,$BaseDirectory\Applications$UserBaseDirectory\Applicationsとして書くことができる.ここで$InstallationDirectory$BaseDirectory$UserBaseDirectoryはこれらの組込みの Mathematica シンボルで与えられる場所を参照する.Mathematica アプリケーションに .NET/Link, を通して呼び出されることを意図したDLLが含まれる場合は,アプリケーションディレクトリをこれらの標準の場所のいずれかにインストールする必要があり,DLLはアプリケーションディレクトリのLibraries\Windowsサブディレクトリに置かれなければならない.「.NET/Link を使うアプリケーションを配布する」ではアプリケーションディレクトリのレイアウトについてさらに詳しく触れる.

DefineDLLFunctionを呼び出すときにはDLLを見付けようという努力はなされない.これは関数が最初に呼び出されたときにだけ起ることである.つまり,.NET/Link がDLLあるいはその中にある指定の関数を見付けることができない場合は,関数が呼び出されるときだけエラーメッセージが表示され,関数が定義されるときには表示されない.

引数と戻り値を指定する

はじめに

戻り値と引数の型を特定する場合は,"int","double","void"等の文字列を使う.自分が最も使いやすい言語(C, C#,Visual Basic .NETのいずれか)の構文に従う型の名前を使ってよい.また,"WORD","BOOL","LPSTR"等Windows APIで使われる型の名前の多くを使うこともできる.ほとんどの場合,C言語の関数プロトタイプから作業をすることになり,そのプロトタイプからの名前を直接使うのが最も便利である.例えば,Standard Cライブラリのmath.hヘッダファイルからfloor()関数の以下の宣言を使うとしよう.

Mathematica からこの特定の数学関数を呼び出したいことはほとんどあり得ないが,これは,誰もが持っているDLLからの簡単な例として役立つ.Windowsでは,CのランタイムライブラリはDLLのmsvcrt.dllの中にある.以下はDefineDLLFunctionを使ってfloor()関数を呼び出す Mathematica 関数を作成する1つの方法である.

In[158]:=
Click for copyable input
In[161]:=
Click for copyable input
Out[161]=

DefineDLLFunctionでは,Cの型の名前を直接使うことができる.これはヘッダファイルからC言語のプロトタイプを見るときには便利である.以下は同じ定義を作る同等の方法である.

Click for copyable input

C#あるいはVisual Basic .NETの構文を型の名前に使うことは,これらの言語の1つのサンプルコードのいくつかから外部関数宣言をコピーしている場合には便利である.実際,DefineDLLFunctionを使う最も簡単な方法は,C#あるいはVisual Basic .NETのサンプルコードの外部関数用の既存の宣言を見付けて,その宣言で使われている型の名前を単にコピーするという方法である.以下ではfloor()のための宣言がこれらの言語でどのようになるのかを示している.

Visual Basic 6のDeclare Function文を正しい型の名前のガイドとして使うこともできるが,VB 6とVB .NETとの間にはいくつかの重要な違いがあることに注意されたい.まずVB 6では,パラメータはデフォルトでByRef(これらはVB .NETではデフォルトでByValである)であるので,VB 6 宣言でのDoubleのような型の名前は,ByRef Doubleと変換されるべきである.また,VB 6のIntegerは,VB .NETのShortに等しく,VB 6のLongは,VB .NETのIntegerに等しい.VB .NETに適切な型の名前を使う必要がある.

以下のサブセクションでは使用できる型の名前についてさらに詳しく見ていく.

プリミティブ型

以下の表は,どの型の名前をプリミティブ型(例えば,整数,実数,ブーリアン等)に使うことが正しく,どの型をそれらのプリミティブ型が Mathematica にマップするかについて示している.

外部関数宣言の型
Mathematica の型
C言語の名前:Integer
char, int, short,     long (and unsigned versions)
C#の名前:
byte, sbyte, char, short,     int (and unsigned versions)
Visual Basic .NETの名前:
Short, Integer,     Long (these are all ByVal)
.NET Frameworkの名前:
Byte, SByte, Char, Int16, UInt16, Int32, UInt32, Int64, UInt64
Win32 APIの名前:
BOOL, BYTE, SHORT, INT, UINT, LONG, WORD, DWORD, LPARAM, WPARAM
float, double, Single, DoubleReal
bool, BooleanTrue あるいは False
void, Void戻り値に使われる場合はNull;ゼロ引数の関数には {} を引数の型のリストとして使う

DefineDLLFunctionのプリミティブ型に対する正しい型の指定.

上の型の名前を使うことは簡単であるはずである."long"は標準Windows C long(4 バイト)を意味し,8バイトであるC# longは意味しないことに注意されたい.またWindows APIのBOOL型は,TrueおよびFalseではなくむしろ整数(0および非零)にマップされる.プリミティブ型のへのポインタとプリミティブ型の配列については,後のサブセクションで触れる.

文字列

文字列を取って返すDLL関数を呼び出す際に気を付けなくてはならない細かいことがいくつかある.これらは, DLL関数が文字列をどのように(ANSIスタイル,1バイト,ヌル終端の文字列,あるいはUnicode,2バイト,ヌル終端の文字列として)表示することを期待するかということにかかわっている. データがシステムの境界上を移動するにつれてそれを1つの表記法から別のものに変換するプロセスはマーシャリングと呼ばれる.ほとんどのDLL関数ではこのよくあるC文字列の形式を処理するように書かれているので,アンマネージドコードを呼び出す際の.NETのデフォルトの動作は,文字列を1バイトでヌル終端の文字列としてマーシャルするということである.デフォルト以外の動作が必要な場合は,DefineDLLFunctionMarshalStringsAsオプションを使うとよい.

以下は,異なる型の文字列を操作するWindows Cランタイムライブラリからの2つのDLL関数の例である.それぞれが文字列を小文字に変換する.ここで,_strlwr()関数はANSIの文字列を取り,_wcslwr()関数はワイド文字の文字列を取る.

以下は,これらの関数の両方に対するDefineDLLFunctionの呼出しである._wcslwr()関数はワイド文字の文字列を取って返すので,文字列のデフォルトのマーシャリングをオーバーライドする必要がある.

In[197]:=
Click for copyable input

どちらの関数も1バイトにフィットする文字を持つ文字列に対しては同じように動作する.

In[199]:=
Click for copyable input
Out[199]=

予想されるように,strlwr関数は2バイトを必要とする文字を持つ文字列に対しては不成功に終る. の文字は1バイトに切り捨てられる(この例では,切捨ては文字列が.NETからDLLに渡される際に起るが,DLLから出る際にも起る)ことに注意されたい.

In[200]:=
Click for copyable input
Out[200]=

ワイド文字のバージョンを呼び出す場合はうまくいく.

In[202]:=
Click for copyable input
Out[202]=

パラメータあるいは戻り型として2つ以上の文字列を持ち,文字列が異なる方法でマーシャルされなくてはならない場合には,MarshalStringsAsオプションは関数のすべての文字列に適用されるので,この関数を使うことはできない.代りにDefineDLLFunctionの特別の「完全宣言」形式を「特別の属性を必要とする宣言」で説明するように使うことができる.

外部関数宣言の型
Mathematica の型
C言語の名前:String
char*
C#の名前:
string
Visual Basic .NETの名前:
String
.NET Frameworkの名前:
String
Win32 APIの名前:
LPSTR, LPCSTR

DefineDLLFunctionの文字列に対する正しい型の指定.これらはすべて等しい.

_strlwr()および_wcslwr()に対するDefineDLLFunctionでは,C#構文である型の名前"string" を使って文字列を示した.上の表で示すように,以下はまったく同等の宣言である.

Click for copyable input

ここまで関数の"[in]"パラメータとして使われる文字列だけを見てきた.つまり,文字データが関数の中に送られている場合だけを見てきた.char*を取るように入力される関数の中には,文字列を"[out]"パラメータとして使うものもある.つまり,これは実際には関数によって書き込まれるバッファである.文字列を[out]パラメータとして使う関数は一般に,割り当てた文字列のバッファの長さを与える余分な引数で渡すことを必要とするか,あるいはこれらの関数がバッファに書き込むことになる文字の最大数をドキュメント化する.文字列にデータを書き込む関数の例は,馴染み深いStandard Cライブラリ関数のsprintf()である.

bufferの引数は,関数が書き込む文字列である.これは[out]パラメータであり,データの文字列をこの関数に上書きするために渡すことはできる(ただし文字列は書かれたデータが文字列の長さを超過しないだけの長さを持っていなくてはならない)が,修正した文字列をもとに戻すことはできない..NETランタイムでは[out]文字列パラメータで作業するための特別の方法をサポートする.これは,System.Text.StringBuilderクラスのインスタンスを使用するという方法である.StringBuilderは文字のバッファとしてアンマネージド関数にマーシャルされる.関数が戻った後で,StringBuilderオブジェクトがバッファに書き込まれたデータを保持する.StringBuffer.ToString()メソッドを使ってデータを文字列として抽出することができる.

これがsprintf()関数を使ってどのように行われるかを見てみよう.この関数は変数引数カウントを取るが,DefineDLLFunctionはこれを処理できないので,書式文字列(3つの完全引数)に続く1つの整数引数の場合について特別にバージョンを定義する.

In[222]:=
Click for copyable input

上のDefineDLLFunctionへの呼出しで1つ注意しなくてはならないことは,const限定子を任意の引数スロットについて指定することができるということである.これは .NET/Link の目的には関係がないので,.NET/Link には無視されるが,宣言をもっと自己文書化するのに役立つと考える場合や,ただやみくもにC関数のプロトタイプをコピーしている場合には,この限定子を使ってもよい.

sprintfを呼び出すためには,まずその中に書き込まれるかもしれないデータがすべて入るのに十分な大きさのバッファを持つStringBuilderオブジェクトを作成する.この例では小さな文字列を使うので,20バイトで十分である.

In[223]:=
Click for copyable input
Out[224]=

戻り値はバッファに書き込まれる文字の数である.文字列を見るためには,ToString()を呼び出す.

In[225]:=
Click for copyable input
Out[225]=

配列とポインタ

ポインタあるいは配列を取ったり返したりする関数を取り扱っている場合は,もう少し注意を払って,呼び出している関数でパラメータがどのように取り扱われているかを必ず理解する必要がある.例えば,型int*のパラメータがある場合,このパラメータは以下のいずれかである.

  • 関数に渡される整数の配列([in]配列)
  • 関数によって書き込まれることになる整数の配列([out]配列)で,配列の初期値を必要とする場合もある([in, out]配列)
    • 関数によって書き込まれる値を持つ整数変数のアドレス([out] int)で,初期値を必要とする場合もある([in, out] int)

    これらの可能な値のそれぞれについて,DefineDLLFunctionで異なる型の指定が必要である.例として,Standard Cライブラリからのmodf()関数を見てみよう.

    この関数は,2つのdを整数と分数に分解する.分数が戻り値で,整数値はpintによって二重点で保持される.この説明から,double*パラメータが配列ではなく,([out] double)に書き込まれることになる2アドレスであることが分かる.プロトタイプから直接型の名前を使ってDefineDLLFunctionを呼び出して,何が結果として出るか見てみよう.

    In[251]:=
    Click for copyable input

    上の例のようにdouble*のようなポインタの型を直接DefineDLLFunctionで使うと,.NET/Link はパラメータが[in, out] double(上の箇条書きリストの最後の項目)であると想定する.C#表記では,この型のパラメータはref doubleと,そしてVisual Basic .NET 表記ではByRef As Doubleと呼ばれる.「Out」と「Ref」のパラメータから,refパラメータスロットに渡さなくてはいけないのは,正しい型の値が入るシンボルであり,このシンボルは出る際に修正された値を割り当てられる可能性もあるということが分かっている.これはつまり,modfを呼び出して,それをintegerPartと呼ばれるシンボルの第2引数に割り当てるために,integerPartに呼出しの前に数値を与える必要があり,さもなければ,.NET/Link が悪い引数について文句を言うということである.

    In[253]:=
    Click for copyable input
    Out[254]=
    In[255]:=
    Click for copyable input
    Out[255]=

    これは,第2引数が概念的には[out] doubleであって,[in, out] doubleではなく,関数に入る値は使われないので,理想的ではない.したがって,呼出しの前に値を与えなくてはならないということには意味がない.定義を改善するために,型の指定としてout doubledouble*の代りに使うとよい.

    In[256]:=
    Click for copyable input
    Click for copyable input
    Out[258]=
    Out[259]=

    要約すると,ポインタの型を直接DefineDLLFunctionで使う場合は,.NET/Link はこれをref(C#表記)あるいはByRef(VB表記)のパラメータとして扱う.これがもし正しくパラメータの使用を捕まえない場合は,別の型の指定を使うべきである.C#表記を型の名前に使う方が,書き込まれることになる整数のアドレスとして取り扱われる int*パラメータがout intとして指定されるので,望ましい.値が関数によって読み込まれ,そして書き込まれる場合は,ref intとして指定される. Visual Basic .NETは「純粋な」[out]パラメータのための構文を持たないので,この場合C#表記を使用するのが最適である.

    配列パラメータの場合について考えてみよう.C関数プロトタイプでは,int*はしばしばintの配列を意味することがある.ただし,これは通常int[]と書かれる.データの配列をDLL関数に渡す必要がある場合は,パラメータの型は配列のカッコでint*ではなくint[]のように( DefineDLLFunction が明示的にポインタとして宣言される型をどのように取り扱うかについてはすでに上で触れた)書かれなければならない.次の2つの(架空の)DLL関数のプロトタイプを考えてみよう.

    SumArray()関数は整数の配列と配列の長さを取り,総和を返す.以下はDefineDLLFunctionと関数への呼出しをどのように書くかを示す.

    Click for copyable input

    カッコ表記を使って配列が関数に渡され,これを Mathematica から整数のリストで呼び出す.

    ReverseArray()関数は全く異なる.これは適切な場所にあるデータの配列を逆にするので,配列は[in, out]パラメータとして使われる.これは以下のように定義することができる.

    Click for copyable input

    しかし,これを次のように呼び出した場合に何が起るかを考えてみよう.

    Click for copyable input

    DLL関数がintの配列を受け取りそれを逆にするという意味ではこれは成功するが,修正された配列を関数からもとに伝播することはどうやってもできない.ref int[]が使用できる正しい型であるように見えるかも知れないが,実際には,refあるいはoutを型に加えるということは実質的に方向性の欠如のレベルを加えるということであるので,これは(int[])*に変換される.これを解決する方法は, .NET 配列オブジェクトを作成してそのオブジェクトを関数に渡すという方法である.関数が戻ってから,配列データを Mathematica リストとして得ることができる.配列を取るために入力される引数スロットがあると,.NET/Link のどこででも,これを Mathematica からリストあるいは適切な型の配列である.NETオブジェクトへの参照と一緒に呼び出すことができるので,この方法はうまくいくのである.以下は,配列に書き込まれるDLL関数を呼び出す正しい方法である.

    Click for copyable input

    しかし,以下のようにそれを呼び出した場合には何が起るかを考えてみよう.

    Click for copyable input

    例題ファイルには,.NET FrameworkのIntPtr型を使って一般的ポインタを表すことを含めて,ポインタ関係の方法がもっと含まれている.

関数ポインタ

DLL関数の中には,コールバック関数ポインタを引数として取るものがある..NETは関数ポインタを代表にマップするので,関数ポインタ引数に適切な型の代表オブジェクトを渡す.「イベントを処理する」ではNETNewDelegate関数とそれがDLL呼出しの中で関数ポインタの代表オブジェクトを作成するためにおもに使われることが紹介されている. 関数ポインタについて適切なシグナチャが付いた既存の.NETの代表の型がないということがよくあるが,DefineNETDelegate関数を使って適切なシグナチャ付きの.NETの代表の型を作成することができる. EnumWindows.nbの例題ファイルは,DefineNETDelegateおよびNETNewDelegateを使ってコールバック関数ポインタを引数として取るDLL関数を呼び出すことを示す.

特別の属性を必要とする宣言

.NETランタイムは,関数がどのように呼び出されて引数がどのようにマーシャルされるかを精密に制御するために数多くの属性をサポートする.DefineDLLFunctionCallingConventionおよびMarshalStringsAsのオプションは,これらの側面にある程度のコントロールを与えるが,使用できる属性すべてを十分にサポートするものではない.以下は,Windows APIからのMoveFile()関数の複雑なC#宣言に関する.NET Frameworkのドキュメントからの例である.この宣言は,わざとかなり複雑なものになっているが,可能な属性のいくつかを示している.

DefineDLLFunctionのオプションでできる以上の属性を指定する必要がある場合は,完全な宣言をC#構文の文字列として指定できる別の形式を使うとよい.

Click for copyable input

このバージョンの .NET/Link では,C#構文だけがサポートされていて, Visual Basic .NETはされていない.

この型の完全な宣言が必要となる関数のもう1つの例は,別々のマーシャリング慣例が必要な2つの文字列引数を持つ関数である.

以下はどのようにしてこれを Mathematica で定義するかを示す.

Click for copyable input

DLL宣言がSystemアセンブリにはない型を使う場合には,ReferencedAssembliesオプションを使ってそのアセンブリを指定する必要がある.これはVisual Studioプロジェクトでアセンブリに参照を加えるのに似ている.以下はこのオプションを使った例である.RectangleクラスはSystem.Drawingアセンブリにあるので,これを明示的に参照アセンブリとして指定しない限りエラーが出る.

Click for copyable input

例題ファイル

.NET/Link に含まれている以下の例題ファイルは,CスタイルのDLLを Mathematica から呼び出すことを示す.

BZip2Compression.nb

WindowsAPI.nb

EnumWindows.nb

COMを Mathematica から呼び出す

はじめに

.NETランタイムには,COM(コンポーネントオブジェクトモデル.ActiveXとも呼ばれる)との相互運用性をサポートする数多くの機能が含まれている..NETの登場によって,COM/ActiveXは公けには「レガシー」テクノロジーとなったが,今もまだ使われている数多くのCOMオブジェクトとライブラリがあり,COMはまだWindowsのプログラミングの世界で重要な位置を占めている.COMオブジェクトは簡単に.NETから呼び出すことができるので,Mathematica から .NET/Link を通して呼び出すことも簡単にできる.

COMプログラミングは複雑(Microsoftがこれを.NETに取って代らせた理由の1つはここにあることは間違いない)であり,これを読んでおられる皆さんがCOMについての基本的な知識を持っているものとここでは想定する.COMと.NETの相互運用性の問題についてはずいぶん詳しく.NET SDKのドキュメントで取り上げられている.

.NETからCOMへの相互運用性の中心的要素は,ランタイム呼出し可能ラッパー (Runtime Callable Wrapper: RCW) と呼ばれる特別のプロキシオブジェクトである.COMオブジェクトを作成してそれを.NET環境にインポートしたい場合はいつでも,.NETランタイムが.NET環境内のCOMオブジェクトを表すRCWオブジェクトを作成する. RCWは.NETからCOMへの呼出しの仲立ちをし,引数をまとめて値を.NETとCOMの世界の間で行き来させる. RCWオブジェクトの例はこの後のセクションに掲載されている.

.NET/Link はCOMオブジェクトを Mathematica から呼び出す2つの主要な方法を提供する.最初の方法はいわゆるCOMオートメーション(遅延結合)を使う方法である.これは,準備が不要であるため便利であるが,後で触れるようにさまざまな理由から理想的な方法であるとは言えない.好まれる2つ目の方法は,呼び出したいCOMオブジェクトに対して相互運用アセンブリを作成,あるいは獲得するという方法である.相互運用アセンブリは,COMライブラリをラップしてライブラリの型とインターフェースをネイティブの.NETの型ように見せる特別の.NETアセンブリである.COMオブジェクトを呼び出すこれらの2つのメソッドについて次の2つのセクションで触れる.

オートメーション(遅延結合)を使う

COMインターフェースは,基本的には関数ポインタの表にすぎない.これはC++プログラマーが使用するのには理想的であるが,COMオブジェクトを使用するために言語をスクリプトする方法が必要である.しかし,この言語にはコンパイルの段階がなく,C++ヘッダファイルへのアクセスがない.この問題を解決するのが,IDispatchと呼ばれる特別のCOMインターフェースである.IDispatchを実装するCOMオブジェクトは,オブジェクトのユーザがランタイムに使用できるメソッドとプロパティを見付けて,それらを呼び出せるようにする.IDispatchは,COMにおいて .NETの「リフレクション」機能に匹敵するものである.COMオブジェクトをそのIDispatchインターフェースを通して使用することは,しばしば遅延結合,オートメーション,ディスパッチ等と呼ばれる.ここではこれをオートメーションを呼ぶ.

すべてのCOMオブジェクトがオートメーションをサポートするわけではないが,できる限り幅広い種類のプログラミング言語と環境で使えるようにしたいCOMオブジェクトの多くがこれをサポートしている.Visual Basic 6ではCOMオブジェクトをオートメーションあるいは初期結合(後で触れる)を通して使うことができる.しかしVBScriptを含めたほとんどのスクリプト言語では,オートメーションしか使えない..NET/Link ではオートメーションも初期結合も使える.初期結合の方が好まれる方法で,これについては,次のセクションで触れる.ここではオートメーションに焦点を当てる..NET/Link でオートメーションを通してCOMオブジェクトを使うことは,Visual BasicあるいはVBScriptでオートメーションを使うのとほとんど全く同じである.自分が興味を持つCOMオブジェクトを使用するサンプルコードをこれらの言語のいずれかで見付けたら,一般にそのコードを一字一句違えず Mathematica に変換することができる.

COMライブラリの例として,このセクションではMicrosoft Speech APIを使用する.MicrosoftはいずれそのAPIのすべてを純粋な.NET実装に移動させるであろうが,COMオブジェクトとしてしか使用できないものもまだ多くあり,Speech APIはその1例である.(MicrosoftにはSpeech Application SDKと呼ばれる.NETベースの言語ツールがある.これは,テレフォニアプリケーションを作成しているASP .NETの開発者用のものであり,古いCOMベースのSpeech APIとは違うことに注意されたい.後者が次の例では使われている.)単に入出力を読めばよいので,このセクションを理解するのにSpeech APIをインストールする必要はないが,入力を再評価したい場合,あるいは自分で実際に操作してみたい場合は,Speech APIを http://www.microsoft.com/speech/download/sdk51/ からダウンロードするとよい.CreateCOMObjectを呼び出す行がうまくいかない場合は,Speech APIがインストールされていないということである.

オートメーションを通したコントロールのためのCOMオブジェクトを作成する基本関数はCreateCOMObjectである.この関数は,Visual BasicのCreateObject()関数に似ている.CreateCOMObjectの引数は,COM coclassのProgIDあるいはCLSIDを提供する文字列である.ProgIDがExcel.Application等の人間が読み取れる文字列であるのに対し,CLSIDは"{000208d5-0000-0000-c000-000000000046}"等の16進数字列である.COMオブジェクトのCLSIDあるいはProgIDは,そのドキュメントから,あるいはもっとよい方法としては,それを使うVisual Basicのサンプルコードから得ることができる.

CreateCOMObject["ProgID"]指定のProgID(例えば,Excel.Application)を持つCOMオブジェクトを作成する
CreateCOMObject["CLSID"]指定のCLSID(例えば,{000208d5-0000-0000-c000-000000000046})を持つCOMオブジェクトを作成する
GetActiveCOMObject["ProgID"]指定のProgID(例えば,Excel.Application)を持つすでにアクティブなCOMオブジェクトへの参照を得る
GetActiveCOMObject["CLSID"]指定のCLSID (例えば,{000208d5-0000-0000-c000-000000000046})を持つすでにアクティブなCOMオブジェクトへの参照を得る

COMオブジェクトを得る.

以下はSpVoice COMオブジェクトのインスタンスを作成する.

In[1]:=
Click for copyable input
Out[1]=

voiceオブジェクトは,これまで見てきた.NETオブジェクトは異なっている.このオブジェクトは,「はじめに」で触れたオブジェクトのクラスであるランタイム呼出し可能ラッパー (RCW) である.これは.NETの世界のCOMオブジェクトを表すプロキシオブジェクトであると考えることができる.ほとんどの.NET オブジェクトでは,オブジェクトのOutputForm表記のカッコの中の文字列は,オブジェクトの.NETの型の名前を与える.voiceオブジェクトでは異なっていて,文字列はオブジェクト(SpeechLib.ISpeechVoice)でサポートされるデフォルトのCOMインターフェースの名前を示す.以下は,オブジェクトの実際の型の名前である.

In[2]:=
Click for copyable input
Out[2]=

もう気付いている方もいらっしゃると思うが,System.__ComObjectはRCWクラスの名前である..NET オブジェクトが Mathematica<<NETObject[System.__ComObject]>> と表されているのを見ても,これは任意のCOMオブジェクトであり得るために,何もオブジェクトについて何も語らないので,あまり情報としては役に立たない..NET/Link がRCWオブジェクトを Mathematica に返すときには,.NET/Link はオブジェクトがサポートするデフォルトのCOMインターフェースの名前を見付けようとする.これが成功すると,.NET/Link はオブジェクトを<<NETObject[COMInterface[Default.COM.Interface]]>>として報告する.覚えておかなければならないことは,これはCOMインターフェースの名前であり,.NETあるいは .NET/Link には何の意味も持たないということである.これは単にこの.NETオブジェクトが表すCOMオブジェクトについての情報を何か知らせようとするために表示されるだけである..NET/Link がデフォルトのCOMインターフェースの名前を見付けるためには,オブジェクトはCOMのタイプライブラリを通して十分に詳しい型の情報を提供する必要がある.ほとんどのCOMオブジェクトはこの機能を持っているので,COMのインターフェース名でフォーマットされたRCWオブジェクトをよく見かけることになる.しかし,デフォルトのインターフェース名の検索に失敗する場合もあり,その場合COM オブジェクトは<<NETObject[System.__ComObject]>>としてだけフォーマットされる.

オートメーションを通してCOMオブジェクトを使うことの1つの問題は,NETTypeInfoを使ってCOMのメソッドおよびプロパティについての情報を得ることができないということである.

In[3]:=
Click for copyable input
Out[3]//TableForm=

COMオブジェクトについてメソッドやプロパティを呼び出すことはできるが,これらのメソッドを直接.NETから見ることはできない.メソッドとプロパティ,およびその引数についての情報は,COMオブジェクトのドキュメントを見る必要がある. Visual BasicのサンプルコードはしばしばCOMオブジェクトを使って見付けることができ,これを Mathematica から .NET/Link を通して使うことは,ほとんど同じように見える.

ISpeechVoiceのCOMインターフェースは,Volumeと呼ばれるプロパティを含む.

In[4]:=
Click for copyable input
Out[4]=

Speak()メソッドはテキストの文字列を示す.以下は,SpeechLibのタイプライブラリのIDL ファイルからのSpeak()メソッドに対する宣言である.

ベテランのCOMプログラマーならこの宣言の要素に見覚えがあるであろう.第1引数はBSTRで,これはCOMの世界での文字列である.このような引数は.NETからの文字列と一緒に,つまり Mathematica からの文字列と一緒に呼び出される.第2引数はオプショナルという印が付いていて,デフォルトの値は0である.これはつまりSpeak()メソッドは第2引数なしでも呼び出せるということである.

In[5]:=
Click for copyable input

第2引数は型SpeechVoiceSpeakFlagsであるとしてリストされており,この型はどのようにテキストが話されるかを制御する定数を含むCOM列挙である.オートメーションを通してCOMオブジェクトを使う難点は,COM列挙にアクセスできる方法が全くないということである.第2引数を使うためには,enumの正しい値に対応する整数値を供給する必要がある.この情報はドキュメントから,あるいはOLE Viewのようなツールを使ってタイプライブラリそのものから得ることができる.OLE ViewはMicrosoft Visual Studioにバンドルされている.非同期的に意見を話すためには,つまりテキストが話し終える前にSpeak()メソッドが返すためには,1の値を持つフラグSVSFlagsAsyncを使う.

In[6]:=
Click for copyable input

オートメーションを使うことの難点

上では,.NET/Link でどのようにCOMオブジェクトをそのIDispatchインターフェースを通して使うことができるかということについて見た.これはなにも準備がいらないという点では優れているが,いくつかの難点もある.

  • オブジェクトのデフォルトのインターフェースに対してだけメソッドを呼び出すことができる.
  • NETTypeInfoを使ってオブジェクトのメソッドおよびプロパティについての情報を得ることができない.
  • COMの列挙あるいはストラクトにアクセスできない.
    • COMイベントを使えない.

    これらの難点は,COMをVBScriptのような純粋のスクリプト言語から使ったときに起る問題と同じである.次のセクションでは,COMを .NET/Link と一緒に使うもっとよい方法について触れる.

相互運用アセンブリ(初期結合)を使う

前セクションでは, .NET/Link でCOMオブジェクトをそのIDispatchインターフェースを通して使うことについて触れた.この方法はしばしば遅延結合あるいはオートメーションと呼ばれる.この方法にはいくつかの使用上の難点があるが,幸い.NET は,COMオブジェクトを初期結合と呼ばれるもっと洗練されて効率的な方法で呼び出すことをサポートする.これはCOMオブジェクトがC++で使われる方法に似ていて,メソッドの発送はIDispatchではなくvtableインターフェースを通して起る..NETで初期結合を使うためには,まずいわゆる相互運用アセンブリを作成するか見付けるかする必要がある.相互運用アセンブリは,COMのタイプライブラリの型およびメソッドを表すメタデータを含んでいる特別のアセンブリである.一旦相互運用アセンブリがあると,他のどのような.NET アセンブリとも同じようにロードして使用することができて,それを表すCOMの型はクライアントにとってネイティブの.NETの型のように見える.

相互運用アセンブリは,tlbimp.exe(タイプライブラリインポータ)と呼ばれるツールを使って作成することができる.これは.NET Framework SDK,およびVisual Studio .NETに含まれている.tlbimpの実行の仕方については多くのドキュメントが出ており,後で1つの例を見る.また,プログラム的に相互運用アセンブリを作成することも可能で,.NET/LinkLoadCOMTypeLibrary関数をこの目的に提供する.LoadCOMTypeLibraryは,COMのタイプライブラリを取り,それから相互運用アセンブリを作成し,このアセンブリを .NET/Link にロードする.これをLoadNETAssemblyに似たものと考えることができるが,代りにCOMのタイプライブラリへのパスを取るという点で異なる.

LoadCOMTypeLibrary[typeLibPath]特定のタイプライブラリから相互運用アセンブリを作成し,これをロードする

COMのタイプライブラリをロードする.

上では,COMベースのMicrosoft Speech APIをオートメーションを通して使ったが,もっとよい方法は,初期結合が行えるようにタイプライブラリをロードするという方法である.

In[7]:=
Click for copyable input
Out[7]=

LoadCOMTypeLibraryで作成された相互運用アセンブリは,ネームスペースと型のネーミングについてのデフォルトの規則で作成され,NETTypeInfoをアセンブリに使ってどの型が使用できるかを見るのに便利である.COMのタイプライブラリの各coclassについて,クラスは"Class"という言葉が加えられたcoclassの名前が付いた相互運用アセンブリで作成される.先ほどSpVoiceと呼ばれるcoclassがあることを見たので,相互運用アセンブリでSpVoiceClassと呼ばれる.NETクラスが見付かることが予想できる.このアセンブリには多くの型があり,以下はそのクラスだけを示している.

NETTypeInfoは相互運用アセンブリを探索する上で大変便利である.上でオートメーションを使ってCOMオブジェクトを作成したときにCreateCOMObject関数を使用した.ここではCOMのcoclassを表す.NETクラスがあるので,代りにNETNewを呼び出すことができる.

In[9]:=
Click for copyable input
Out[9]=

このオブジェクトはその他の.NETオブジェクトと同じように使用できる.以下はSpeak()メソッドである.なぜか第2引数のオプショナルの性質は相互運用アセンブリでは保持されないので,2つの引数でSpeak()を呼び出す必要がある.

In[10]:=
Click for copyable input
Out[10]=

オートメーションを使用したときには,第2引数はSpeechVoiceSpeakFlagsと呼ばれるCOM列挙であることをさきほど見た.しかし,オートメーションを使用している場合には列挙にアクセスする方法は全くなく,このため整数値を渡さなければならなかった.これに対して相互運用アセンブリでは,コードをもっと読みやすくするために使える.NET 列挙がある.

In[11]:=
Click for copyable input

相互運用アセンブリを使う大きな利点の1つは,NETTypeInfoを使ってオブジェクトがサポートするメソッドおよびプロパティについての情報を得ることができるということである.以下は,voiceオブジェクトのプロパティである.

一旦相互運用アセンブリがタイプライブラリのためにロードされると,CreateCOMObjectをCOMのcoclassの名前で呼び出す場合に,以前のような生のRCWではなく,そのCOMのcoclassに対応するクラスのネイティブの.NETオブジェクトを返される.これはつまりCreateCOMObjectおよびNETNewは,COMのcoclassのインスタンスを作成するための同等の方法になるということである.

In[13]:=
Click for copyable input
Out[13]=
オプション名
デフォルト値
SaveAssemblyAsNone作成したいアセンブリファイルへの完全パス名
SafeArrayAsArrayFalseSAFEARRAYの型をSystem.Arrayとしてマーシャルするかどうか

LoadCOMTypeLibraryのオプション.

LoadCOMTypeLibraryは,アセンブリ作成のプロセスを制御する2つのオプションを取る.1つはSaveAssemblyAsで,これで作成したアセンブリを保存したいファイル名を指定することができる.LoadCOMTypeLibraryは,実行するのに時間がかかるので,アセンブリをファイルに保存して,それを将来LoadNETAssemblyを使ってロードするすると便利である.これでLoadCOMTypeLibrarytlbimp.exeツールを実行することとプログラム的には同等となり,生成されたアセンブリをその中で書き出すことができる.LoadCOMTypeLibraryの2つ目のオプションはSafeArrayAsArrayで,これはすべてのCOMのSAFEARRAY配列を入力された一次元マネージド配列としてよりもむしろSystem.Arrayクラスとしてインポートするかどうかを指定する.デフォルトはFalseである.この上級オプションの詳細については,System.Runtime.InteropServices.TypeLibImporterFlags列挙についての.NET Frameworkドキュメントを参照されたい.生成されたアセンブリに対してもっとコントロールが必要な場合は,次のセクションで触れるtlbimp.exeツールを使うとよい.

COMオブジェクトをオートメーションを通して使用することについて上で触れた際に,RCWの役割について述べた. 生のRCWのクラス名はSystem.__ComObjectであることもここで見た.相互運用アセンブリを使っているときでも,すべてのCOMオブジェクトは.NETでRCWとして表されることをここで思い出すことが大切である.以下では,SpVoiceClass__ComObjectに由来するものであることが分かる.

以下は,継承関係を証明するもう1つの方法である.

In[15]:=
Click for copyable input
Out[15]=

tlbimp.exeを使って相互運用アセンブリを作成する

上でも述べたように,.NET Framework SDKにはtlbimp.exe(タイプライブラリインポータ)と呼ばれるツールが含まれており,COMのタイプライブラリから相互運用アセンブリを作成する.このツールを使って相互運用アセンブリを作成し,LoadCOMTypeLibrary関数の代りにLoadNETAssemblyを使ってそれをロードすることができる. tlbimpプログラムにはどのようにアセンブリが生成されるかということを制御する多くのオプションが含まれているので,このレベルのコントロールが必要な場合は,これを間違いなく手動で実行するようにすべきである..NET Framework SDKのドキュメントには,tlbimpの使い方が詳しく述べられているが,以下にSpeechLibのタイプライブラリ用の相互運用アセンブリを作成するのにtlbimpをどのように使えばよいかの例を示す.

一旦アセンブリが作成されたら,他のアセンブリと同じようにそれをロードするとよい.

Click for copyable input

プライマリ相互運用アセンブリ

プライマリ相互運用アセンブリ (PIA) は,ベンダーの署名付きで,グローバルアセンブリキャッシュ (GAC)に置くことができるように強い名前を付けられた特別の相互運用アセンブリである.PIAは,COMのタイプライブラリのベンダーがそのタイプライブラリに対して理想的なインターフェースを表す公式の相互運用アセンブリを作成するであろうという考えのもとに作られたものである..NETランタイムは,PIAを特別で幸運なアセンブリであると認識し,関連するPIAがインストールされているcoclassにCreateCOMObjectを呼び出すときに自動的にPIAをロードする.COMライブラリを使用する場合は,ベンダーがそれ用にPIAを作成しているかどうかをチェックして,もししていたら,それをインストールすべきである.

PIAのよい例は,MicrosoftがOffice XPスイートに合うように作成したセットで,これはオートメーションのリッチオブジェクトモデルを公表している.WordあるいはExcelのようなOffice XPコンポーネントを Mathematica から制御しようと考えている人なら誰でも,Office PIAをhttp://msdn.microsoft.com/library/default.asp?url=/downloads/list/office.asp から取得すべきである.これらのPIAはデフォルトでOffice 2003と一緒にインストールするようになっているが,Office XPとおそらくもっと古いバージョンのOfficeで使うためにもこれをインストールすることができる.

ExcelPieChart.nbの例題ファイルは,Excelを Mathematica から .NET/Link を使って呼び出す方法を示す.Office PIAをインストールしているしていないにかかわらずこの例を使うことができるが,PIAがあると,強く定型化された.NETオブジェクトを生のRCWオブジェクトの代りに使って作業できるという利点がある.PIAがお使いのマシンにインストールされていて,CreateCOMObjectがExcelのインスタンスを起動するために呼び出される場合は,PIAが自動的に<<NETObject[COMInterface[Excel._Application]]>>のような生のRCWではなく.NET の型をExcel の相互運用アセンブリから返す.

In[16]:=
Click for copyable input
Out[16]=

COMリソースを解放する

RCWオブジェクトの.NETにおける役割の1つに,それをラップするCOMオブジェクトのライフサイクルを管理するということがある.COM オブジェクトは,RCWオブジェクトが.NETのガベージコレクタによって解放されたときに破棄される.しばしばCOMオブジェクトのライフタイムに対するこのレベルのコントロールは問題ない.しかし, .NETガベージコレクタはまれにしか実行されない場合があり,一般に.NETのメモリスペース(マネージドヒープ)がいっぱいになったときにのみ実行される.RCWオブジェクトはマネージドヒープでは小さいフットプリントしか残さないが,アンマネージドCOMの世界では大変大きなオブジェクト(Excelのインスタンスのように)にくっつく場合もある.COMの使用状況によっては,数多くの解放されていなく未使用のRCW オブジェクトが多大な量のメモリと他のリソースをCOMの世界で使い続けている場合でも,.NETのガベージコレクタが実行されないこともある.このため,オブジェクトによって捕らえられたCOMのリソースを解放することを強制するような関数を持つことが大切である.その関数はReleaseCOMObjectである.

ReleaseCOMObject[obj]特定のCOMオブジェクトで所有されるCOMリソースを解放する

COMリソースを解放する.

.NETのCOMオブジェクトはすべてそれぞれ単独のユニークなRCWによって表される.2つの異なる手段によって同じCOMオブジェクトへの参照を獲得した場合には,同じRCWを毎回得ることになる.この唯一のRCWがCOMオブジェクトの参照カウントを保持する.この参照カウントはCOM内部のもので,.NETオブジェクトの参照カウントと混同しないようにしなければならない.ReleaseCOMObjectを呼び出すことが実際にCOMリソースを即時に解放することを強制することには繋がらない.ReleaseCOMObjectはこの内部のCOMの参照カウントをデクリメントするだけである.しばしばこのカウントはただの1であり,ReleaseCOMObjectはこれをゼロにしてから,リソースを解放する.ReleaseCOMObjectは新しい参照カウントを返すので,ゼロになったかどうかをチェックすることができる.

以下は同じCOMオブジェクトに複数の参照を得る例である.次の行がExcelの新しいインスタンスを起動する.可視にはならないが,プロセスのタスクマネージャのリストでそれを見ることができる.

In[17]:=
Click for copyable input
Out[17]=

今度はExcelのその同じインスタンスに対してもう2つ参照を得る.GetActiveCOMObject関数はCreateCOMObjectに似ているが,前者は新しいオブジェクトを作成する代りにすでにアクティブなオブジェクトを得るという点で異なる.これはCOM APIとVisual Basic 6のGetActiveObject()関数に似ている.Pauseがここで必要なのは,COMがGetActiveObject()への呼出しの間で明らかに1息つく必要があるからである.

In[18]:=
Click for copyable input

ReleaseCOMObject[excel1]を今呼び出す場合,excel2およびexcel3のオブジェクトを通してExcel への未解決の参照が他にもあるので,おそらくExcelが終了してしまうと困るであろう.ReleaseCOMObjectを呼び出すことでExcelのインスタンスに対するCOMの参照カウントが最終的にゼロになるまでどのようにデクリメントするかに注意していただきたい.ゼロになって初めてExcel のプロセスが終了する.

In[21]:=
Click for copyable input
Out[21]=

これでReleaseCOMObjectを使ってタイムリーなCOMリソースの解放を強制する方法を見てきたことになる..NETのガベージコレクタによっていずれはこの解放が実行されるので,厳密にこれを行う必要は決してないが,ガベージコレクタは十分タイムリーにこれを実行しないことが多い.ReleaseCOMObjectを使う代りの方法としては,NETBlockあるいはReleaseNETObjectを確実に使用して,作成する.NETオブジェクトが解放されるようにするという方法がある.それから手動で.NETガベージコレクタを強制的に実行することができる.この方法は,たくさんのCOMオブジェクトを作成するコードを持っているような場合には特に便利である.それらすべてを追跡し,それぞれに対して ReleaseCOMObjectを呼び出すよりも,NETBlockを使って確実にオブジェクトがすべてゼロの.NET参照カウントを終了時に持っているようにしてから,ガベージコレクタを呼び出す方が容易である.以下はこれがどのようになるかのアウトラインである.

Click for copyable input

COMオブジェクトをキャストする

CastNETObject関数については「キャスティング」で触れている.この関数は普通の.NETオブジェクトにはめったに使われないが,COMオブジェクトに対して特別の任務を持っている.次の例では,使用中のマシンにMicrosoft Office XPのプライマリ相互運用アセンブリがインストールされている必要がある.次の行は,可視にはならないが, Excelアプリケーションの新しいインスタンスを作成する.

In[22]:=
Click for copyable input
Out[22]=

今度は新しいワークブックを作成する.

In[23]:=
Click for copyable input

ApplicationClassのクラスは,ActiveSheetと呼ばれ,今作成したワークブックのワークシートを返すプロパティを持つ.

In[24]:=
Click for copyable input
Out[24]=

ActiveSheetからの結果は生のRCWオブジェクトであり,強く定型化された.NET オブジェクトではない.(生のRCWオブジェクトはクラスSystem.__ComObjectのオブジェクトであり,.NET/Link はそれをサポートするデフォルトのCOMインターフェースの名前でフォーマットしようとすることを思い出していただきたい.)Microsoft.Office.Interop.Excel.WorksheetClassクラスがワークシートを表すExcel の相互運用アセンブリにあるのに,それではどうしてActiveSheetの結果がそのクラスのインスタンスではないのであろうか.その答を見るために,ActiveSheetプロパティの宣言を見てみよう.

このプロパティはWorksheetClassではなくobjectだけを返すように入力される.なぜならアクティブなシートは表またはワークシートであり得て,これらは異なるクラスであるからである.相互運用アセンブリでは,objectを返すようにタイプされたメソッドあるいはプロパティは生のRCWを返す.これは.NET内のCOMオブジェクトは通常の.NETオブジェクトとは異なるものであるということを思い起こさせる.任意のオブジェクトがCOMから.NETへマーシャルされると,それは必ず生のRCWとして到着する.相互運用アセンブリからの型の情報の助けを得て,.NETランタイムは生の RCWを特定のマネージドの型にキャストすることができる.例えば,メソッドがクラスXを返すようにタイプされ,メソッドがCOM オブジェクトを(RCWとして)返す場合は,.NETはRCWをメソッドから返す前に型Xにキャストする.メソッドがActiveSheetプロパティのようにobjectだけを返すようにタイプされる場合は,.NETがオブジェクトをキャストするために使える型の情報がないので,生のRCWを得ることになってしまうのである.

ActiveSheetで返されたオブジェクトを使うことができるが,型の情報がないので,それを遅延結合を通して呼び出すことになる.初期結合を通して呼び出せる強く定型化されたオブジェクトを作成したい場合は,C#あるいはVisual Basic .NETで行うのと全く同じ操作を行う,つまりオブジェクトを希望の型にキャストする.一旦希望するマネージドの型にキャストしたら,遅延結合の代りに相互運用アセンブリを使用することから得られる利点をまた持つことができる.この例では,アクティブシートがワークシートであることが分かっているので,それをWorksheetClassにキャストすることができる.

In[26]:=
Click for copyable input
Out[262]=

この説明が分かりにくい場合は,全く同じことがC# あるいはVisual Basic .NETで行われるということを思い出すとよい.以下はC#を使った場合である.

COMオブジェクトが指定のマネージドの型にキャストできない場合は,CastNETObjectはメッセージを発行し,$Failedを返す.

キャスティング」では,オブジェクトは常にその真のランタイムの型を持っているので,.NET/Link でダウンキャストを行う必要は全くない,つまり継承階層中にダウンキャストできる低い階層の型が全くないと述べた.この規則は,COMオブジェクトをキャストすることについては当てはまらない.なぜなら,ある意味ですべてのCOMオブジェクトのランタイムの型は,生のRCWのクラスである__ComObjectにすぎないからである.型の情報があると,.NET ランタイムは自動的にもっと派生したマネージドの型にダウンキャストすることができる.プロパティあるいはメソッドがobjectだけを返すようにタイプされると,正しい型が分かっていることを条件としてオブジェクトを自分でダウンキャストすることができる.

COMイベントを処理する

.NETランタイムはCOMイベントをどのように.NETイベントにマップするかを知っている.つまり,COMオブジェクトで引き起されたイベントに応えるということは,.NET オブジェクトで引き起されたイベントに応えることとちょうど同じようなものであるということである.「相互運用アセンブリ」を使って Mathematica コードでのCOMイベントを処理する必要がある.ここでは遅延結合を使うことはできない.

以下の例はInternet Explorerで引き起されたCOM イベントの処理方法を示している.Internet Explorerは HTMLウィンドウのコンテンツのためのリッチオブジェクトモデルをサポートする.これはドキュメントオブジェクトモデルと呼ばれ,mshtmlのCOMライブラリを通してクライアントに公表されている.Microsoftは.NET Frameworkと一緒にmshtmlのプライマリ相互運用アセンブリをバンドルして,これを.NETプログラムから使いやすいようにしている.おそらく将来Internet Explorerおよびmshtmlの ネイティブの.NETバージョンができることと思うが,今の時点では,その他多くのCOMベースのMicrosoftテクノロジーと同様に,これらを相互運用アセンブリを通して.NETから使う.以下に開発された例では,Internet ExplorerウィンドウのWebページが表示され,ユーザがページの要素上でマウスを動かすのに合わせて要素がそのフォントの大きさをランダムに変える.

mshtmlのCOM要素とその関連のプライマリ相互運用アセンブリは,Internet Explorerウィンドウのコンテンツを管理するだけである.Internet Explorerアプリケーションは,生のCOMオブジェクトに対して普通の方法で作成されなければならない別のCOMオブジェクトである.

In[27]:=
Click for copyable input
Out[27]=

このオブジェクトがCOMInterface[...] でフォーマットされているという事実は,これが生のRCWであり,オートメーションを通してしかインタラクトできないということを示している.これは,少ないプロパティしか必要ではないので,問題ない.COMオブジェクトのドキュメントは,オンラインあるいはVisual Studioのヘルプシステムにある MSDNライブラリで探してみるとよい.

まず,URLにナビゲートする.

In[28]:=
Click for copyable input

今度はブラウザウィンドウを可視にして,ページが完全にロードされるまでループさせる.

In[29]:=
Click for copyable input

Internet Explorerアプリケーションではなく,それを含むドキュメントオブジェクトとインタラクトしたいので,そのオブジェクトを得る.

In[31]:=
Click for copyable input
Out[31]=

このオブジェクトは,強く定型化された.NETオブジェクトであって,生のRCWではない.ドキュメントオブジェクトモデルに対してプライマリ相互運用アセンブリがあるので,そのようなCOMオブジェクトが.NETにインポートされるときはいつも,このオブジェクトはドキュメントのCOMのcoclassにマップされる.NETクラスに自動的にラップされることができる.これで強く定型化されたオブジェクトを相互運用アセンブリから使えるようになる.

オブジェクトによって引き起されたCOMイベントは,相互運用アセンブリの.NETイベントにマップされる..NETクラスが実装する [source] COMインターフェースすべてで,それぞれのメソッドの.NETイベントのメンバを見ることができる.NETTypeInfoを使って,HTMLDocumentClassクラスによって引き起されたイベントを見ることができる.このクラスはずいぶんたくさんあるので,ここでは必要なものだけを示す.

2つの異なるインターフェースから継承された2つのonmouseoverイベントがある.HTMLDocumentEvents2_Event_onmouseoverという名前で,引数を供給するイベントの方を使用する.mshtmlのプライマリ相互運用アセンブリについては個別のドキュメントが存在しないので,COMバージョンのドキュメントを使用して,適切な変換を頭の中でする必要があるが,この変換は通常かなり単純である.mshtml要素についての完全ドキュメントは以下を参照されたい. http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/mshtml/reference/reference.asp.

イベントを処理する」ではAddEventHandler関数を使って,イベントが.NETで引き起されたときに呼び出される Mathematica 関数を割り当てる方法について述べた.COMイベントについても,これらは相互運用アセンブリによって.NETイベントのように見えるようにされているので,同じ関数を使用する.いつも通り,.NETの名前をシンボルとして使用するときには_の文字をUに変える必要がある.

In[33]:=
Click for copyable input

今度はonMouseOver関数を定義する.上のNETTypeInfoの呼出しでは,イベントの引数がmshtml.IHTMLEventObjというインターフェースの型のものであることを示した.これはIHTMLEventObj COMインターフェースのマネージドの同等のものであり,そのインターフェースのドキュメントから,ランダムなフォントの大きさを指定する <FONT>要素のそれぞれをすべてラップする次の簡単な関数を作成することができる.

In[34]:=
Click for copyable input

実際にマウスをWebページ上で動かしてみる前に,もう1つ処理しなければならないことがある.「カーネルとフロントエンドを手動で.NETと共有する」では,関数を使って.NETから到着する呼出しをカーネルが受け入れられる状態にカーネルを置く方法について触れた.通常はDoNETModeless関数を使って自動的に共有の状態に入り,そこから出ることができるため,.NET/Link ではほとんど必要とされない.しかし,DoNETModelessはトップレベルのウィンドウをその引数として必要とする.ここではそのようなウィンドウはなく,Internet Explorerのインスタンスがあるだけである.したがって,を使って手動で共有の状態に入る必要がある.いつもと同じように,からの結果を保存して,後でに渡せるようにする.

In[35]:=
Click for copyable input

今度はInternet Explorerウィンドウを最前面に持ってきて,その上でマウスを(マウスボタンを押さずに)動かしてみる.最初のフォントの効果が現れるのに1,2秒かかるかも知れない.

使い終わった後で片付けるのを忘れないようにする.

(sharing) In[36]:=
Click for copyable input

これは取るに足らない例であるが,Mathematica を使ってブラウザウィンドウとインタラクトするもっと役に立って洗練された方法が数多くあることは想像できるであろう.

ActiveXコントロールを表示する

COMオブジェクトの多くは,ツールバー,グリッドボックス,あるいはその他のウィンドウ要素等の視覚的な表示を持っている.ActiveXコントロールという用語はCOMオブジェクトの同意語にすぎないが,可視のCOMオブジェクトは通常ActiveXコントロールとして参照される.ActiveXコントロールを.NETプログラムで表示したい場合は,そのコントロール用に特別の型の相互運用アセンブリを作成しなければならない.このアセンブリは,aximp.exeツール(ActiveXインポータ)で作成され,このツールは上で触れたtlbimpツールに似ているが,ActiveXコントロールが.NETウィンドウにホストされなくてはならないという点で異なる.コントロールが .NETウィンドウ内でホストされる場合は,System.Windows.Forms.Controlから継承する特別のラッパークラスが必要である.aximpツールがこのラッパークラスを作成する.

aximpについての完全ドキュメントが.NET Framework SDKにあるが,以下に簡単な例を挙げる..NETウィンドウでMicrosoft Calendar Control(おそらくすでにこのコントロールはマシンにインストールされている)を使いたいとしよう.このコントロールのタイプライブラリはマシンのファイル d:\OfficeXP\Office10\mscal.ocxにあるとする.(このような情報を得る1つの方法は,Visual StudioにバンドルされているOLE Viewツールを使ってコントロールを見付けるという方法である.)次のコマンドラインがaximpを実行する.(もちろんこれが書かれているように動作するためには,aximp.exeが自分のPATH上になければならない.)

上のaximpの起動は,現行のディレクトリであるMSACAL.dllおよびAxMSACAL.dll(「MSACAL」はコントロールのタイプライブラリの定義にあるライブラリのMSACAL 文から来ている)に2つのアセンブリを作成する.MSACAL.dllアセンブリは,ICalendarインターフェース,およびCalendarClassクラスに対するマネージドの型をその他のものと一緒に含む.これは,tlbimpツールを使って作成される相互運用アセンブリと同じものである.2つ目のアセンブリであるAxMSACAL.dllは,Calendarコントロールを.NETウィンドウでホストできる.NET コントロールにする特別のラッパークラスを含む.このラッパークラスは,「Ax」にCOMのcoclassの名前(Calendar)が続く慣例に従って名付けられ,AxCalendarと呼ばれる.ildasm.exeツール(中間言語ディスアセンブラ)は,作成されたアセンブリを調べるのに大変便利である.ildasm.exeプログラムは,.NET Framework SDKとバンドルされ,aximp.exeおよびtlbimp.exeと同じディレクトリに属する.

AxMSACAL.dllアセンブリをロードすると,MSACAL.dllアセンブリもロードされる.というのは,AxMSACAL.dllは後者に依存していて同じディレクトリに属するからである.

In[1]:=
Click for copyable input
Out[1]=

今度はAxCalendarラッパークラスのインスタンスを作成する.このクラスはCalendar COMオブジェクトのすべてのメソッドとプロパティを持ち,System.Windows.Forms.Controlクラスからも継承するので,その他の任意の.NETコントロールと同じように使用することができる.

In[2]:=
Click for copyable input
Out[2]=

以下でコントロールをホストして表示する.NETフォームを作成する.最後のDoNETModal関数はフォームを表示し,そのフォームが閉じられたときにカレンダーのValueプロパティを返す.このプロパティはユーザが選択した日付を保持する.

In[3]:=
Click for copyable input
Out[7]=

.NETランタイムは便利なことにValueプロパティの結果をDateTimeオブジェクトにマップする.これは簡単に操作して,選択された日付を文字列として得ることができる.

In[8]:=
Click for copyable input
Out[8]=

今度はカレンダーオブジェクトを解放して片付ける.AxCalendarオブジェクトは,COMオブジェクトではなく,COMオブジェクトへの参照を保持するだけの純粋な.NETクラスであるので,このAxCalendarオブジェクトに対してReleaseCOMObjectは呼び出さない.実際のCOMオブジェクトは,クラスMSACAL.CalendarClassのものであるが,そのクラスのインスタンスを直接作成することは決してなく,AxCalendarラッパーだけを作成する.

In[9]:=
Click for copyable input

例題ファイル

.NET/Link に含まれている以下の例題プログラムは,COM要素を Mathematica から呼び出すことを例示する.

ExcelPieChart.nb

New to Mathematica? Find your learning path »
Have a question? Ask support »