2011-07-29 11 views
2

現在、C++ API用のC#ラッパーを作成していますが、この構造体に依存する特定の構造体と関数はデバッグ時に非常に奇妙なエラーを出しています。PInvokeからの奇妙なエラーstruct/function

C++構造体:

int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices); 

構造体及び機能のようなインポートされ:この関数に続い

typedef struct 
{ 
    unsigned __int handle; 
    char name[80]; 
    unsigned int unique_ID; 
} DeviceInfo; 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct DeviceInfo 
{ 
    public UInt32 handle; 
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)] 
    public String name; 
    public UInt32 unique_ID; 
} 

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)] 
public static extern int get_device_info(ref DeviceInfo di, int length_of_di_array, ref numValidDevices); 

この構造体及び機能の使用目的ちょうどボードImアクセスからいくつかのデバイス情報を取得することです。現在、私はC++の関数本体にアクセスすることはできないので、100%動作しているとしか思えません(C++でうまく動作します)。

構造体の配列を実行する関数を使用すると、探しているデータが出力されるだけでなく、実行時にエラーが発生してさまざまなエラーウィンドウが表示されるという問題があります。

C#コード:

static void Main() 
{  
    int numValidDevices = 0; //initialize variable 
    DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices 

    for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices 
    { 
     rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data 
     Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID); 
    } 
    Console.ReadLine(); //stops console from closing prematurely 
    API_close(); //custom close function from the C++ API 
} 

エラーのデバッグ中(情報がまだ示されている): 「タイプ 'System.Threading.ThreadStateException' の未処理の例外は、System.dllの

で発生しました追加情報:スレッドは開始されていません。 「未処理の例外タイプの「システム:(情報が表示されない、プログラムの実行に失敗した)デバッグ中

エラー「型 『System.ExecutionEngineException』の未処理の例外がmscorlib.dllで発生しました」。 mscorlib.dllでAccessViolationException 'が発生しました

追加情報:保護されたメモリを読み書きしようとしました。これは他のメモリが壊れていることを示していることがよくあります。

コンソールウィンドウを閉じるとき: '0x7c9113c0'の命令が '0x00000000'のメモリを参照しました。メモリを書き込めませんでした。 " (時には「書かれた」の代わりに「読む」と言います)。

私はのPInvokeに多くの研究を行い、Microsoft InteropAssistant applicationに出くわした、などthis oneなど様々なスタックオーバーフローの記事、およびthis postはイムがやって何にでも近いようだが、私はまだ元帥の使用方法に掘っていました.CoTaskMemAlloc/Free、それがanyhtingを行う場合でも参照してください...

これまで私の構造体と関数は正しいですが、私はIntPtrを使用する構造体を変更しようとしましたが、 di.name値とdi.unique_IDはジバリッシュになります(奇妙なことにディ。ハンドルが)有効なまま

C#コード:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct DeviceInfo 
{ 
    public UInt32 handle; 
    IntPtr p_name; 
    public String name { get { return Marshal.PtrToStringAnsi(p_name); } } 
    public UInt32 unique_ID; 
} 

意図した出力:

Handle: 3126770193 
Name: DEVICE_A 
Unique ID: 12345678 

のIntPtr出力:奇妙な

Handle: 3126770193 
Name: 
Unique ID: 1145128264 

、エラーのどれでのIntPtr結果を使用して上記の通り、うまく動作します。 これは、文字列にC + + charをマーシャリングすることに問題があると考えていますが、マーシャリング、メモリ管理(問題はありませんか?)、または私が完全にキャッチしていない問題。

任意およびすべてのフィードバックは本当に

+0

あなたは1つの参照を渡していますが、配列が必要です。 – leppie

+0

また、Cで 'sizeof'がCで==' sizeof'であることを確認してください。名前が間違って宣言されていると思われます。代わりにbyte []を試して、値の配列サイズの定数を適用してください。 – leppie

+0

質問を編集するのではなく、回答を投稿することになっています。あなたはそうしたいですか? –

答えて

2

何かがここでは、合算していない...私は今、数週間のためにこれに困惑してきた、高く評価されるだろう。関数がどのように呼び出されるはずであるかはわかりません。特に

、この宣言:

int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices); 

は、あなたがそれを使用しているどのように一致していません:

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)] 
public static extern int get_device_info(ref DeviceInfo di, const int length_of_di_array, int* p_numValidDevices); 

... 

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices 
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices 
{ 
    rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data 
} 

あなたはインデックスで始まる、配列の長さが16あるということを言っていますi、これは間違っています。一度に配列の要素を1つだけ渡すことを意味しましたか?

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices 
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices 
{ 
    rc = get_device_info(ref di[i], 1, ref numValidDevices); //accesses each device element and returns the data 
} 

または、アレイ全体を1回渡すことを意味しましたか?

DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices 
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data 
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices 
{ 
    Console.WriteLine(...); 
} 

P.S.私はあなたのp /する宣言を呼び出して変更することを検討します:

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)] 
public static extern int get_device_info(
    [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di, 
    int length_of_di_array, 
    ref int p_numValidDevices); 
+0

あなたは完全に正しいです、私は関数の使用法を再評価し、あなたがそこに持っているものに私のDllImportを変更し、それはトリックをした!ありがとうございました! (解説は元の投稿の編集として追加されています) – Calvin

2

あなたが得る例外は、あなたがpinvokingているアンマネージコードがガベージコレクトヒープを破壊していることを示しています。それはなぜクリスタルではありませんが、ピンボケ・マーシャルに右のことをする機会を与えることはほとんどありません。配列を正しく固定できません。適切に関数を宣言することから始めので1を宣言し、それが配列をとる:

[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)] 
public static extern int get_device_info(
    DeviceInfo[] di, 
    int length_of_di_array, 
    out int p_numValidDevices 
); 

DEVICEINFOのあなたの最初の宣言は、文字列がポインタではありませんので、2回目ではない、正しいです。

+0

そうですね。何らかの出力を強制するために、いくつかのハッキングされたコードを実行していました。 – Calvin

0

以下の回答で指摘されているように、私は2つの問題がありました。
1.私はDllImportを正しく呼び出しませんでした。出力をハックしようとした私は構造体の配列にメモリ割り当てをねじ込んだ。
2.私は出力をハックしてコードをさらに壊しました(DeviceInfo配列diを1つの要素di [number]としてではなく全体として渡そうとしました)。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct DeviceInfo 
{ 
    public UInt32 handle; 
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)] 
    public String name; 
    public UInt32 unique_ID; 
} 

[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)] 
public static extern int get_device_info(
    [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di, 
    int length_of_di_array, 
    ref int p_numValidDevices); 

static void Main() 
{ 

    int numValidDevices = 0; 

    DeviceInfo[] di = new DeviceInfo[16]; 

    get_device_info(di, 16, ref numValidDevices); 

    for (int i = 0; i < numValidDevices; ++i) 
    { 
     Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID); 
    } 

    Console.ReadLine(); 
    API_close(); 
}