Community Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

様々なデータタイプを含む C/C++ の DLL を LabVIEW から呼び出す

注釈: この記事は、Calling C/C++ DLLs Containing Simple and Complex Datatypes from LabVIEW を翻訳したものです。内容に相違がある場合、英語の資料が正文となっております。

概観

LabVIEW開発者はLabVIEWからC/C++のDLLを呼び出すことがありますが、始めはLabVIEWとC/C++の間のデータタイプの違いから、DLLとデータの送受信を行うことに抵抗を感じることがあるかもしれません。

この記事に添付されたサンプルは、LabVIEWとDLLの間で数値(int、float、double、など)、配列、そして文字列などのシンプルなデータタイプから、ポインタ、構造体(クラスタ)、構造体の配列や、構造体の配列を含む構造体の配列などの複雑なデータタイプを渡す方法をLabVIEW開発者の方々にお見せするために作成されました。

また、このサンプルではLabVIEWとDLLの間で、データを渡す方法-関数が値渡しでデータを渡し、return ステートメントでデータを戻すか、リファレンス渡しでデータを戻すか、についても説明します。
このサンプルでは下記のケースを取り上げます:

データ型呼び出し型
1. 数値(整数)a) 値を戻す (return ステートメント)
2. 数値配列b) ポインタを戻す(returnステートメント)
3. 文字列c) パラメータを渡す(値渡し)
4. 2次元配列d) リファレンスで値を戻す(リファレンス渡し)
5. (単純なデータ型を含む)単純な構造体
6. (構造体や配列を含む)複雑な構造体

添付されたファイル

  • LabVIEWWrapper.zip: 各関数の呼び出し方の例を含むLabVIEWライブラリ (.lvlib)
  • PassingDataSampleDLL.zip: ANSI C DLLのソースコードとDLL。CVIでかかれたものですが、Visual Studioでコンパイル可能なことを確認済み
  • CTestApplication.zip: C DLLをANSI Cで呼び出す例。Visual Studioで作成。

サンプルのヒエラルキー

サンプルに含まれる多数のVIは共有ライブラリをインポートウィザードで自動的に生成されました。ただし、ウィザードはすべてのライブラリ関数に対してラッパーVIを生成できるわけではないため、一部のVIは未完成です。それでもこのウィザードはDLLを呼び出すためのはじめの一歩にはなります。

共有ライブラリをインポートウィザードの使用例については下記の資料を参照してください。
チュートリアル: 共有ライブラリをインポートウィザードで C/C++ の DLL 関数のラッパーVIを作成する

共有ライブラリをインポートウィザードが適応できなかったVIについては、「共有ライブラリをインポートウィザードの例外」セクションを参照してください。

サンプル内のVIは、下記の仮想フォルダに含まれています。

  • Import Shared Llibrary Wizard Generated VIs: 共有ライブラリをインポートウィザードで自動的に正しく生成されたVI
  • Dereferencing Pointers: ポインタをデリファレンスするために役立つVI。これらはMoveBlockもしくはGetValueByPointer関数を使用します。
    詳細は、LabVIEW で C/C++ の DLL からのポインタをデリファレンスするを参照してください。
  • Custom Controls For Structs: DLL内の構造体を表す指定タイプ定義
  • Completed, Corrected and Added VIs: 共有ライブラリをインポートウィザードの例外を回避するために追加されたVI

共有ライブラリをインポートウィザードの例外

下記のケースでは共有ライブラリをインポートウィザードが正しく対応できませんでした。

VIが生成されない

下記の場合VIは生成されませんでした。

  • 関数が、数値、文字列、voidあるいはポインタ以外を戻す場合。この場合データはポインタとして扱い、LabVIEWでは戻り値のデータタイプを符号なしポインタサイズ整数に設定します。

サンプルlvlibではこれらのVIは手動で生成し、VI名の末尾に「Added」を追加しています。

未完成のVI

下記の場合、生成されたVIは未完成でした。

サンプルlvlibではこれらのVIを手動で完成させて、VI名の末尾に「Completed」を追加しています。

不正確なVI

下記の場合、生成されたVIは正確に生成されませんでした。

  • 関数がポインタのポインタ(文字列や2次元配列)を戻す場合。これらは二度でリファレンスされる必要があります。
  • 関数に、配列や文字列を含む構造体(クラスタ)を入力される場合。この場合、各要素がそれぞれでリファレンスされる必要があります。

