質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

1541閲覧

C#の構造体配列をネイティブDLLに渡したい

taro13

総合スコア15

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

3クリップ

投稿2018/11/06 09:41

個人的に用いる目的で、UnityのC#からバックエンドのC++のDLLを呼び出すプログラムを作っています。プラットフォームはwindowsのみを想定しています。

通常、そのような場合C#側では

C#

1[DllImport("HogeLib")] 2extern void Hoge(IntPtr ptr); 3 4void test() 5{ 6 double[] x = new double[10] 7 var handle = GCHandle.Alloc(x, GCHandleType.Pinned); 8 IntPtr ptr = handle.AddrOfPinnedObject(); 9 Hoge(ptr); 10 handle.Free(); 11}

としてネイティブDLLを呼び出すのが正しい方法かと思うのですが、一部のサイト等では単に

C#

1[DllImport("HogeLib")] 2extern void Hoge(double[] ptr); 3 4void test() 5{ 6 double[] x = new double[10]; 7 Hoge(x); 8}

としているような例を見かけました。

これだとDLL呼び出し中にガベージコレクションでxのアドレスが変わった場合問題が起きるので、
これが良くないのは分かるのですが、

C#

1[DllImport("HogeLib")] 2extern void Hoge(double[] ptr); 3 4void test() 5{ 6 double[] x = new double[10]; 7 var handle = GCHandle.Alloc(x, GCHandleType.Pinned); 8 Hoge(x); 9 handle.Free(); 10}

とするのは何か問題があるんでしょうか?
これまで、この方法でdoubleやintの配列をネイティブDLLに渡して要素の値を書き換えても特に問題は起きなかったのですが、自作の構造体の配列を同じ方法でDLLに渡すと要素の値は受け取れるものの、DLL内で要素の値を書き換えてもC#側には反映されませんでした。
構造体は以下のようなintとdoubleを並べただけの単純なものです。

C#

1struct MyStruct 2{ 3 int i1; 4 double a1; 5 double a2; 6 double a3; 7 double a4; 8 double a5; 9}

なぜdouble[]を渡した場合はDLL内での要素値の書き換えが反映されて、MyStruct[]では書き換わらないのでしょうか?

試したこと

一番上に書いた方法を用いれば、プログラムは問題なく動作しています。
ただ、引数を全てIntPtrにしてしまうとDLLの引数がIntPtrだらけになり可読性が悪くなるのと、型チェックが働かなくなるため、できればdouble[]やMyStruct[]等の形で渡したいと思った次第です。

補足情報

・Windows 10
・Visual Studio 2015 update3
・Unity 2017.4.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

似たような質問がありました。pin は必要ないと書かれていますね。
(c++側でポインタを保持していたらどうなるかわかりませんが。)
When passing a managed byte[] array through PInvoke to be filled in by Win32, does it need to be pinned?

構造体配列については、以下のように属性をつけると上手くいった覚えがあります。

cs

1extern void Hoge([In, Out]MyStruct[] ptr);

投稿2018/11/06 11:11

gaya-K

総合スコア449

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taro13

2018/11/08 15:07

回答ありがとうございます。 単にdouble[]やint[]としても今まで一度もエラーが起きなかったのは、これらがblittableな型で自動的にメモリが固定されているからだったんですね。よく理解出来ました。
guest

0

ベストアンサー

投稿2018/11/06 09:55

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taro13

2018/11/06 10:20

回答ありがとうございます。 上のページの.Netからネイティブに渡すときの「型の相互変換処理」という表現ですが、これは単に.Net側とDLL側でメモリ上の配置が同じでなければメンバ変数の値がおかしくなる、という意味ではないんでしょうか?ここに問題があるとすると、そもそもDLL側で受け取った要素の値がおかしくなると思うんですが、要素の値は問題なく受け取れていますので、構造体のメモリ配置の問題ではないように思われます。 「型の相互変換処理」という表現の解釈が間違っていますでしょうか?
y_waiwai

2018/11/06 11:23

C++のint,double と、C#のint,doubleは違うものです。 なので、DLLに渡すとき、DLLから読み出すとき、はそれぞれ変換しないといけないですが、そこらへんはC#のほうでうまくやってくれるようです http://www.atmarkit.co.jp/ait/articles/0305/09/news004.html 「C# マーシャリング」でぐぐればそこらへんの記事が出てきます
taro13

2018/11/08 15:12

マーシャリングという言葉を何となくしか理解していなかったんですが、DLLに渡すときにはblittableな型以外ではコピーが行われるんですね。 blittableな型であれば自動的にfixされるからblittableな型を渡したときは参照渡しのようになって、blittableでない型を渡したときには、[In, Out]指定が無ければOut方向のマーシャリングが行われないためDLL側の変更が反映されなくなるということでようやく合点しました。 また、自作の構造体で上手くいかないのはbool型が含まれているからでした。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問