NI製品ディスカッション

キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

C言語で書いたDLLの、配列ではないポインタの引数の使い方に付いて

C言語で書いたDLLを ライブラリ関数呼び出しノード で呼び出す場合、配列ではない(整数とか浮動小数点とか)ポインタの引数の使い方で分からないところがあります。
DLLへの入力では使用せず、DLLから結果を受け取るだけの場合でも、ライブラリ関数呼び出しノード の入力側にも変数を接続する必要はあるのでしょうか?

 

たとえば、下記の関数の三つ目の引数 int *sum のような場合です。

void intsumsample(int x, int y, int *sum) {
    *sum = x + y;
}

LabVIEW2014で試したところ、入力側はx, yのみ接続して、出力側はsum(値へのポインタ)を接続したところ、うまく動作してsumにxとyの合計が返ってきました。

 

でも、値が格納される変数の実体を定義せずにDLLを呼び出した場合、ポインタに格納されるアドレスがどうなるのかについて不安があります。
ポインタにたまたま格納された、どこかよく分からないアドレスが指す場所に値を書き込んで、その領域を破壊するなんてことは起きないのでしょうか?
破壊しているけれどそれが分からず、ごくまれにうまくいかないことがあるのなら、数回試しても分かりません。
LabVIEWがうまくやってくれている(値を格納する変数の実体をちゃんと専用に確保してくれている)のならいいのですが。

 

配列の場合は、入力につなげないとうまくいかないのは、理解しています。
配列は入力に必要なのだから、配列ではなくても入力に必要なのではないか、と想像していますが、実際試すと入力につなげなくてもうまく動いているので、戸惑っています。

 

できれば、たぶんこうだろうという想像ではなくて、LabVIEWの仕様が知りたいです。
マニュアルやディスカッションフォーラム内を探したのですが、これに付いて明記されている記述を見つけることができませんでした。
このドキュメントのここに書かれている、とかでもいいです。
この点に付いてのLabVIEWの仕様をご存知の方がいらっしゃいましたら、教えてください。
よろしくお願いします。

0 件の賞賛
メッセージ1/13
3,866件の閲覧回数

こちらの資料にいろいろまとめられているので、ご覧ください~

 

LabVIEW で C/C++ の DLL にポインタを渡したり受け取る

https://forums.ni.com/t5/Community-Documents/LabVIEW-%E3%81%A7-C-C-%E3%81%AE-DLL-%E3%81%AB%E3%83%9D%...

 

hyamasiさんのやりたい事なら、「ポインタを受け取る」セクションの「ポインタを受け取り自動的にデリファレンス」で実現できると思います。

 