サンプルlvlibではこれらのVIを修正し、VI名の末尾に「Corrected」を追加しています。

関数とVIのリスト(データタイプと呼び出しタイプ)

下記にてC DLLの関数とその関数を正しく呼び出すためのVIをリストしています。

  1. 数値 (整数)   
    1. 値を戻す (return ステートメント)
      関数: int ReturningAValue_Integer (void);
      VI:
      Returning A Value Integer.vi
      自動生成されたVIのステータス: 正常動作
           
    2. ポインタを戻す(returnステートメント)
      関数:
      int* ReturningAValue_PointerToInteger (void);
      VI: Returning A Value Pointer To Integer Complete.vi
      自動生成されたVIのステータス: 未完成
           
    3. パラメータを渡す(値渡し)
      関数:
      int PassingParameters_Integer (int x, int y);
      VI: Passing Parameters Integer.vi
      自動生成されたVIのステータス:
      正常動作     
    4. リファレンスで値を戻す(リファレンス渡し)
      関数:
      void ReturningValuesByReference_Integer (int x, int y, int *sum);
      VI:
      Returning Values By Reference Integer.vi
      自動生成されたVIのステータス:
      正常動作     
  2. 数値配列    
    1. 値を戻す (return ステートメント)
      関数:
      int* ReturningAValue_ArrayOfIntegers (int length);
      VI:
      Returning A Value Array Of Integers Complete.vi
      自動生成されたVIのステータス:
      未完成     
    2. ポインタを戻す(returnステートメント)
      関数: N/A (arrays variables are already pointers)
      VI: N/A
      自動生成されたVIのステータス: N/A
           
    3. パラメータを渡す(値渡し)
      関数: int PassingParamters_ArrayOfIntegers (int x[], int length);
      VI:
      Passing Paramters Array Of Integers.vi
      自動生成されたVIのステータス:
      正常動作     
    4. リファレンスで値を戻す(リファレンス渡し)
      関数: void ReturningValuesByReference_ArrayOfIntegers (int *x, int length, int **newArray, int *newLength);
      VI:
      Returning Values By Reference Array Of Integers Complete.vi
      自動生成されたVIのステータス: 未完成
           
  3. 文字列   
    1. 値を戻す (return ステートメント)
      関数:
      char* ReturningAValue_String (void);
      VI:
      Returning A Value String.vi
      自動生成されたVIのステータス: 正常動作
           
    2. ポインタを戻す(returnステートメント)
      関数: N/A (arrays variables are already pointers)
      VI:N/A
      Auto Generated VI Status: N/A
           
    3. パラメータを渡す(値渡し)
      関数:
      int PassingParamters_String (char *str);
      VI:
      Passing Paramters String.vi
      自動生成されたVIのステータス: 正常動作
           
    4. リファレンスで値を戻す(リファレンス渡し)
      関数:
      void ReturningValuesByReference_String (char *str, char **newString);
      VI:
      Returning Values By Reference String Corrected.vi
      自動生成されたVIのステータス: 不正確
           
  4. 2次元配列   
    1. ReturningAValue_2DArrayOfIntegers
      関数: int** ReturningAValue_2DArrayOfIntegers (int rows, int cols);
      VI:
      Returning A Value 2D Array Of Integers Complete.vi
      自動生成されたVIのステータス: 未完成
           
    2. ポインタを戻す(returnステートメント)
      関数: N/A (arrays variables are already pointers)
      VI: N/A
      自動生成されたVIのステータス: N/A
           
    3. パラメータを渡す(値渡し)
      関数:
      int PassingParamters_2DArrayOfIntegers (int *x, int rows, int cols);
      VI:
      Passing Paramters 2D Array Of Integers Corrected.vi
      自動生成されたVIのステータス: 不正確
           
    4. リファレンスで値を戻す(リファレンス渡し)
      関数:
      void ReturningValuesByReference_2DArrayOfIntegers (int rows, int cols, int ***newArray);
      VI:
      Returning Values By Reference 2D Array Of Integers Complete.vi
      自動生成されたVIのステータス: 未完成
           
  5. (単純なデータ型を含む)シンプルな構造体    
    1. 値を戻す (return ステートメント)
      関数:
      struct simpleStructCircle ReturningAValue_SimpleStruct(void);
      VI:
      Returning A Value Simple Struct Added.vi
      自動生成されたVIのステータス: 未生成
           
    2. ポインタを戻す(returnステートメント)
      関数:
      struct simpleStructCircle* ReturningAValue_PointerToSimpleStruct(void);
      VI:
      Returning A Value Pointer To Simple Struct Added.vi
      自動生成されたVIのステータス: 未生成
           
    3. パラメータを渡す(値渡し)
      関数:
      float PassingParamters_SimpleStruct (struct simpleStructCircle circle);
      VI:
      Passing Paramters Simple Struct.vi
      自動生成されたVIのステータス: 正常動作
           
    4. リファレンスで値を戻す(リファレンス渡し)
      関数:
      void ReturningValuesByReference_SimpleStruct (struct simpleStructCircle circle, struct simpleStructCircle *largerCircle);
      VI:
      Returning Values By Reference Simple Struct.vi
      自動生成されたVIのステータス: 正常動作
           
    5. 構造体の配列を戻す
      関数: void ReturningValuesByReference_ArrayOfSimpleStruct (struct simpleStructCircle **circleArray, int length);
      VI:
      Returning Values By Reference Array Of Simple Struct Complete.vi
      自動生成されたVIのステータス: 未完成
       
  6. (構造体や配列を含む)複雑な構造体   
    1. 値を戻す (return ステートメント)
      関数:
      struct complexStructPolygon ReturningAValue_ComplexStruct (void);
      VI:
      Returning A Value Complex Struct Added.vi
      自動生成されたVIのステータス: 未生成
           
    2. ポインタを戻す(returnステートメント)
      関数:
      struct complexStructPolygon* ReturningAValue_PointerToComplexStruct (void);
      VI:
      Returning A Value Pointer To Complex Struct Added.vi
      自動生成されたVIのステータス: 未生成
           
    3. パラメータを渡す(値渡し)
      関数:
      int PassingParamters_ComplexStruct (struct complexStructPolygon triangle);
      VI:
      Passing Paramters Complex Struct Corrected.vi
      自動生成されたVIのステータス: 不正確
           
    4. リファレンスで値を戻す(リファレンス渡し)
      関数:
      void ReturningValuesByReference_PointerToComplexStruct (struct complexStructPolygon* triangle);
      VI:
      Returning Values By Reference Pointer To Complex Struct Corrected.vi
      自動生成されたVIのステータス: 不正確
           
    5. 構造体の配列を戻す
      関数: void ReturningValuesByReference_ArrayOfComplexStruct (struct complexStructPolygon **triangles, int length);
      VI:
      Returning Values By Reference Array Of Complex Struct Corrected.vi
      自動生成されたVIのステータス: 不正確
           

