C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: コールバック関数のマーシャリングについて


(過去ログ 129 を表示中)

[トピック内 3 記事 (1 - 3 表示)]  << 0 >>

■76543 / inTopicNo.1)  コールバック関数のマーシャリングについて
  
□投稿者/ キム (52回)-(2015/07/21(Tue) 19:12:13)

分類:[C#] 

お世話になります。

C#のバージョンは2010 / .NET Framework は4.0で、プラットフォームは x86 です。
C言語DLL側はWin32DLL(32bitのみ)です。

C言語で作成されたDLL内のAPIを呼び出しています。
このAPIにはコールバック関数を引数として渡す必要があります。
このコールバック関数のマーシャリングについて御知恵をお借りしたいです。
(特にSizeParamIndexについて)

C言語DLL側宣言

typedef BOOL (__stdcall *MyCallback)(LPCWSTR myMessage, BYTE* myData, unsigned int dataSize);
BOOL __stdcall MyAPI(MyCallback callBack);

myMessageは内容を変更不能なUNICODEヌル終端文字列です。
myDataは、内容を変更不能なバイト配列の先頭アドレスで、配列の要素数は引数dataSizeで示されます。

C#側宣言

[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool MyCallback(
    [MarshalAs(UnmanagedType.LPWStr)] string myMessage,
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] myData,
    uint dataSize);

[DllImport("MyAPIs.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern BOOL MyAPI(MyCallback callBack);

myMessageはUNICODEヌル終端文字列なので、[MarshalAs(UnmanagedType.LPWStr)]を指定した
string型で受けています。

myDataは引数dataSizeで長さが規定されるバイト配列の先頭アドレスなので、
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] を指定したbyte配列です。
UnmanagedType.LPArrayでC言語側の実体が配列であること、SizeParamIndex = 2でこの
配列の要素数を示すのが2番め(先頭=0)の引数であることを指定しています。

また、API、コールバック関数共に戻り値はWin32のBOOL(4バイト)なので、それを指定する
ために[return: MarshalAs(UnmanagedType.Bool)]をそれぞれに付加しています。

これで正常に動作しているのですが、コールバック関数のC#的にはdataSize引数が冗長なので
可能であれば省きたいのです。
単純に考えると上記delegateはprivateとしてAPIからのコールバックを受け、C#側に公開する
ためのdataSize引数を省いたdelegateをpublicで用意すれば良さそうです。
が、これだと二段階の呼び出しになってしまうので、もっと単純に一発でdataSize引数を省く
方法があればそれを使いたいのです。

自分で調べた限りでは見つけられなかったのですが、もし方法があればご教示いただけると嬉
しいです。
または、こんな方法では全然ダメでもっとスマートな方法があるのであれば、ご指摘いただけ
るととても嬉しいです。

以上、よろしくお願いします。

引用返信 編集キー/
■76544 / inTopicNo.2)  Re[1]: コールバック関数のマーシャリングについて
□投稿者/ Hongliang (327回)-(2015/07/21(Tue) 19:15:51)
> 一発でdataSize引数を省く方法があればそれを使いたいのです。
ありません。
引用返信 編集キー/
■76559 / inTopicNo.3)  Re[2]: コールバック関数のマーシャリングについて
□投稿者/ キム (53回)-(2015/07/22(Wed) 10:47:37)
No76544 (Hongliang さん) に返信

Hongliang さん、お返事ありがとうございます。

>>一発でdataSize引数を省く方法があればそれを使いたいのです。
> ありません。

やはり、一発で省く方法はないのですね。
私も更に調べてみましたし、簡単な方法がないのは確実なようです。
API呼び出し部分をprivateにしてコールバックを二段階にすればC#側に
公開する方のコールバックからはdataSize引数を省く事もできますが、
多少冗長になるのには目を瞑ってそのままの形で使うことにしました。

不要で冗長な引数を簡単に省くという目的は果たせませんでしたが、
おかげさまで迷うこと無く結論が出せて嬉しいです。
ありがとうございます。

一応まとめです。

■バイト配列(の先頭アドレス)と、その要素数を受け取るコールバック関数の受け方
・[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = n)]属性(nは要素数を示す引数の0から始まる
 インデックス)を付加した byte[] で受ける。
・C#的には配列はそれ自体の要素数がわかるので要素数引数を示す引数は冗長だが、マーシャリング
 には必須であり、それを一発で省くような方法はない。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -