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

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

ただいまの
回答率

90.48%

  • C#

    9215questions

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

  • C

    4641questions

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

  • C++

    4505questions

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

  • DLL

    110questions

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

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

受付中

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 192

yukibeatles

score 1

今回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は問題なく取得できている)
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

0

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/11 20:44

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

    キャンセル

  • 2019/06/11 21:57

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

    キャンセル

  • 2019/06/13 17: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のメモリ確保がうまくできておらず、メモリ確保に失敗しているのだと思うのですが。。

    キャンセル

  • 2019/06/13 19:29

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

    キャンセル

  • 2019/06/14 11:46

    申し訳ありません、根本的に私が勘違いしているようです。
    私の認識では、
    ptrdataOutはポインタ(IntPtr)の配列であり、ptrdataOut[ii]のポインタがさすアドレス先にsrtruct_D分のメモリを確保する必要があるという認識だったのですが、誤っているでしょうか…?

    ptrdataOutをsrtruct_Dの配列にしてみると、マネージ配列へコピーする際、ポインタから値(構造体)を取得する際、型がポインタでないためにエラーしてしまいます。

    お手数ですが、具体的にコードをご教示頂けませんでしょうか…?
    ご迷惑おかけしまして申し訳ございませんが、よろしくお願い致します。

    キャンセル

  • 2019/06/15 05:46

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

    キャンセル

0

    // アンマネージなメモリサイズ1個分
    var size = Marshal.SizeOf(typeof(struct_D));
    IntPtr ptr = Marshal.AllocHGlobal( size * Total);

    //略 (Marshal.Copyはいらない)

    //受け取り
    struct_D[] dataOut = new struct_D[Total]; 
    for ( int i = 0; i < Total; ++i)
    {
        //メモリずらしながら1個ずつ取り出す
        struct_D pp = (struct_D)Marshal.PtrToStructure(ptr + (size*i), typeof(struct_D));
        dataOut[i] = pp;
    }
    Marshal.FreeHGlobal(ptr);


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/15 08:26

    ご回答ありがとうございます。
    これは動きそうだな、と思って、//略 と記載頂いている部分にDLLの関数を追加し、
    実装してみましたが、受け取れませんでした。。

    コンパイル時エラーはしませんが、dataOutの各要素の構造体の各値がDLLの出力すべき値と一致しません。
    (値が全く異なるので恐らく参照しているアドレスが正しくないのだと思います)

    キャンセル

  • 2019/06/16 20:27

    1.修正したコードを質問に「追記」してみて

    2.どの値がおかしいか確認
    #途中でズレが起きているのか最初の最初からおかしいのか?

    3.使用する構造体は数値だけにしてみて

    キャンセル

同じタグがついた質問を見る

  • C#

    9215questions

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

  • C

    4641questions

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

  • C++

    4505questions

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

  • DLL

    110questions

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

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