2010-12-29 206 views
2

構造体配列をC++ DLLに渡して問題を実行しようとしています。私は無駄に数日間それを把握しようとしてきました。 C++からデータを得ることができますが、.NETを使用して構造体配列を取得しようとすると問題が発生します。構造体配列をC++ DLLからC++へ渡す

[StructLayout(LayoutKind.Sequential, Size = 23), Serializable] 
public struct header 
{ 
    // HEADER 
    public UInt16 h_type; 
    public UInt32 frame_num; 
    public UInt16 count_1pps; 
    public byte data_options; 
    public byte project_type; 
    public byte tile_num;  
    public byte tile_set;   
    public byte total_rows;  
    public byte total_cols;  
    public byte num_rows;   
    public byte num_cols;  
    public byte first_row;   
    public byte first_col;  
    public UInt16 num_sensors;  
    public UInt16 num_data_bytes; 
    public byte h_checksum; 
} 

[StructLayout(LayoutKind.Sequential, Size = 25), Serializable] 
public struct footer 
{ 
    // FOOTER 
    public UInt16 f_type; 
    public byte ts_len;   
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] 
    public byte[] ts_array; 
    public byte frame_status; 
    public byte f_checksum;  
} 

[StructLayout(LayoutKind.Sequential, Size = 51), Serializable] 
public struct buffer_node 
{ 
    // HEADER 
    public header data_header; 

    // DATA 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 
    public byte[] data; 

    // FOOTER 
    public footer data_footer; 
} 

:私の構造体は次のように定義されるbuffer_nodeある

[DllImport("SocketAPI.dll")] 
static extern int api_get_data(int iSize, buffer_node[] data); 

C++プロトタイプは次のとおりです。として私のC#コードで

static __declspec(dllexport) int SocketAPI::api_get_data(int iSize, buffer_node *data); 

、私は機能を定義しました試してみたら、以下の輸入品:

​​

私のC#のGetDataプログラムは、現在、次のようになります。

// Get current data size 
int iSize = api_is_data_available(); 

// Create buffer to hold the data 
buffer_node[] buf_data = new buffer_node[iSize]; 

for (int i = 0; i < iSize; i++) 
{ 
    buf_data[i].data = new byte[3]; 
    buf_data[i].data_footer.ts_array = new byte[20]; 
} 

// Get the data 
//int iStructSize = Marshal.SizeOf(buf_data[0]); 
//IntPtr bufNodePtr = IntPtr.Zero; 
//IntPtr buffer = Marshal.AllocHGlobal(iStructSize * iSize); 
//api_get_data(iSize, buffer); 
//for (int i = 0; i < iSize; i++) 
//{ 
// IntPtr ptr = new IntPtr(buffer.ToInt64() + iStructSize * i); 
// buf_data[i] = (buffer_node)Marshal.PtrToStructure(ptr, typeof(buffer_node)); 
//} 

//api_get_data(iSize, buf_data); // See buffer, but everything is 0 - ie. not being populated 
// api_get_data(iSize, out buf_data); // fails no error 
api_get_data(iSize, ref buf_data); // fails no error 
// api_get_data(iSize, ref buf_data); 

// Print the data 
for (int i = 0; i < iSize; i++) 
{ 
    StringBuilder sb = new StringBuilder(); 
    sb.Append("Tile Number: " + Convert.ToString(buf_data[i].data_header.tile_num)); 
    AppendTextBox(sb.ToString()); 
} 

ありがとうございました。どのような助けも大いにありがたく思えます。私は単純な仕事になるので、本当にループのために私を投げています!

答えて

0

refoutのものは、最初の要素へのポインタではなく、参照へのポインタを渡すため、機能しません。


編集1:あなたが今やっているように私はちょうど気づいた、あなたの周りの配列を渡すことはできません - 構造体の内部管理しているアレイは、通常、あなたがそれらを望むようにマーシャリングされません。私は1つのことを考えるときに解決策を書くつもりですが、手作業でマーシャルする必要があると思います。


編集2:あなたが危険なコードを使用することができるしている場合は、この問題を修正する必要がありますし、このコードを使用し、ByValArrayからfixed byte[]にすべてを変更します。

[StructLayout(LayoutKind.Sequential, Size = 23), Serializable] 
public struct header 
{ 
    // HEADER 
    public UInt16 h_type; 
    public UInt32 frame_num; 
    public UInt16 count_1pps; 
    public byte data_options; 
    public byte project_type; 
    public byte tile_num;  
    public byte tile_set;   
    public byte total_rows;  
    public byte total_cols;  
    public byte num_rows;   
    public byte num_cols;  
    public byte first_row;   
    public byte first_col;  
    public UInt16 num_sensors;  
    public UInt16 num_data_bytes; 
    public byte h_checksum; 
} 

