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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C#

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

C++

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

Q&A

解決済

2回答

6540閲覧

C#実行プログラムからのC++ライブラリ(dll)呼び出し時の構造体の参照渡しについて

yukibeatles

総合スコア12

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C#

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

C++

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

0グッド

0クリップ

投稿2019/06/11 05:30

今回2度目の質問をさせていただきます。
開発環境はVisual Studio 2013です。

実現したいこと

C#で書かれた実行プログラム(A.exe)からC++で書かれたライブラリ(B.dll)内の関数(Func_C)を呼び出す際、引数として渡す、
構造体の配列(struct_D)をA.exe側で受け取りたい。

発生している問題

A.exeからFunc_Cをコールした後、struct_Dがnullである。
(Func_Cの実行結果が格納されない)

試したこと

・下記コードのようなプログラムを実装してみました。その結果、以下のことは可能でした。

 ・A.exeからB.dll側に値(int型、int型・char型を含む構造体)を引数として渡すことはできる。
(下記コード内の関数1の入力引数はB.dll側に問題なく渡せる)
・B.dll側からA.exe側に値(int型)を引数として渡すこともできる。
(下記コード内の関数2の出力引数はA.exeに問題なく返せる)

以上の結果より、問題は呼び出し元にあると考え、試行錯誤していますが、うまく実装できずにいます。

下記コード内で誤り等ありましたらご教示頂けませんでしょうか。
お手数ですが宜しくお願い致します。

<A.exe(呼び出し元)>

