-1

この質問をする方法がわかったので、ここで新しい質問を作成していますが、私はまだ PInvoke の初心者です。

次の構造を持つC APIがあります。

    typedef union pu
    {
        struct  dpos   d;
        struct  epo    e;
        struct  bpos   b;
        struct  spos   c;
     } PosT ;


    typedef struct dpos
    {
        int     id;                       
        char    id2[6];      
        int     num;
        char    code[10];
        char    type[3];
     } DPosT ;

および次の API 関数:

    int addPos(..., PosT ***posArray,...)

これをCで次のように呼び出す方法:

    int main(int argc, const char *argv[])
    {
        ...
        PosT  **posArray = NULL;
        ...

        ret_sts = addPos(..., &posArray, ...);
        ...
    }

addPos() 内のメモリは posArray に割り当てられ、データも取り込まれます。割り当ては calloc を使用して次のようになります。

    int addPos(..., PosT ***posArray, ...)
    {
         PosT **ptr;
         ...
         *posArray = (PosT **) calloc(size, sizeof(PosT *));
         *ptr = (PosT *)calloc(...);
         ...
         (*posArray)[(*cntr)++] = *ptr;
         ...

         /* Population */
         ...
      }

そのメモリの割り当てを解除するために呼び出される別の関数があります。

今、私はC#で同じことをしたいのですが、

C# クラスでこれを作成しました。

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DPositionT
    {
       public int Id;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.Id2Len)]
       public string Id2;

       public int Num;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.CodeLen)]
       public string Code;

       [MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.TypeLen)]
       public string type;
    }

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit )]
    struct PosT
    {
        [System.Runtime.InteropServices.FieldOffset(0)]
        DPosT d;
    };

クライアントコードで共用体のこのメンバーのみを使用するため、d のみを定義しました。

addPos() を呼び出すには、 posArray をどのように作成して渡す必要がありますか?

あなたの助けは非常に高く評価されています.

4

2 に答える 2

2

注意すべき非常に重要なことの 1 つは、PosT*** の 3 番目の * は、割り当てられたポインターを呼び出し元に返す可能性を提供するためだけにあるということです。これは、実際には型が PosT** であることを意味し、C# のパラメーターはreforout修飾子のいずれかで宣言する必要があります。

あなたが提供したフラグメントは不完全ですが、いくつかのことを伝えています:

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     *ptr = (PosT *)calloc(...);   // B1
     ...
     (*posArray)[(*cntr)++] = *ptr;  // B2

(A) 配列では、PosT* が作成され、(B1)+(B2) では、その配列のいくつかのセルが非公開サイズのいくつかの新しい配列に初期化されます。コード スニペットでは、最初の「some」と 2 番目の「some」が無関係である可能性があることに注意してください。

また、これらの配列のサイズは、おそらくパラメータに由来する最上位の「サイズ」を除いて不明です。

つまり、C# の観点からは、実際に受け取りたいデータ構造体は "PosT[][]" であるため、P/Invoke 署名は次のようになります。

[...]
... addPos(..., out PosT[][] posArray, ....)

したがって、C# でも、C/C++ と同様に、パラメーターによって返される 2 次元のギザギザの配列になります。

ただし、不特定のデータ ブロックを指すルーズ ポインターを使用できる C とは異なり、C# では、すべての配列の長さが正確にわかっている必要があります。「サイズ」はおそらくわかっているので、マーシャラーに最初の次元のサイズを伝えることができます。

外側の「サイズ」が定数の場合:

[...]
... addPos(..., [MarshalAs(SizeConst=100)]out PosT[][] posArray, ....)

または、パラメーターで渡される場合:

[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out PosT[][] posArray, ....)

もちろん、「サイズ」が実際には最初のパラメーターであると仮定します。

ただし、これらすべてが内部配列のサイズを指定しているわけではありません。さらに、提供されたコード スニペットでは、これらの内部配列の長さが異なる場合があります。あなたが提示したコードスニペットで遊んでみましょう:

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     (*cntr) = 0

     for(int idx = 0; idx<size; ++idx)
     {
         *ptr = (PosT *)calloc(5+40*(*cntr));   // B1

         (*posArray)[(*cntr)++] = *ptr;  // B2
     }

さらに悪いことに、スニペットは内部配列が一意であるかどうかさえ示していないため、スニペットではこれが許可されています。

     *posArray = (PosT **) calloc(size, sizeof(PosT *)); // A

     (*cntr) = 0

     *ptr = (PosT *)calloc(5);   // B1
     *ptr2 = (PosT *)calloc(25);

     for(int idx = 0; idx<size / 2; ++idx)
     {
         (*posArray)[(*cntr)++] = *ptr;  // B2
         (*posArray)[(*cntr)++] = *ptr2;
     }

ここでは 2 つの内部配列のみを割り当て、外部配列のすべてのセルをこれら 2 つの内部配列のいずれかを指すように設定することに注意してください。これも完全に正しいデータ構造であり、スニペットで可能です。

これら 2 つのケースのいずれにおいても、そのようなデータ構造のレイアウトについて .Net Marshaller に伝える適切な方法はありません。ポインターの配列として外側の配列を取得する必要があります。

[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out IntPtr[] posArray, ....)

(PosT**) を (void*) にキャストすると想像できます。その後、プログラムのどこかで、さまざまな IntPtr を適切な長さの PosT[] に手動でアンパックする必要があります。もちろん、実際に正しい長さを推測する必要があります。

IntPtr から構造体の配列を読み取る方法は次のとおりです。 IntPtr から構造体の配列を取得する

編集:

ああ、もちろん、C#側ではパラメータを生のポインタとして取得できることを完全に忘れていましout PosT[][] posarrayた。 C# 側の「addPos」関数。unsafe 修飾子の入門書は次のとおりです。PosT*** posarray

http://www.codeproject.com/Articles/1453/Getting-unsafe-with-pointers-in-C

于 2012-12-20T16:52:43.843 に答える
0

[更新] OK、ここでいくつかの進歩を遂げました。

out IntPtr[] アンマネージコードから戻るときと同じように渡すと、 例外がスローされます。しかし、私はこのリンクに出くわしたので、使用して渡そうとしましたが out IntPtr 、うまくいくようです。少なくとも今はポインタを取り戻しました。必要なものを取得するために、すべてをマーシャリングする作業を開始する必要があります。

于 2013-01-03T14:31:14.240 に答える