C#からCスタイルの配列を取得する関数のIDL宣言(C ++)
質問
私は、C#フロントエンドを備えたC ++で記述されたいくつかのCOMインターフェイスで構成される既存のコードベースを使用しています。追加する必要がある新しい機能があるので、COM部分を変更する必要があります。特定のケースでは、配列(C#から割り当てられた)をコンポーネントに渡す必要があります。
私がやりたいのは、C#からintの配列をメソッドに渡すことができるようにすることです:
// desired C# signature
void GetFoo(int bufferSize, int[] buffer);
// desired usage
int[] blah = ...;
GetFoo(blah.Length, blah);
作業中のいくつかのレンチ:
- C ++ / CLIまたはManaged C ++は使用できません(この場合、COMは廃止できます)。
- C#側を/ unsafeでコンパイルすることはできません(マーシャルの使用は許可されています)。
COMインターフェースはC#部分でのみ使用されるため(これまで使用されることはありません)、他のCOMコンシューマーとの相互運用性についてはあまり関心がありません。 32ビットと64ビット間の移植性も問題になりません(すべてが32ビットマシンでコンパイルおよび実行されているため、コードジェネレーターはポインターを整数に変換しています)。最終的には、C ++ / CLIに置き換えられますが、それは道のりです。
最初の試み
は次のようなものです:
HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);
また、出力TLB定義は次のとおりです(合理的なようです):
HRESULT _stdcall GetFoo([in] int bufferSize, [in] int* buffer);
次のようにC#によってインポートされる(それほど合理的ではない):
void GetFoo(int bufferSize, ref int buffer);
一緒に使用できました
int[] b = ...;
fixed(int *bp = &b[0])
{
GetFoo(b.Length, ref *bp);
}
... / unsafeでコンパイルできないことを除いて。
現時点
私は使用しています:
HRESULT GetFoo([in] int bufferSize, [in] INT_PTR buffer);
インポート対象:
void GetFoo(int bufferSize, int buffer);
そして次のように使用する必要があります:
int[] b = ...;
GCHandle bPin = GCHandle.Alloc(b, GCHandleType.Pinned);
try
{
GetFoo(b.Length, (int)Marshal.UnsafeAddrOfPinnedArrayElement(b, 0));
}
finally
{
bPin.Free();
}
どのように機能しますか...しかし、よりクリーンな方法を見つけたいです。
だから、質問は
この場合、TLBジェネレーターからのC#インポートに適したIDL定義はありますか?そうでない場合は、C#側で何ができると少し安全になりますか?
解決 3
うーん...私は私を近づける情報を見つけました...
このIDL宣言(C ++)
HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);
(MSIL)としてインポートされます
method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32& buffer) runtime managed internalcall
そして(MSIL)に変更した場合
method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32[] marshal([]) buffer) runtime managed internalcall
(C#)のように使用できます
int[] b = ...;
GetFoo(b.Length, b);
まさに私が求めていたもの
しかし、tlbimportによって生成されるランタイム呼び出し可能ラッパーのMSILを修正する必要のない他のソリューションはありますか?
他のヒント
つまり、32ビットマシンでは32ビット、64ビットマシンでは64ビットのIDLデータ型を要求しています。しかし、マーシャリングコードでポインターのように、ちょうどintとして扱う必要はありません。では、64ビットプロセスから32ビットプロセスに呼び出すときに、余分な32ビットに何が起こると思いますか?
物理学の違反のように聞こえます。
インプロセスのみの場合は、このディスカッションの最後を参照してください: http://www.techtalkz.com/vc-net/125190-how-interop-net-client-com-dll.html 。
推奨事項は、inttptrの代わりにvoid *を使用して[local]でフラグを立て、マーシャラーが関与しないようにすることです。
C#COMの操作性についてあまり知りませんが、SAFEARRAY(INT_PTR)などを使用してみましたか?