2011年2月22日火曜日

[LabVIEW]LabVIEWからDLLを遅延バインディングで利用する

前々回前回の「LabVIEWからSusie plugin DLLを利用する」では、DLL関数に『ライブラリ関数呼び出しノード』を使ってアクセスしていました。このノードの【ダイアグラムでパスを指定】オプションを使うことで、異なるDLLにある関数を実行時に呼び分けることが可能となっていました。

ただし、これはどのDLLでも関数名・引数列が同じだから出来ていたわけで、要するに『ライブラリ関数呼び出しノード』は事前バインディングなわけです。

世の中に存在するプラグインとして機能するDLLが、全てSusie pluginのように「同じ関数名・同じ引数列」ならば良かったんですが、そんなには甘くありませんw

例えば、様々な形式の圧縮・解凍処理をプラグインDLLとして共通化されたAPIから利用しよう、という『統合アーカイバプロジェクト』ってのがあるんですが、このプラグインDLLでは、各機能を持つ関数の引数列は同じですが、関数名が「DLL固有名+機能共通名」とDLL関数によって異なっています。
これだと、LabVIEWの 『ライブラリ関数呼び出しノード』による事前バインディングでは、簡単には対応できません。

というわけで、どうにかならんもんか、と色々調査してみました。



DLLの各関数のエントリポイントは、関数ポインタとして扱えるため、C++なら簡単に動的な呼び出しが可能です。…がLabVIEWでは出来ません。

.NET framework 2.0以降が使えるのなら、関数ポインタをデリゲート変換して動的に呼び出すことが可能です。…がLabVIEWからデリゲートを(通常の方法で)使うことはかなり無理があります。
C++なんかでラッパーを書けば可能ですが、やはりLabVIEWだけでなんとかしたいところです。

しかたないので、.NET framework 1.xのように、動的モジュールのアセンブリを作成して遅延バインディングを実装することになります。

ココを参考にして書いたダイアグラムがこんな感じ(参考、というか丸写しですがw)。

アセンブリとモジュールを作成し、そこへDefinePInvokeMethodメソッドを使って「user32.dll」内の「MessageBox」関数を定義した後に、CreateGlobalFunctionsメソッドで関数定義を終了します。

設定した関数は、関数名を引数で与えたGetMethodメソッドでMethodInfoオブジェクトを取り出し、そのInvokeメソッドで呼び出します。

このダイアグラムを実行すると、タイトル【Title】・メッセージ【Message】なメッセージボックスが現れます。


…ん~~、もうちょっと実行時ダイナミックな遅延バインディングが実感できるサンプルも作ってみました。
(ダウンロードはココから)
『統合アーカイバプロジェクト』のAPI仕様に従ったアーカイバDLLである「UNLHA32.DLL」と「7-zip32.dll」について、各々のバージョンを動的に切り替えて取得するサンプルです。

『統合アーカイバプロジェクト』のAPI仕様では、バージョン番号は【word (固有名)GetVersion( void )】という関数で取得することになっており、「UNLHA32.DLL」では【UnlhaGetVersion】、「7-zip32.dll」では【SevenZipGetVersion】という関数名になります。

ダイアグラムの2段目で 【UnlhaGetVersion】を、3段目で【SevenZipGetVersion】を関数定義しています。
このサンプルでは各関数を個別に文字列で書き込んでいますが、プラグインDLLの検索・DLL関数の一覧取得と該当する関数の定義をforループで繰り返せば、もっとダイナミック感が増すことでしょうw

最下段のイベントストラクチャにおいて【DLL選択】列挙体の値で関数名を動的に切り替えて呼び出しています。

実行画面はこんな感じ。
【DLL選択】列挙体を切り替えて【バージョン取得】ボタンをクリックすると、各々のバージョン(を100倍した整数値)が表示されます。



さて、これでLabVIEWから遅延バインディングにより動的にDLL関数を呼び分けることができました。

ちなみに、サンプルとして『統合アーカイバプロジェクト』のプラグインDLLを使っていますが、このプラグインDLLをLabVIEWから使って圧縮が出来るようにするLLBを書く予定は現在のところありませんw 必要な方は、本エントリを参考に自分で実装してみてください(そして是非フリーで公開してくださいw)。

0 件のコメント:

コメントを投稿