DOBON.NET DOBON.NETプログラミング掲示板過去ログ

メンバにポインタを持つ構造体へのポインタをパラメータとするアンマネージDLL呼出し

環境/言語:[WindowsXP、Visual C# 2008、.NET Framework3.5]
分類:[.NET]

ポインタを含む構造体をパラメータとするアンマネージDLLをC#から使用したい
のですがうまくいかずに困っています。
どなたか、不備をご指摘願えませんでしょうか。宜しくお願い致します。

あるメーカ作成のアンマネージDLLで、Visual C/C++からのデータ読出しの関数
仕様として、以下のような呼出しを定義されています。

[読出し]
 WORD Read(WORD, A_READ*);

[構造体定義]
 typedef struct tag_A_READ
 {
WORD wNumber;
DWORD dwDataBufferLength; /* データを格納する領域の長さ */
LPBYTE lpbDataBuffer; /* データを格納する領域のポインタ */
 } A_READ;


これを先人が作成したDelphi6の以下のようなソースでアクセスすると、
データバッファにはデータを読み出すことができます。

[レコード定義]
PA_READ = ^TA_READ;
TA_READ = packed record
wNumber : Word;
dwDataBufferLength : DWORD; // Buffer length (Number of BYTE)
lpbDataBuffer : ^Byte; // Data buffer pointer
end;
[読出しバッファ定義]
 var
 A_Data: array[0..1023] of Word;

[読出し]
 procedure ADataRead;
 var
  AREAD: TA_READ;
 begin
  with AREAD do
  begin
  wNumber := 1;
  dwDataBufferLength := SizeOf(A_Data);
  lpbDataBuffer := @A_Data;
  end;
  if Read(5, @AREAD) <> 1 then
  begin
  MessageBox(0, Read_Error, nil, mb_OK);
  Halt;
  end;
 end;

Delphiには明るくないのですが、DLLに問題がないことを証明していると
考えています。
今回C#にて書き換えを試みているのですが、以下のようにC#コードを書くと
読出し関数から返ってこず最悪はブルースクリーンとなります。

[構造体定義]
 unsafe public struct A_READ
 {
public ushort wNumber;
public uint dwDataBufferLength;
public byte* lpbDataBuffer;
 }
[読出しバッファ定義]
 unsafe public byte[] a_data = new byte[2048];

[読出しメソッド]

 void DataRead
 {
fixed (byte* bp = &a_data[0])
{
read.wTransferLineNumber = 1;
read.dwDataBufferLength = 2048;
read.lpbDataBuffer = bp;

ret = Read(5, ref read);
}
}

現象からして、アンマネージコードでのメモリ破壊が起こっているように思い
ます。
a_data[0]に何か値を入れると、デバッガ上ではlpbDataBufferにはその値を読む
ことができるので、ポインタは期待通り渡せていると思うのですが、DLL内で異常
なアドレスへの書き込みが起こるためにブルースクリーンになる等の不安定動作
となっていると考えています。
上記C#コードのどこに不備があるのか、どなたかご指摘願えませんでしょうか。
可能性の吟味はできていませんが、とりあえず、DllImport 属性を付与した関数宣言の部分も掲載できませんか?
■No30459に返信(おじさんプログラマさんの記事)

lpbDataBufferをIntPtrで定義して
配列を渡す時に
Marshal.AllocHGlobal で確保してから
渡すようにするとどうでしょう?
■No30460に返信(Azuleanさんの記事)
> 可能性の吟味はできていませんが、とりあえず、DllImport 属性を付与した関数宣言の部分も掲載できませんか?

すいません。情報が不足しておりました。
[DllImport("xxx.dll")]
extern static ushort Read(ushort id, ref A_Read a_read);
で定義しているはずです。
週明けにならないとソースが見られないので、不正確かもわかりません。
■No30461に返信(shuさんの記事)
> ■No30459に返信(おじさんプログラマさんの記事)
>
> lpbDataBufferをIntPtrで定義して
> 配列を渡す時に
> Marshal.AllocHGlobal で確保してから
> 渡すようにするとどうでしょう?
>
アイディア頂き有難う御座います。
Marshalクラスはヒントになるだろうと思っておりましたが、
まだ把握できておりません。
上記を含め少し勉強し、動作確認させて頂こうと思います。
■No30463に返信(おじさんプログラマさんの記事)
> [DllImport("xxx.dll")]
> extern static ushort Read(ushort id, ref A_Read a_read);
> で定義しているはずです。
> 週明けにならないとソースが見られないので、不正確かもわかりません。

とりあえず、呼び出し規約を確認してみませんか。
__cdecl とか __stdcall とかその手のキーワード付与していないなら、プロジェクトのオプションとしてどうなっているかの確認かな。
呼び出し規約を取り違えていても、受け取り側のポインタは見えるかもしれません。ただ、その後の動きがおかしくなります。

もし、__cdecl なのであれば、DllImport で呼び出し規約を指定してください。
■No30465に返信(Azuleanさんの記事)
> とりあえず、呼び出し規約を確認してみませんか。
> __cdecl とか __stdcall とかその手のキーワード付与していないなら、プロジェクトのオプションとしてどうなっているかの確認かな。
> 呼び出し規約を取り違えていても、受け取り側のポインタは見えるかもしれません。ただ、その後の動きがおかしくなります。
>
> もし、__cdecl なのであれば、DllImport で呼び出し規約を指定してください。

わかりました。週明けに確認してみます。

書き忘れている部分がありました。追記しておきます。
C#での構造体の属性として
[StructLayout(LayoutKind.Sequential)]
を宣言しております。

DOBON.NET | プログラミング道 | プログラミング掲示板