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

C# からのポインターを引数に取る DLL 呼出し

環境/言語:[Windows XP VS2008 .Net Framework 3.5]
分類:[.NET]


こんにちは、いつも参考にさせて頂きありがとうございます。
初めての投稿です。 うまくいかず、困っており投稿させて頂きました。
ご教授の程、宜しくお願い致します。

C++から 既存のDLL(ソース無し)を下記のように呼出して正常に動いています。
今回これを C#から呼出そうとしていますが、「 'System.ExecutionEngineException' の例外がスローされました。」のエラーが出ます。
ご教授、宜しくお願いします。

// *** 正常に動作している C++ からの DLL 呼出し ***
typedef void ***Bd;
Bd val = NULL;
extern "C" int func() {
  DllFunc( &val );
   :
   :
}

// *** エラーとなる C# からの呼出しプログラム ***
public Form1() { // フォームからの呼出し
InitializeComponent( );
DllLib lib = new DllLib();
lib.func();
}
unsafe class DllLib {
uint val = 0;
public void func() {
fixed( uint *p = &val ) {
DllDef.DllFunc( p ); // ここで、ExecutionEngineException
}
}
}

class DllDef {
[DllImport("MakerDll.dll")]
public static extern unsafe int DllFunc( uint *val );
}
// *** ここまで ***
> typedef void ***Bd;
> Bd val = NULL;
> DllFunc( &val );
これを見る限り、

> public static extern unsafe int DllFunc( uint *val );
uint* じゃ全然間接参照が足りなさそうですけど。
■No26865に返信(Hongliangさんの記事)
> > typedef void ***Bd;
>>Bd val = NULL;
>>DllFunc( &val );
> これを見る限り、
>
>>public static extern unsafe int DllFunc( uint *val );
> uint* じゃ全然間接参照が足りなさそうですけど。

「間接参照が足りない」とは、Bd が4バイトしか確保されず DllFunc 内部でオーバーアクセスする、と言う意味でしょうか?

val は void ***Bd なのでポインタ分の領域だけ用意し、DllFunc 内部で構造体などへのポインタとしてアドレスがセットされると判断しました。

DLL のマニュアルには int DllFunc( Bd *val ) となっています。
どのように修正すればよろしいでしょうか?

宜しくお願いします。
取り敢えず uint* の代わりに out IntPtr を使ってみて下さい。
あとは呼び出し規約の確認かな−。
■No26866に返信(Hiroさんの記事)
> 「間接参照が足りない」とは、Bd が4バイトしか確保されず DllFunc 内部でオーバーアクセスする、と言う意味でしょうか?

C 言語におけるポインタについて調べ直してください。
* が増えればバイト数が増えるという意味ではありません。

unsigned int val = 0;
unsigned int *a = &val;
unsigned int **b = &a;
unsigned int ***c = &b;
unsigned int ****d = &c;

こう置いた場合、C# の uint* は 2 行目の a と同じ層なので、関数が求めている 5 行目の d の層には届いていませんよね。
■No26869に返信(Azuleanさんの記事)
> ■No26866に返信(Hiroさんの記事)
>>「間接参照が足りない」とは、Bd が4バイトしか確保されず DllFunc 内部でオーバーアクセスする、と言う意味でしょうか?
>
> C 言語におけるポインタについて調べ直してください。
> * が増えればバイト数が増えるという意味ではありません。
>
> unsigned int val = 0;
> unsigned int *a = &val;
> unsigned int **b = &a;
> unsigned int ***c = &b;
> unsigned int ****d = &c;
>
> こう置いた場合、C# の uint* は 2 行目の a と同じ層なので、関数が求めている 5 行目の d の層には届いていませんよね。
すみません、言葉足らずでした。

> unsigned int val = 0;
> unsigned int *a = &val;
> unsigned int **b = &a;
> unsigned int ***c = &b;
> unsigned int ****d = &c;
上記、val, a, b, c, d 全て関数として渡すときは、コールスタック分として4バイトしか消費しませんよね。
「val は void ***Bd なのでポインタ分の領域だけ用意」とは、val はポインタなので、uint* を用いて4バイト分のコールスタックを確保した、と言う意味です。
宣言方法が不明なための苦肉の策です。

> こう置いた場合、C# の uint* は 2 行目の a と同じ層なので、関数が求めている 5 行目の d の層には届いていませんよね。
d層に届くにはどうすればよろしいでしょうか?
■No26867に返信(Hongliangさんの記事)
> 取り敢えず uint* の代わりに out IntPtr を使ってみて下さい。
> あとは呼び出し規約の確認かな−。