(`・ω・´)ゞ

0 件の賞賛
メッセージ2/13
3,803件の閲覧回数

hyamasiさんのおっしゃるのは、Call Library Functionで「値へのポインタ」にしてある引数に何もつながない場合に、出力から得られる値は何のポインタから値を取ってきたの?どこに書き込んじゃってるの??ということかと思います。

 

確かに興味深いところで、私も「仕様」は分からないので回答になっていませんが、今まで自分は「不明なポインタやヌルポインタを渡してハングするのはイヤなので、必ずダミーデータによる『箱・受け皿』を明示的に渡してきた」ため、気付きませんでしたが、やってみるとおっしゃる通りですね。

 

しかも、サブviの入力端子のように「つながない場合はデフォルト値がつながってるものとみなす」というわけでもない挙動が見えてきました。ますます謎です。

 

今回、次のようなテストdllを作ってみました。

==========

__declspec(dllexport) void _cdecl getadr(unsigned long *,unsigned long *);


void _cdecl getadr(unsigned long *retadr, unsigned long *dat)
{
    *retadr=(unsigned long)dat;
}

===========

 

これは、ポインタ渡しした引数datのアドレスそのものを、もう1つの引数retadrから返すものです(こっちもポインタ渡しということになるので少々ややこしいですが)。datそのものは、何もしないので、呼び出し時の「値(ポインタの指すところにある値)」が返ります。要は、引数datにつないだ値そのものが再度引数datに、引数datのアドレスが引数retadrに返る、というものです。

 

①まず、「値へのポインタ」にしてある引数datは、入力も出力もつながないと、viが壊れた状態になります。

170731-sample1-1.png

 

②次に、引数datに何か入力をつなぐと、viは壊れた状態ではなくエラー無しになります(これはごく普通のこと)。<フロントパネルは実行前です>

170731-sample1-2.png

 

③ここで、入力を消し、出力に表示器をつなぐと、これもviはエラー無しです(これが今回の質問の状態ですね)。<フロントパネルは実行前です>

170731-sample1-3.png

入力をつないでいない場合、出力がフリーだと壊れるが、出力が表示器につないであると壊れない・・・・もしかして、入力が無い場合、出力につないだ表示器のアドレスを持ってきてくれている?・・と思ったのですが、どうもそうでもないようです。

 

④以下のように、引数datの入力に定数0xFFをつないで実行すると、初期値が0だった表示器datに0xFFが代入され、かつ表示器retadrが0x263C600である、と返ります。これは特に異論の無い動作です。

170731-sample1-4.png

retadrから返ってきた0x263C600は、渡したデータ定数0xFFの格納場所が0x263C600なのだろう・・と思われます。この状態で何度か実行しても、同じ場所に格納されているらしく、返るアドレス値は変わりません。

 

⑤ここで(④をやった後)、定数の0xFFを消して引数datの入力をフリーにし、さらに、フロントパネルの表示器datの中身も0にクリアした状態で実行してみると、、、

170731-sample1-5.png

なぜか、どこにも書いていないはずの「0xFF」が再び表示器datに代入され、そのアドレスが0x26390C0である(さっきとも違う)となりました。入力が無いのでLabVIEWがおせっかいにも確保してくれたのか何なのか、「良くわからないどこか」のアドレスに、書いていない0xFFが作られ、そこのアドレスが引数datに渡され、0xFFが表示器datに返り・・・みたいな印象ですね。で、このアドレスが表示器datのアドレスではないことは、表示器datの初期値が0だったことから明らか(もし表示器datのアドレスが渡されているのなら、初期値0が参照されて返ってくるはずで、0xFFになるはずがない)。0xFFが返ってくる、ということは、別の「どこか」を参照していることになります。0xFFも、明らかに「デフォルト値」としては不自然で。

 

ただ、何であれLabVIEWが「0xFF」というデータを勝手に用意してくれたみたい、という意味では、「どこに書き込んじゃってるのか全く分からないで心配」というよりは、「どこかに意図的に用意してくれたエリア」になっているみたい・・・という気はしますが。。

 

というわけで、ただ現象を見てみました、というだけですが、入力が無い場合に、出力につないだ表示器のアドレスが渡されているわけでもなく、何か「デフォルトの値」がおせっかいにも準備されているようですが、デフォルトというより、一度値をつないで消すと「さっきまでつないでいた値」が復活する・・・という不思議な挙動ですね。

 

個人的には今後もいつでも、「良くわからないアドレスを指してハングするのはイヤなので、必要か不要かに関わらず今後も明示的に『受け皿』は渡しておいて、気がすむようにする」と思いますが、内部の仕様的にはどうなっているのかは興味はあります。値へのポインタに入力が無くても引数に何かを渡してくれるなら、入出力両方なくてもviは壊れなくて良いと思いますが、入出力両方無いとviが壊れる、というところも、全体の挙動としては整合が今ひとつ??

 

長くなりすみません。

メッセージ3/13
3,780件の閲覧回数

皆様、お邪魔します。

 

サンプル「DLLを呼び出し」を見てみました(LabVIEWヘルプのライブラリ関数呼び出しノードのサンプルから開けます)

見た感想になってしましますが

①文字列・配列・クラスタ(文字列のANSIchar.viの入力を入れないと実行できない状態になる)
②その他のデータタイプは不要っぽい

 

・サイズが特定できないデータタイプは必ず必要!
・固定長データタイプは不要(ライブラリ関数呼び出しノードが確保) 値を渡すときだけ入力すればよい?

って仕様じゃないかと私は考えました。

 

想像でレスで申し訳ありません。(ぺこ)

 

M.Shiraishiさんが試された③・④のretadrとdatのポインタ値がどうなるのか気になってます。

 

すべてをダウンロード
0 件の賞賛
メッセージ4/13
3,766件の閲覧回数

度々申し訳ないです。

 

>M.Shiraishiさんが試された③・④のretadrとdatのポインタ値がどうなるのか気になってます。
④は retadrとdat両方に定数FFを入れた場合でした。

0 件の賞賛
メッセージ5/13
3,763件の閲覧回数

>>M.Shiraishiさんが試された③・④のretadrとdatのポインタ値がどうなるのか気になってます。
>④は retadrとdat両方に定数FFを入れた場合でした。

 

④は定数0xFFはdatにしか入れていないです(引数retadrは全ての場合で何も入力していないです)。

 

それはそれで、

 

>・固定長データタイプは不要(ライブラリ関数呼び出しノードが確保)

 

は、私もそうなのだろう、と感じています。

ただ、確保はしてくれたとしても、初期値が不自然ですね・・・④と⑤の挙動が、④で0xFFをつないで実行し、0xFFを消去すると、ダイアグラム上は0xFFはどこにも記述されていないのに、「④の時とは違うアドレスに、④の時に使われた0xFFの値が復活する」(わざわざコピーしてるのか・・)とか。。

まぁ、推察の域を出ないので、NIの方が「こうです」と示して下さるのがいいのですが。

メッセージ6/13
3,742件の閲覧回数

NI技術サポートが情報入手できないかなぁ

 

M.Shiraishiさんのようにいろいろ試めして
「実行できる状態の時は、LabVIEWはその仕様を満たしてくれている」
と考える?

メッセージ7/13
3,687件の閲覧回数

> M.Shiraishiさんのようにいろいろ試めして
> 「実行できる状態の時は、LabVIEWはその仕様を満たしてくれている」
> と考える?

 

それだと、将来のバージョンでは仕様が変わってうまくいかないかもしれない、という怖さがあります。

やはり、マニュアルとかの正規のドキュメントに明記しておいてほしいです。

0 件の賞賛
メッセージ8/13
3,574件の閲覧回数

3週間も前のスレッドの内容で恐縮ですが、M. Shiraishi様の検証にて、次の値に興味があります。

・(2)の状態で「実行したときの」retadrと、(4)のretadr

・(3)の状態で「実行したときの」retadrと、(5)のretadr

これらがそれぞれ異なる値になるかどうかが気になります。

もしお時間があれば検証していただけないでしょうか。

 

(当方DLL作成の知識が乏しいので、お手数をおかけしてしまい申し訳ありません。)

0 件の賞賛
メッセージ9/13
3,555件の閲覧回数

>>・(2)の状態で「実行したときの」retadrと、(4)のretadr

>>・(3)の状態で「実行したときの」retadrと、(5)のretadr

 

基本的には、変わります。

(2)でも、datに既に整数が接続された状態で一度実行し、datにつないだ数字を変えて実行すると、retadrは別のところを指します。元の数字に戻して実行すると、retadrが元のアドレスを指したこともあれば、別のところを指したこともありました。で、(2)で実行した場合と(4)で実行した場合も、別のところを指します。

 

(2)でも、何も変更しないで「再実行」すると、retadrは同じところを指しています。何度実行しても同じです。ダイアグラム上のオブジェクト(端子やワイヤ)を消すことなく動かすだけでは、変わりません。しかし、ダイアグラム上でワイヤを一旦消して再度同じようにつなぐという場合だと(結果的に同じ論理構造でも)retadrは別のところを指します。

 

(3)と(5)も同様で、変わります。特に、一度datの方に "FF" などをつないで実行し、そのあどdatの入力の "FF" を消して実行しても、datの戻り値には "FF" が入るのに retadrは「別のところ」を指しています(別のところに即値の "FF" が準備されたことを意味する?)。

すべてをダウンロード
メッセージ10/13
3,535件の閲覧回数