3

私は何日も検索し、見つけることができるすべてを試しましたが、まだこれを機能させることができません.

詳細: 管理されていない dll を呼び出すサード パーティの株式取引アプリがあります。dll が処理/フィルター処理してグローバル リング バッファーに保存するデータを提供しています。リング バッファは、長さが 100 の構造体の配列です。これらはすべて、株式取引アプリのプロセスで実行されます。

また、グローバル リング バッファー内の情報を可能な限り迅速かつ効率的に取得する必要がある別のプロセスで、同じ dll を呼び出すマネージド C# アプリもあります。配列内の最初の構造のデータしか取得できないことを除いて、すべてが機能します。また、C# から dll を呼び出した後、C# コードは arrayMD が構造体の配列であることを認識しなくなり、デバッガーに単純な構造として表示されます。問題の原因はdllのmemcpyでしょうか?[In, Out]、IntPtr、および Marchal.PtrToStructure の組み合わせであらゆる種類の組み合わせを試しました。私は大いに怒り狂っています。どんな助けでも大歓迎です。

ありがとう

これが私が試みていることです。dll 側:

struct stMD
{
  float Price;
  unsigned int  PriceDir;
  unsigned int  PriceDirCnt;
};

// Global memory
#pragma data_seg (".IPC")
    bool NewPoint = false;      // Flag used to signal a new point.
    static stMD aryMD [100] = {{0}};
#pragma data_seg()

void __stdcall RetrieveMD (stMD *LatestMD [])
{
    memcpy(*LatestMD, aryMD, sizeof(aryMD));
}

C# 側:

[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
    public float Price;
    public uint PriceDir;
    public uint PriceDirCnt;
};

public static stMD[] arrayMD = new stMD[100];

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(ref stMD[] arrayMD);

RetrieveMD(ref arrayMD);
4

3 に答える 3

2

問題は、DLL エントリ ポイントの定義です。

void __stdcall RetrieveMD (stMDP *LatestMD []) 

配列のサイズを指定しない場合、C# は配列にコピーされた要素の数をどのように知るのでしょうか? これは他の言語の問題でもあります。実装では、提供されたメモリが aryMD を格納するのに十分な大きさであると単純に想定しています。しかし、そうでない場合はどうなりますか?バッファ オーバーランが発生しました。

呼び出し元に配列を割り当ててもらいたい場合、呼び出し元は配列に含まれる要素の数も渡す必要があります。

編集

C++ 宣言は次のようになります。

// On input, length should be the number of elements in the LatestMD array.
// On output, length should be the number of valid records copied into the array.
void __stdcall RetrieveMD( stMDP * LatestMD, int * length );

C# の宣言は次のようになります。

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(
    [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref stMD[] arrayMD,
    [In, Out] ref int length);
于 2012-10-24T16:37:49.213 に答える
0

私はそれを働かせました。これをもっと難しくしようとしたことはありますか。

「.NET 2.0 Interoperability Recipes: A Problem-Solution Approach」の第 2 章を読み直しました。

これが機能するものです。

C++ 側では、ポインターを削除しました

struct stMD 
{ 
float Price; 
unsigned int PriceDir; 
unsigned int PriceDirCnt; 
}; 

// Global memory 
#pragma data_seg (".IPC") 
bool NewPoint = false; // Flag used to signal a new point. 
static stMD aryMD [100] = {{0}}; 
#pragma data_seg() 

void __stdcall RetrieveMD (stMD LatestMD [100]) 
{ 
memcpy(LatestMD, aryMD, sizeof(aryMD)); 
}

C# 側では、両方の (ref) を削除し、[In, Out] を追加しました。

[StructLayout(LayoutKind.Sequential)] 
public struct stMD 
{ 
    public float Price; 
    public uint PriceDir; 
    public uint PriceDirCnt; 
}; 

public static stMD[] arrayMD = new stMD[100]; 

[DllImport(@"Market.dll")] 
public static extern void RetrieveMD([In, Out] stMD[] arrayMD); 

RetrieveMD(arrayMD); 

助けてくれたみんなに感謝します。

于 2012-10-24T17:20:15.993 に答える
0

あなたの問題は、C# から C に参照によって配列を渡そうとしていることだと思いますが、それを行う必要はほとんどありません。あなたの場合、C#が100個の構造体にメモリを割り当て、そのメモリをCに渡して入力するだけです。あなたの場合、構造体の配列へのポインタを渡しています。本当に必要のない余分なレベルの間接化。

固定サイズの配列を取る C 関数はめったに見ません。代わりに、関数は通常、ポインターと長さを受け入れるように C で定義されます。たとえば、次のようになります。

void __stdcall RetrieveMD (stMDP *LatestMD, int size) 
{
   int count = 0;
   if (size < sizeof(aryMD))
     count = size;
   else
     count = sizeof(aryMD);

   memcpy(LatestMD, aryMD, count);      
}

このメソッドを C# から呼び出すには、2 つのことを行う必要があります。まず、渡す適切なサイズの配列を割り当てる必要があります。次に、マーシャリング コード (C# -> C のコピーを行う)に、マーシャリングするデータの量を属性を介して把握する方法を伝える必要があります。[MarshalAs]

[DllImport(@"Market.dll")]
public static extern void RetrieveMD (
  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] stMDP[] arrayMD,
  int length
);

var x = new stMDP[100];
RetrieveMD(x, 100);
于 2012-10-24T17:09:40.117 に答える