早々の回答ありがとうございます。
やってみましたがだめでした。 同じエラーメッセージです。
DLL 側にデバッガで入っていけないので確定的な事はいえませんが、呼出し規約は確認した積もりです。
C++ からの呼出しではOKなので、C# の呼出し方の問題だと思うのですが...
う〜ん、難儀な。
■No26871に返信(Hiroさんの記事)
> 上記、val, a, b, c, d 全て関数として渡すときは、コールスタック分として4バイトしか消費しませんよね。
> 「val は void ***Bd なのでポインタ分の領域だけ用意」とは、val はポインタなので、uint* を用いて4バイト分のコールスタックを確保した、と言う意味です。
> 宣言方法が不明なための苦肉の策です。
なるほど。そういった狙いでしたか。

>>こう置いた場合、C# の uint* は 2 行目の a と同じ層なので、関数が求めている 5 行目の d の層には届いていませんよね。
> d層に届くにはどうすればよろしいでしょうか?
申し訳ありませんが、こっちは本筋じゃないようなので止めさせてください。
Bd 型が void のポインタベースであり、ハンドルか何かのように見受けられること、C# の型とひも付けするとしても、どのように使うものかわからないことからです。


■No26872に返信(Hiroさんの記事)
> DLL 側にデバッガで入っていけないので確定的な事はいえませんが、呼出し規約は確認した積もりです。
ちなみに、呼び出し規約は何だったのでしょうか?
また、この関数で得られたポインタはどのように利用されるのでしょうか?
あとは、差し支えがなければ、C++ 側のヘッダーファイルに用意されている、プロトタイプ宣言を掲載していただけないでしょうか。
Azulean 様

折角頂いたコメントに対して無視した格好になり大変申し訳ありませんでした。
完全に見落としていました。


> ちなみに、呼び出し規約は何だったのでしょうか?
> また、この関数で得られたポインタはどのように利用されるのでしょうか?
> あとは、差し支えがなければ、C++ 側のヘッダーファイルに用意されている、プロトタイプ宣言を掲載していただけないでしょうか。

レス頂けるとは思っていませんが、遅まきならがレスさせて頂きます。
関数規約の抜粋、英語ですが添付します。
得られたポインタは、お察しの通りハンドルです。この為、Cs側の上位では一切
使用していません。 DLL を呼出す際に使用するのみなので、型を適当に変換しても
問題はありません。


Cs の呼出し側
fixed( uint *p = &hBoard ) {
if ( LinxDll.CiBrdOpen( entry, p, LinxDll.CiSysInitialize ) != 0 )
ErrorThrow( 113, "Could not open board." );
}

Cs DLL 定義側
[DllImport("cid.dll")]
public static extern unsafe int CiBrdOpen( CiENTRY entry, uint *hBoard, uint val );

DLL 側
typedef void ***Bd; // BitFlow board handle.
BFRC BFCAPI CiBrdOpen(PCiENTRY pEntry, Bd *pBoard, BFU32 Mode)

以上、大変申し訳ありませんでした。
添付ファイル: 1275449051.zip (22 KB)
■No26902に返信(Hiroさんの記事)
> BFRC BFCAPI CiBrdOpen(PCiENTRY pEntry, Bd *pBoard, BFU32 Mode)
BFRC や BFCAPI の定義は何でしょうか?
BFCAPI は最終的に __stdcall などになるのでしょうか?

# 添付されているのはマニュアルの一片のようですが、勝手に公開して良いものだったのでしょうか?
■No26904に返信(Azuleanさんの記事)
回答ありがとうございます。

>>BFRC BFCAPI CiBrdOpen(PCiENTRY pEntry, Bd *pBoard, BFU32 Mode)
> BFRC や BFCAPI の定義は何でしょうか?
> BFCAPI は最終的に __stdcall などになるのでしょうか?
 BFRC は int に定義されています。
 BFCAPI は、__stdcall になっていました。

> # 添付されているのはマニュアルの一片のようですが、勝手に公開して良いものだったのでしょうか
 製作元の許可は取っていませんだ、Web で公開されている物なので大丈夫と思います。


?
■No26906に返信(Hiroさんの記事)
>BFRC BFCAPI CiBrdOpen(PCiENTRY pEntry, Bd *pBoard, BFU32 Mode)
あとは PCiENTRY や BFU32 の実体が何か、C# 側ではどのような定義をしているかかなぁ。
2010/06/04(Fri) 11:40:09 編集(投稿者)

ありがとうございました。 解決しました。

> ■No26906に返信(Hiroさんの記事)
> >BFRC BFCAPI CiBrdOpen(PCiENTRY pEntry, Bd *pBoard, BFU32 Mode)
> あとは PCiENTRY や BFU32 の実体が何か、C# 側ではどのような定義をしているかかなぁ。
初めて対応する Bd にばかり注意が向き、基本的なミスを犯していました。
PCiENTRY の中で、
char name[256] の代わりに、byte [] name = new byte[256];
としてしまい、name は 256 byte ではなくポインタ分の 4 byte しか確保されていなかった為、オーバーアクセスしていました。

色々ご指導ありがとうございました。 お騒がせしました。
解決済み!

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