2016-04-05 18 views
1

私はこれに数日をかけてきましたが、今のところどこに到着するのに役立つ多くの質問を読んだことがあります。しかし、まだ助けが必要です。C++のdll関数の戻り値としての構造体のポインタ

私は説明します。私は、C#でそれを使用するためにラップしたいC++ DLLを持っています。 DLLのドキュメントがありますが、何も変更することはできません。

DLL documentation 
struct stChannel LookForAvailableChannels (const char *dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime) 

私はまた、これらの構造体を持っている:機能の多くは、基本的なDLLIMPORTの設定で動作しますが、私が正常に動作しないいくつかの機能を持っている、これはその一つであるのでしよう

struct stChannelInfo 
{ 
char ChannelTag[17]; 
char ChannelEnabled; 
} 

struct stChannel 
{ 
int ChannelNumber; 
struct stChannelInfo *ChannelInfo; 
} 

C# Code 
    [StructLayout(LayoutKind.Sequential)] 
    public struct stChannelInfo 
    { 
     public IntPtr ChannelTag; 
     public byte ChannelEnabled; 
    }; 

    [StructLayout(LayoutKind.Sequential)] 
    public struct stChannel { 
     public int ChannelNumber;  
     public stChannelInfo ChannelInfo; 
    }; 

[DllImport("NG.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] 
public static extern stChannel LookForAvailableChannels(string dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime); 

stChannel Estructura = new stChannel(); 

私はこのコードを火災を呼び出すボタンがあります:異なる事があり、多くのことを読んだ後、私は「partialyは」機能するソリューションに来て

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal); 

その後、私はEstructura.ChannelInfo.ChannelTagをマーシャリング:

string btListFile = Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag); 

これは実際に動作しますが、それは私はそれが正しいことを知っているデータを返します。 stChannelInfo stChannel内のstructはポインタなので、私は配列の最初の要素しか受け取っていません。これをC#でどのように処理するのか分かりません。

それは方法で行われるべきである私が今使用して、このコードこと:

Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag); 

Marshal.PtrToStringAnsi(Estructura.ChannelInfo[i].ChannelTag); 

が、私は今だけでは動作しません使用していたすべてのものであるべき。助けていただければ幸いです。

ありがとうございます。

EDIT:

C#コード [StructLayout(LayoutKind.Sequential)] パブリック構造体stChannelInfo { [MarshalAs(UnmanagedType.LPStr、SizeConst:ユーザアドリアーノRepetti今持ってこれに

おかげ= 17)] 公開ストリングChannelTag; パブリックバイトChannelEnabled; };

[StructLayout(LayoutKind.Sequential)] 
    public struct stChannel { 
     public int ChannelNumber; 
     public IntPtr ChannelInfo; 
    }; 

[DllImport("NG.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
public static extern stChannel LookForAvailableChannels(string dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime); 

stChannel Estructura = new stChannel(); 

私はこのコードまで火を呼び出すボタンがあります。

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal); 

var channelinf = (stChannelInfo)Marshal.PtrToStructure(Estructura.ChannelInfo, typeof(stChannelInfo)); 


     for (int i = 0; i < 4; i++) 
     { 
      var ptr = IntPtr.Add(Estructura.ChannelInfo, Marshal.SizeOf(typeof(stChannelInfo)) * i); 
      var channelll = (stChannelInfo)Marshal.PtrToStructure(ptr, typeof(stChannelInfo)); 
     } 

問題は今、それは私がここにAccessViolationExceptionを得ることです:

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal); 

しかし、私は本当に知りませんのなぜ、私は助けていただければ幸いです。

答えて

0

残念ながら、配列ChannelInfoは固定サイズではありません。自動[MarshalAs(UnamangedType.LPArray)]でマーシャリングできません。こうマーシャリングを手動ChannelInfoIntPtrとして宣言されなければならないことを意味します。あなたがそれを必要とする場合

[StructLayout(LayoutKind.Sequential)] 
public struct stChannel { 
    public int ChannelNumber;  
    public IntPtr ChannelInfo; 
}; 

あなたはそれ構造体へに変換する必要があります。

var channelInfo = (stChannelInfo)Marshal.PtrToStructure(
    Estructura.ChannelInfo, 
    typeof(stChannelInfo)); 

は、今あなたが最初の要素にアクセスしています、配列アイテムにアクセスするにはオフセットが必要です:

var ptr = IntPtr.Add(Estructura.ChannelInfo, 
    Marshal.SizeOf(typeof(stChannelInfo)) * itemIndex); 

それ以上の210。このように使用

static GetUnmanagedArrayItem<T>(IntPtr baseAddress, int index) { 
    var ptr = IntPtr.Add(baseAddress, Marshal.SizeOf(typeof(T)) * index); 
    return (T)Marshal.PtrToStructure(ptr, typeof(T)); 
} 

:あなたはヘルパーメソッドを記述することもできます

var channelInfo = GetUnamangedArrayItem<stChannelInfo>(Estructura.ChannelInfo, 1); 

あなたが明示的に手動でマーシャルあなたがすることができないことunamangedを尋ねるが、注意していませんでしたchar*文字列の長さが固定長の場合IntPtr

「UnmanagedType.LPStr」でそのフィールドを文字列で表すと宣言できます。 [MarshalAs(UnmanagedType.LPStr、SizeConst = 17)] public string ChannelTag;

EDIT:それはblittableはないので、私はこのことについて間違っていたが、Stringを使用すると、固定長の配列を持っているので、私が使用することをお勧めしたいもちろんIntPtrで間違って、戻り値で使用することはできません。

[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 17)] 
public byte[] ChannelTag; 

Encoding.ASCII.GetString(yourStruct.ChannelTag);でこの配列を単純にデコードすると、元の文字列を取得できます。代わりに、あなたはJaredParによって提案されたものをthis postに従うかもしれません。

+0

ありがとう、本当に何が起こっているのか理解するのに役立ちます。すべてがOKですが、この部分 'Marshal.SizeOf(typeof(T)* index))'はコンパイルされません。この演算子は、この2つの型には適用できません。解決する方法はわかりません。 – JosueSB

+0

_manual_マーシャリングを行う場合は、それも機能するはずです。ちょっとしたことがありますが、いくつかのヘルパー機能では合理的かもしれません。私が示唆したいのは、それらのまわりに完全なマネージラッパーを書くことです。 –

+0

編集済みの私の前のコメント:D – JosueSB

関連する問題