2

私はこれについて Google でぐるぐる回っており、あらゆる種類の議論、多くの提案を見つけることができますが、何もうまくいかないようです. イメージをバイト配列として受け取る ActiveX コンポーネントがあります。TLB のインポートを行うと、次の署名が付いてきます。

int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap);

それにバイト[]を渡すにはどうすればよいですか?

同様の署名でデータを返すことができる別の関数があり、「null」を渡すことができるため機能します。返される型は byte[1..size] (非ゼロ境界 byte[]) です。しかし、戻ってきたものを渡そうとしても、型の不一致の例外が発生します。


詳細:

IDispatch インターフェイス シグネチャのメソッドを編集してきました (ILSpy を使用して、自動生成された相互運用アセンブリからインターフェイスを抽出します)。次のほぼすべての組み合わせを試しましたが、常にタイプの不一致例外が発生します。

  1. 「ref」の追加と削除
  2. パラメータのデータ型を「byte[]」または「Array」に変更する
  3. [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)] としてマーシャリングします。MarshalAs をかなりいじってみると、IDispatch はこれらの属性を使用しないことに確信が持てるようになりました。

また、「ref object」インターフェースをそのまま使用して、別のタイプを渡そうとしました: byte[], Array.CreateInstance(typeof(byte) (どちらも同じだと思いますが、誰かがそれを提案しているのを見つけたので、できませんでした)しようとすると痛い)。

渡す適切な配列を作成する Delphi コードの例を次に示します。

var
   image: OLEVariant;
   buf: Pointer;

image := VarArrayCreate([0, Stream.Size], VarByte);
Buf  := VarArrayLock(image);
Stream.ReadBuffer(Buf^, Stream.Size);
VarArrayUnlock(image);

同じことを行う C コードを次に示します。C# から動作させることができない場合は、マネージ C++ を介して呼び出すことができると思いますが、すべてを 1 つのプロジェクトにまとめたいと思います。

long HandleImage(unsigned char* Bitmap, int Length)
{
    VARIANT vBitmap;
    VariantInit (&vBitmap);
    VariantClear(&vBitmap);

    SAFEARRAYBOUND bounds[1];
    bounds[0].cElements = Length;
    bounds[0].lLbound = 1;

    SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
    SafeArrayLock(arr);
    memcpy(arr->pvData, Bitmap, Length);
    SafeArrayUnlock(arr);
    vBitmap.parray = arr;
    vBitmap.vt = VT_ARRAY | VT_UI1;

    long result;
    static BYTE parms[] = VTS_PVARIANT;
    InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&result, parms,
        &vBitmap);

    SafeArrayDestroy(arr);
    VariantClear(&vBitmap);

    return result;
}
4

1 に答える 1

0

最終的に、100% C# コードでそれを行う方法を見つけました。Microsoft は、誰かがこのシグネチャを持つメソッドを使用してデータを渡す可能性があるとは考えていなかったようです。

また、ICustomMarshaler は IDispatch 呼び出しで呼び出されず、カスタム マーシャラーのブレークポイントにヒットすることはありません (そのインスタンスを取得するための静的メソッドを除く)。

この質問でのハンス・パッサントの答えは、私を正しい軌道に乗せました: C#から IDispatch COM インターフェイスのメンバーを呼び出す

そこにある IDispatch のコピーには、IUnknown の "Invoke" メソッドは含まれていませんが、必要に応じて System.Runtime.InteropServices.ComTypes の型を使用してインターフェイスに追加できます: http://msdn.microsoft.com/en -us/library/windows/desktop/ms221479%28v=vs.85%29.aspx

つまり、マーシャリング引数を 100% 制御できます。Microsoft は VARIANT 構造の実装を公開していないため、独自に定義する必要があります: http://limbioliong.wordpress.com/2011/09/19/defining-a-variant-structure-in-managed-code-パート2/

Invoke の入力パラメーターはバリアント配列であるため、それらをアンマネージ配列にマーシャリングする必要があり、バリアント出力パラメーターがあります。

バリアントができたので、何を含める必要がありますか? これは、自動マーシャリングが失敗する場所です。SAFEARRAY へのポインターを直接埋め込む代わりに、別のバリアントへのポインターが必要であり、そのバリアントは SAFEARRAY を指す必要があります。

P/Invoking これらのメソッドを介して SAFEARRAY を構築できます

したがって、主なバリアントは VT_VARIANT | VT_BYREF、それは別のバリアントを指している必要があります VT_ARRAY であり、それは SafeArrayCreate() によって生成された SAFEARRAY を指す必要があります。次に、その最も外側のバリアントをメモリ ブロックにコピーし、その IntPtr を Invoke メソッドに送信する必要があります。

于 2013-08-22T22:59:56.467 に答える