LabVIEW から呼ぶことが出来る DLL を生成する(クラスライブラリ)

サンプルプロジェクト (C++, LabVIEW のプロジェクト)


前回から時間が空いてしまいましたが、ブログを更新。

更新してなかったですが、何もしていなかったわけではなく Windows8 やもちろん LabVIEW とも毎日のように戯れていました。

前回で、LabVIEWから DLL 内の関数を呼ぶことが出来ましたが、今回はクラスライブラリを呼びたいと思います。


【前回】LabVIEW から呼ぶことが出来る DLL を生成する(C++)
http://mikioblog.dolphinsystem.jp/2012/05/labview-dll-c.html


【】LabVIEW から呼ぶことが出来る DLL を生成する(クラスライブラリ)
http://mikioblog.dolphinsystem.jp/2012/05/labview-dll.html


関数とクラスライブラリの違い

当たり前の話ですが「関数を呼ぶ」というのは、C の関数を呼ぶと言うことです。

関数にパラメータを与えて、演算結果を返してもらう分にはこれで十分ですね。

しかし C++ 側でクラスが作ってあって、そのメソッドを LabVIEW で呼びたい場合はどうすればいいのでしょうか?

通常クラスライブラリを使うときは、

  1. クラスのインスタンスを生成
  2. そのインスタンスのメソッド(関数)を呼ぶ。
  3. 戻り値は、メソッドから返される場合もあれば、クラス内に保持される場合もある。

という流れで行ないます。

しかし LabVIEW ではあくまで呼べるのは関数ベースです。

クラスライブラリを呼ぶ方法

LabVIEW からは関数しか呼べません。と言うことは関数経由でクラス生成し、メソッドを呼べば良いことになります。

上記の 1. 2. 3. をクリアする方法を考えてみます。

1 のインスタンスを生成。

まずクラスのインスタンスはポインタな訳ですから、関数の中でクラスを生成し、そのポインタを LabVIEW 返すことでインスタンスの生成は出来そうです。

で、「ライブラリ関数の呼び出しノード」を見てみると、データタイプに「符号付きポインタサイズ整数」があります。プロトタイプで見てみると intptr_t なので、ポインタを受けることは出来そうです。

clip_image001

2 のメソッドを呼ぶ。

「ライブラリ関数の呼び出しノード」のパラメータを見てみると、渡すことが出来るデータタイプに「符号付きポインタサイズ整数」がありますので、取得したポインタを渡すことが出来ます。

では、関数にインスタンスのポインタを渡して、その中でメソッドを呼び、戻り値を受けることが出来そうです。

clip_image002

3 クラス内部の値を受け取る

これも「ライブラリ関数の呼び出しノード」にポインタを渡して、クラス内部の値を返してやれば良さそうです。

クラスライブラリを作る

では早速クラスを呼ぶための準備をします。

クラスを追加する

まずはクラスライブラリを作るために、Visual Studio でクラスを追加します。

クラス名は TestClass で生成すると、TestClass.cpp, TestClass.h がプロジェクトに追加されます。

clip_image003

クラスを実装する

TestClass の仕様は、

  1. クラスメンバ変数として int value を持っている。
  2. Add メソッドに渡された値と value を足し算する。
  3. Sub メソッドに渡された値と value を引き算する。
  4. 足し引きしないで値を設定・取得する

という簡単なクラスにします。

以下実装。

class __declspec(dllexport) は、関数を公開するのと同じく、DLL 内のクラスを外部に公開するために必要な定義です。これを忘れると外部から読んでもエラーになりますので、注意が必要です。

TestClass.h

#pragma once
class __declspec(dllexport) TestClass
{
public:
int value;
TestClass(void);
~TestClass(void);

void Set(int value);
int Get();
int Add(int value);
int Sub(int value);
};


TestClass.cpp

#include "StdAfx.h"
#include "TestClass.h"

TestClass::TestClass(void)
{
this->value = 0;
}

TestClass::~TestClass(void)
{
}

void TestClass::Set(int value)
{
this->value = value;
}

int TestClass::Get()
{
return this->value;
}

int TestClass::Add(int value)
{
this->value += value;
return this->value;
}