[StructLayout(LayoutKind.Sequential, Size = 25), Serializable] 
public struct footer 
{ 
    // FOOTER 
    public UInt16 f_type; 
    public byte ts_len;   
    public unsafe fixed byte ts_array[20]; 
    public byte frame_status; 
    public byte f_checksum;  
} 

[StructLayout(LayoutKind.Sequential, Size = 51), Serializable] 
public struct buffer_node 
{ 
    // HEADER 
    public header data_header; 

    // DATA 
    public unsafe fixed byte data[3]; 

    // FOOTER 
    public footer data_footer; 
} 

unsafe static extern int api_get_data(int iSize, buffer_node* pData); 


//... 

// Get current data size 
int iSize = api_is_data_available(); 

// Create buffer to hold the data 
buffer_node[] buf_data = new buffer_node[iSize]; 

unsafe 
{ 
    fixed (buffer_node* pBufData = buf_data) 
    { 
     api_get_data(iSize, pBufData); // fails no error 
    } 
} 

(あなた「要素へのポインタであることが宣言を変更する必要がLL)編集3


:私はちょうどあなたがこのような[Out]を言って試してみました...気づきましたか?

[DllImport("SocketAPI.dll")] 
static extern int api_get_data(int iSize, [Out] buffer_node[] data); 

これは、私が上記のことをやっていることの痛みなしにうまくいくかもしれません。

サイドノート:アライメントを変更しない限り、Size = 23は、構造体がデフォルトのアライメントになるようにパディングされるため、何もしません。

+0

こんにちは。ご返信ありがとうございます。私の好みは、安全なコードを使用することです。しかし、この時点で、私はそれが機能するようにしたい。私はあなたが「編集2」で書いたものを実装する方法を理解していません。ありがとう。 – user557615

+0

私の編集を参照してください。 :) – Mehrdad

+0

うーん... "アウト[アウト]"したかもしれない。私はもう少しテストをしなければならないでしょう。これはGoogleで検索するのは難しいことです。 [アウト]は何を意味しますか?それは外からどのように違いますか? – user557615

5

[DllImport]属性でCallingConventionプロパティを使用する必要があります。デフォルトはStdCallですが、C++宣言では__stdcallを使用していないので、ここではCdeclが必要です。

+0

こんにちは。ごめんなさい。私はそれにまったく従わなかった。説明してください。 – user557615

+0

Google + dllimportattribute + calling convention。 3回目のヒットはいいよね。 –

+0

これは正に正しい答えです。私は同様の問題を抱えており、この解決策で解決されました。この場合、次のように追加します。 '[DllImport(" SocketAPI.dll "、CallingConvention = CallingConvention.Cdecl)]' – rdrmntn

1

int iSizeが要素の配列のサイズ(例:data.Length)の場合は、MarshallAs.SizeParamIndexを試してみてください。これはマーシャラーにデータ内の要素の数を伝えます。

[DllImport("SocketAPI.dll")] 
static extern int api_get_data(int iSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] buffer_node[] data); 

アレイの詳細はmashalled at MSDNです。

+0

これは、配列が0になったことを再度示しています。私はあなたが提供したリンクをさらに読むでしょう。ありがとう。 – user557615

0

空の配列をC#からdllのC関数に渡す必要があるという同じ問題がありました。この関数は、構造体で埋められた配列の最初の要素を指すポインタを返します。

これは私が外部関数を宣言する方法です:問題の

[DllImport(LIB_NAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "getData")] 
unsafe extern void getData(IntPtr data, ref UInt32 dataLen); 

構造体:

[StructLayout(LayoutKind.Sequential)] 
internal struct DataC 
{ 
    internal UInt16 xRes, yRes; 
    internal fixed float rot[9]; 
} 

これは私が関数を呼び出すと、どのように私は私の構造体へのIntPtrをキャストする方法である:

unsafe 
{ 
    UInt32 dataLen = 10; 
    IntPtr dataPtr = Marshal.AllocHGlobal((int)dataLen * Marshal.SizeOf(typeof(DataC))); 
    getData(dataPtr, ref dataLen); 
    // check here for null, obviously 
    DataC* dataArr = (DataC*)dataPtr; 
    for (int i = 0; i < dataLen; i++) 
    { 
     DataC data = dataArr[i]; 
     // I fill a managed class/struct with the unmanaged data and add it to a List or whatever 
     result.Add(new Data(data->xRes, data->yRes, data->rot[0], ...)); 
    } 
    // As we have the data in managed memory now, we free the allocated space 
    Marshal.FreeHGlobal(dataPtr); 
} 
関連する問題