LabVIEW から呼ぶことが出来る DLL を生成する(C++)

えーと今回は、LabVIEW から C++ で書いたライブラリ (DLL) を呼んでみたいと思います。

DLL に信号処理アルゴリズムが実装されていて、それを LabVIEW から呼びたいという事がたまに発生します。

LabVIEW はグラフの描画が用意なので、DLL のデバッグ時も見やすくて良いですね。

LabVIEW のサンプルを実行する

LabVIEW から DLL を呼ぶサンプルは、

C:\Program Files (x86)\National Instruments\LabVIEW 2010\examples\dll

に4つ程入っていますので、こちらを開いてみると良いと思います・・・・

と思ったら VI はちゃんと動くが、VC のプロジェクトが変換できませんでした。

恐らく私のマシンに VC 64bit コンパイラがインストールされていないからです。VS2010 インストールの時にチェック外したんだった orz。

clip_image001

ここはサンプル無しでやってみます。

DLL を生成する

新規に Visual Studio で DLL を作成してみます。

プロジェクトテンプレートは、VC++ → Win32 → Win32 プロジェクトを選択。

clip_image002

アプリケーションの種類は "DLL" を選択。

今回は ANSI 準拠のプログラミングだけをするので、MFC や ATL も使いません。この辺はお好みで。

clip_image003

プロジェクトが作成できたら、まずプロジェクトプロパティを開き、追加のインクルードディレクトリを設定しておきます。

C:\Program Files (x86)\National Instruments\LabVIEW 2010\cintools

フォルダに NI 提供のヘッダファイルなどが入っているので、こちらは必ずインクルードされるようにしておきます。

clip_image004

cintools の中身clip_image005

ソースコードを生成する

まずは試しで、入力された2つの値を加算して返す関数を実装してみます。

ソリューションエクスプローラ→ソースコードに、プロジェクト名.cpp があるので、開いて以下のコードをコピペします。

// DLL_w_LabVIEW.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。
//

#include "stdafx.h"

#include "extcode.h"
#include <windows.h>

_declspec (dllexport) int Add(int in1, int in2);
_declspec (dllexport) int Add(int in1, int in2)
{
return(in1 + in2) ;
}

clip_image006


でビルドすると、Debug フォルダに DLL やデバッグシンボル一式が出力されています。これで DLL 生成は OK。


clip_image007


LabVIEW から DLL を呼ぶ


DLL が作成できたら VI から読んでみます。


LabVIEW から DLL を呼ぶには「ライブラリ関数呼び出しノード」を使用します。



関数パレット→コネクティビティ→ライブラリ&実行可能ファイル→ライブラリ関数呼び出しノード


ブロックダイアグラム上にノードを置いたらコンテキストメニューから「構成」を選択し、「ライブラリ名またはパス」に先ほど生成した DLL を選択します。


で、DLL のエクスポート情報に基づいて「関数名」に一覧が表示されるので、そこから呼び出したい関数を選択します。


呼び出し規約はデフォルトの "C" のまま。


スレッドは「任意のスレッド」にしておくと、for ループなどの並列化時には並列化されたスレッドから呼び出させるため、パフォーマンスが向上すると思われます。


ちなみにDLL 内部で GUI を操作しているような場合は、「UI スレッドで実行」にしないと LabVIEW の操作がブロックされ、ハングアップしたようになってしまいます(恐らく)。


例えば DLL 内部で「ファイルを開くダイアログ」のような GUI を使う場合は、必ず UI スレッドで実行するようにしましょう。


clip_image008


パラメータで、関数のシグネチャに合わせた引数・戻り値のタイプを設定します。


clip_image009


今回は 2つの int 引数、1つの int 戻り値なので以下のように設定。


左側リストボックスの「+」を押すと、引数を追加する事が出来ます。


clip_image010


構成が済んだら、ノードに引数と戻り値を受け取る端子が出るようになったので、制御器・表示器を接続します。


clip_image011


実行すると 100+10 = 110 が返ってきました。


clip_image012


配列を DLL に渡す


次は配列を DLL に渡し、結果も配列で受け取ります。


基本的には、



  • 結果の配列も LabVIEW 側で作成して関数にはポインタで渡す
  • 関数内でポインタ先の配列に値を入れる
  • 渡した配列の長さ(要素数)も一緒に渡しておく

という事です。


ソースコード


ソースコードは以下の通り。配列はポインタにしておきます。

#include "stdafx.h"

#include "extcode.h"
#include <math.h>

_declspec(dllexport) void accuArray(double *input,
int input_length,
int *output);
_declspec(dllexport) void accuArray(double *input,
int input_length,
int *output)
{
int i;

for(i = 0; i < input_length; i++)
{
output[i] = input[i] * 2;
}
}

 


LabVIEW 側


LabVIEW 側は、入力配列と出力配列の配列形式を「配列データポインタ」にしておくことで、関数にポインタ渡しできるようになります。


clip_image013


結線はこのような形ですね。


clip_image014


クラスタを DLL に渡す


LabVIEW からクラスタを渡すと、DLL 側では構造体ポインタが渡ってきたように見えます。


ソースコード


構造体を定義し、そのポインタを受け渡すようにします。


後はいつも通り構造体へと値を設定すれば大丈夫。

#include "stdafx.h"

#include "extcode.h"

/* LabVIEW created typedef */
typedef struct {
double DBL;
long I32;
char Boolean;
} TD1;

_declspec(dllexport) void CLUSTERSimple(TD1 *input, TD1 *output);

_declspec(dllexport) void CLUSTERSimple(TD1 *input, TD1 *output)
{
output->DBL = input->DBL * input->DBL;
output->I32 = input->I32 / 2;
if(input->Boolean)
{
output->Boolean = FALSE;
}
else
{
output->Boolean = TRUE;
}
}

 


LabVIEW 側


配列と違ってクラスタの場合は「タイプに適応」というタイプにしておきます。LabVIEW 側でデータ構造を解析して適当なクラスタに変換してくれるようです。


clip_image015


まとめ


基本的には、LabVIEW から値やポインタもそのまま渡ってきますし、分かっている方なら問題なく使えるかと思います。C# のマーシャリングと同じ要領ですね。


さらに複雑なサンプルは、



C:\Program Files (x86)\National Instruments\LabVIEW 2010\examples\dll\data passing


にあるので、参考になります。


クラスタ構造内の配列にアクセスするなどはコーディングが面倒ですが、複雑なデータ構造を渡す必要が無いのであれば、LabVIEW 側でシンプルな構造にして渡してあげるのが、コードもシンプルになり良さそうです。


めでたし、めでたし。

コメント

  1. このコメントはブログの管理者によって削除されました。

    返信削除

コメントを投稿