スプレッドシートで閲覧されたい場合、下記のファイルを参照してください。
ListOfFunctionsAndVIs.xlsx

まとめ・次のステップ

LabVIEWとC/C++ DLLとの間でデータのやり取りをするのは、特に複雑なデータ型やポインタを使用する際には難しい場合もあります。この記事のサンプルを参照しながらDLLをLabVIEWで呼び出してみてください。

Comments
hyamasi
Member
Member
on

配列ではない(整数とか浮動小数点とか)ポインタの引数の使い方で分からないところがあります。

DLLへの入力では使用せず、DLLから結果を受け取るだけの場合、ライブラリ関数呼び出しノード の入力側にも変数を接続する必要はあるのでしょうか?

たとえば、PassingDataSampleDLL.c の ReturningValuesByReference_Integer の三つ目の引数 int *sum のような場合です。

LabVIEWWrapper_PassingDataSampleDLL.lvlib で上記を呼び出しているところを見ると、入力側にI32のsumをつなげています。これは必須でしょうか?

もし、入力側につなげるのを忘れてしまった場合、どうなるのでしょうか? もしかすると、ポインタに格納されるアドレスがちゃんと初期化されず、たまたま格納されたアドレスに相当するところに値が書き込まれて、その領域が破壊されたりするのでしょうか?

マニュアル等のドキュメント内を探したのですが、これに付いて明記されている記述を見つけることができなかったので、差し支えなければ教えてください。よろしくお願いします。

Contributors