int TestClass::Sub(int value)
{
this->value -= value;
return this->value;
}


クラスのインターフェイスを実装する

クラスが出来たら、LabVIEW からクラスを呼ぶためのインターフェイス関数を作成します。
Visual Studio で、cpp ファイル "LVIF_TestClass.cpp" という名前で追加します。


コードは以下の通り。

#include "StdAfx.h"
#include "TestClass.h"

_declspec(dllexport) TestClass* LVIF_TestClass_GetInstance()
{
TestClass *param = new TestClass();
return param;
}

_declspec(dllexport) void LVIF_TestClass_Delete(TestClass *arg)
{
delete arg;
}

_declspec(dllexport) void LVIF_TestClass_Set(TestClass *arg, int value)
{
return arg->Set(value);
}

_declspec(dllexport) int LVIF_TestClass_Get(TestClass *arg)
{
return arg->Get();
}

_declspec(dllexport) int LVIF_TestClass_Add(TestClass *arg, int value)
{
return arg->Add(value);
}

_declspec(dllexport) int LVIF_TestClass_Sub(TestClass *arg, int value)
{
return arg->Sub(value);
}

  • インスタンスを作成しポインタを返す LVIF_TestClass_GetInstance 関数と、
  • 値を設定・取得する LVIF_TestClass_Get, LVIF_TestClass_Set 関数、
  • Add, Sub メソッドに対応した LVIF_TestClass_Add, LVIF_TestClass_Sub 関数、
  • インスタンスを削除する LVIF_TestClass_Delete 関数

を用意しました。

それぞれ _declspec(dllexport) をつけておきます。


クラスを LabVIEW から呼ぶ

インスタンスの生成

DLL が出来たら、次に LabVIEW からクラスを呼んでみます。まずはインスタンスの生成から。

VI を作成し「ライブラリ関数の呼び出しノード」をブロックダイアグラム上に配置します。右クリック→構成ダイアログで、作成した DLL を選択。

関数名を見ると、先ほどのインターフェイス関数が見えてますね。

まずは GetInstance を選択します。

clip_image004


返り値で、符号付きポインタサイズ整数を選択。

clip_image005


これを実行してみると、何か値が返ってきました。

これが TestClass のポインタですね。

clip_image006


メソッドを呼ぶ

では次に Get, Set をして値を初期化します。

新しい「ライブラリ関数の呼び出しノード」ので、Set 関数を選択。


clip_image007


Set 関数は void のため返り値はデフォルト(void) のまま。

最初の引数でクラスのポインタを渡します。「パス」は必ず「値」を選択します。

clip_image008


2番目の引数で設定する int32 値にしておきます。

clip_image009


Get 関数はこちらのように、int32 を受け取ります。arg1 は Set 関数と同じくポインタを指定しておきます。

clip_image010


実行するとこのように、値を 1 で初期化できました。また生成したポインタと、Set/Get 関数で使用したポインタが同じ事も確認できました。

clip_image011


では次に Add/Sub 関数も足してみたら結果はこの通り、0 で初期化→1を100回加算→1を100回減算→結果 0 という一連の流れをクラスで実行させることが出来ました。

clip_image012


デバッグ方法

このクラスライブラリ呼び出し時に DLL にブレークポイントをしたい場合は、Visual Studio でプロセスにアタッチをすれば可能です。

まずデバッグビルドで DLL を作成し、LabVIEW 側で VI を起動します。

そうしたら Visual Studio のメニュー→デバッグ→プロセスにアタッチ→ LabVIEW.exe を選択し、アタッチボタンを押します。

これでソースコードにブレイクポイントをつけておけば、DLL 実行時にそこで実行が停止しステップ実行など出来ます。

clip_image013


はまり所

c側からの戻り値が bool の場合、LabVIEW で「符号付き8ビット整数」を選択します。32ビットだと戻り値がおかしくなります。


まとめ

  1. クラスのコンストラクタ、メソッドを呼ぶ関数を別途作成する。
  2. クラスのインスタンスポインタを LabVIEW 側で保持することで、クラスの生成・実行は可能
  3. インスタンスの削除を忘れないように
  4. 戻り値の bool は注意。
以上、ドルフィンシステム福島でした。

コメント