class Program { // C++受渡し用構造体 [StructLayout(LayoutKind.Sequential)] internal struct struct_D { // 変数宣言(マーシャリング) [MarshalAs(UnmanagedType.I4)] internal int val1; // 値1 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] internal string val2; // 値2(値が省略("")の可能性あり) [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 60)] internal string val3; // 値3 // カプセル化 public int Val1 { get { return val1; } set { val1 = value; } } public string Val2 { get { return val2; } set { val2 = value; } } public string Val3 { get { return val3; } set { val3 = value; } } } // < C++のDLLファイルより関数を取得 > // 関数1(配列サイズ取得関数)←こちらは問題なく動作する [DllImport("B.dll")]         static extern int GetTotal( IntPtr ptrTotal // (出力引数)←出力引数もint型変数のみであれば問題なく受け取れる );      // 関数2(構造体配列の取得関数) [DllImport("B.dll")] static extern int Func_C( IntPtr[] dataOut,   // (出力引数)ポインタの配列(これが受け取れない) int num, // (入力引数)←入力引数はB.dllに問題なく渡せる struct_D dataIn   // (入力引数)←構造体も問題なく渡せる ); static internal int Func_E() { // 出力構造体の配列サイズを取得(これは問題なく動作する) IntPtr ptrTotal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int))); // レコード数ポインタ(メモリ確保) Marshal.StructureToPtr(Total, ptrTotal, false); // レコード数とポインタを結び付け GetTotal(ptrTortal); // DLLの実行(レコード数取得) Total = (int)Marshal.PtrToStructure(ptrTotal, typeof(int)); // ポインタから値(int型)を取得 Marshal.FreeHGlobal(ptrTotal); // メモリ解放 // 出力構造体分のバッファ確保(これが問題) int ii; struct_D[] dataOut = new struct_D[Total];                  // 出力構造体の宣言 IntPtr[] ptrdataOut = new IntPtr[Total]; // 出力構造体のポインタの宣言 for (ii = 0; ii < Total; ii++)                          // 出力構造体のサイズ分のメモリを確保 { ptrdataOut [ii] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(struct_D))); Marshal.StructureToPtr(dataOut[ii], ptrdataOut[ii], false); } // DLLの関数実行 Func_C(ptrdataOut, num, dataIn); // ポインタから値(構造体)を取得(受け取れない…) for (ii = 0; ii < Total; ii++) { dataOut[ii] = (struct_D)Marshal.PtrToStructure(ptrdataOut[ii], typeof(struct_D)); Marshal.FreeHGlobal(ptrdataOut[ii]); } } }

<B.dll(呼び出し先)>

// C#受渡し用構造体 struct gyddwsproduct { long val1; // 値1 char val2[16]; // 値2(値が省略("")の可能性あり) char val3[60]; // 値3 } // 関数1(配列サイズ取得関数)←こちらは問題なく動作する DLLEXPORT int __stdcall GetTotal(int* Total) { … } // 関数2(構造体配列の取得関数) DLLEXPORT int __stdcall Func_C(struct_D* dataOut, int num, struct_D dataIn) { … // 入力引数(num, dataInは問題なく取得できている) }

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

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

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

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

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

guest

回答2

0

ベストアンサー

C#

1 // アンマネージなメモリサイズ1個分 2 var size = Marshal.SizeOf(typeof(struct_D)); 3 IntPtr ptr = Marshal.AllocHGlobal( size * Total); 4 5 //略 (Marshal.Copyはいらない) 6 7 //受け取り 8 struct_D[] dataOut = new struct_D[Total];  9 for ( int i = 0; i < Total; ++i) 10 { 11 //メモリずらしながら1個ずつ取り出す 12 struct_D pp = (struct_D)Marshal.PtrToStructure(ptr + (size*i), typeof(struct_D)); 13 dataOut[i] = pp; 14 } 15 Marshal.FreeHGlobal(ptr);

実際に動作させていませんが、こんな受け取り方でいけるはず

投稿2019/06/14 12:40

編集2019/06/14 12:43
yominet

総合スコア187

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

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

yukibeatles

2019/06/14 23:26

ご回答ありがとうございます。 これは動きそうだな、と思って、//略 と記載頂いている部分にDLLの関数を追加し、 実装してみましたが、受け取れませんでした。。 コンパイル時エラーはしませんが、dataOutの各要素の構造体の各値がDLLの出力すべき値と一致しません。 (値が全く異なるので恐らく参照しているアドレスが正しくないのだと思います)
yominet

2019/06/16 11:27

1.修正したコードを質問に「追記」してみて 2.どの値がおかしいか確認 #途中でズレが起きているのか最初の最初からおかしいのか? 3.使用する構造体は数値だけにしてみて
yukibeatles

2019/07/08 09:54

大変回答が遅くなり申し訳ございません。 質問させて頂いた件ですが、なんとか解決いたしました。。 原因は、呼び出し先でもメモリを確保してしまっていたためでした。 (callocでメモリを確保していた) 冷静に考えれば、当たり前だったのですが、お恥ずかしい限りです。 ありがとうございました。
guest

0

C#側からC++(DLL)に渡すIntPtr配列はアンマネージ配列のメモリをC#で確保してDLLに渡す必要があるはずです。
詳しくは以下を参考にしてください。

C#からC++のDLLを使う

投稿2019/06/11 08:40

PineMatsu

総合スコア3579

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

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

yukibeatles

2019/06/11 11:44

ご回答ありがとうございます。 そうですよね、上記コード内の「出力構造体のサイズ分のメモリを確保」のところでIntPtr配列分のメモリを確保してやっているつもりなのですが、どうもうまく行っていないようです。
PineMatsu

2019/06/11 12:57

配列の要素はアンマネージでとってますが、それを格納する配列は普通にnewで確保しているのがいけないと思います。配列の方もアンマネージで確保しないと。
yukibeatles

2019/06/13 08:43

コメントが遅くなり申し訳ありません。 いろいろ試行錯誤しているのですが、うまくいかずにいます。。 ご教示頂いた上記URLを参考にすると、 1.ポインタ(IntPtr)についてアンマネージで一括でメモリ確保する 2.DLLの関数に1.のポインタを渡す 3.2.のポインタをマネージのポインタ配列(IntPtr[])にコピーする 4.ポインタ配列から要素ごとに中身(構造体)を取得 といった手順でよいのかな、と認識し、下記のようにプログラムを書き直しました。 ”出力構造体分のバッファ確保”の部分を以下のように書き直しました。 int ii; struct_D[] dataOut = new struct_D[Total];            // 出力構造体の宣言 IntPtr[] ptrdataOut = new IntPtr[Total]; // 出力構造体のポインタの宣言 // メモリ確保 IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(struct_D)) * Total); // DLLの関数実行(Func_Cの第一引数の定義をIntPtr[]からIntPtrに変更しました。) Func_C(ptr, num, dataIn); // マネージ配列へコピー Marshal.Copy(ptr, ptrdataOut, 0, Marshal.SizeOf(typeof(struct_D)) * Total); // 構造体を取得 for (ii = 0; ii < Total; ii++) { dataOut[ii] = (struct_D)Marshal.PtrToStructure(ptrdataOut[ii], typeof(struct_D)); } // メモリの解放 Marshal.FreeHGlobal(ptr); 以上を実行すると、マネージ配列へコピーする箇所で下記エラーが発生します。 追加情報:要求された範囲が配列の終わりを越えています。 恐らく、ptrdataOutのメモリ確保がうまくできておらず、メモリ確保に失敗しているのだと思うのですが。。
PineMatsu

2019/06/13 10:29

ptrdataOutがIntPtrの配列になってるのでサイズ違いでエラーになってます。ptrdataOutをsrtruct_Dの配列にしないと。
yukibeatles

2019/06/14 02:46

申し訳ありません、根本的に私が勘違いしているようです。 私の認識では、 ptrdataOutはポインタ(IntPtr)の配列であり、ptrdataOut[ii]のポインタがさすアドレス先にsrtruct_D分のメモリを確保する必要があるという認識だったのですが、誤っているでしょうか…? ptrdataOutをsrtruct_Dの配列にしてみると、マネージ配列へコピーする際、ポインタから値(構造体)を取得する際、型がポインタでないためにエラーしてしまいます。 お手数ですが、具体的にコードをご教示頂けませんでしょうか…? ご迷惑おかけしまして申し訳ございませんが、よろしくお願い致します。
PineMatsu

2019/06/14 20:46

ポインターの認識はあってますけど、そこにコピーできるのはポインターです。 そもそも、マネージ配列にマーシャルコピーする必要はなくて、PtrToStructureで構造体のサイズ分をずらしながら構造体の配列に入れて行けばいいはず。 今はスマホでしかアクセスできないのでコードは示せません。あしからず。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問