1

ソースを変更できないC++で記述されたCOMコンポーネントがあり、そのメソッドの1つに対する引数の1つはですVARIANT *pParamArray。を使用しtlbimpて、そのためのマネージドスタブを作成し、C#から配列を渡すことができます。

残念ながら、COMコンポーネントは、配列が参照によって渡されることを期待しています。明示的なチェックがpParamArray->vt != (VT_BYREF | VT_ARRAY | VT_VARIANT)あり、そのチェックに合格しなかった場合はエラーが返されます。

PDBとCOMコンポーネントのソースがあるので、C#とアンマネージコードの両方を同時にデバッグしています。私のC#配列object[]がとして渡されていることがわかります。VT_ARRAY | VT_VARIANTこれは基本的SAFEARRAYに私が理解している限りです。

VT_BYREF遠端の型にマスクが含まれるように、参照によって渡すことをC#に明示的に伝えるにはどうすればよいですか?

  • 入れてみましたVariantWrapper- 「 」ArgumentExceptionというメッセージが表示されますVariantWrappers cannot be stored in Variants.
  • Marshal.AllocHGlobalを使ってみましたが、COM側でMarshal.GetNativeVariantForObject()しか取得できません。int

tlbimpデフォルトでは、問題のパラメータを。としてマーシャリングしますUnmanagedType.Structtlbimpマーシャリングをどのように行うか、またはこれが違いを生むかどうかはわかりませんIntPtr(CodePlexからの拡張機能も使用しようとしましたが、構成ファイルでtlbimp2の要求を認識していないようです)。IntPtr

私は決して相互運用の専門家ではないので、あなたにとって明白に見えるかもしれない何かを自由に提案してください。

アップデート1

@ZdeslavVojkovicからのリクエストに応じて、IDLの関連する部分を次に示します。

[
    uuid(01234567-89AB-CDEF-0123-3456789ABCDE),
    version(1.0),
    helpstring("XXX")
]
library LAbc
{
    [
        object,
        uuid(01234567-89AB-CDEF-0123-3456789ABCDE),
        dual,
        helpstring("XXX"),
        pointer_default(unique)
    ]
    interface IAbc : IDispatch
    {
            [id(1), helpstring("XXX")]
            HRESULT CallFunction([in] myEnum Function, [in, out] VARIANT* pParamArray, [out, retval] long* pVal);
    };

    [
        uuid(01234567-89AB-CDEF-0123-3456789ABCDE),
        helpstring("XXXs")
    ]
    coclass Abc
    {
        [default] interface IAbc;
    };
};

そして、これがメソッドシグネチャ自体とパラメータのタイプの内部チェックです。

STDMETHODIMP XAbc::CallFunction(myEnum Function, VARIANT *pParamArray, long *pVal)
{
    ...

    // we must get a pointer to an array of variants
    if(!pParamArray ||
        (pParamArray->vt != (VT_BYREF | VT_ARRAY | VT_VARIANT)) ||
        !(psa = *pParamArray->pparray))
        return E_INVALIDARG;

    ...
}
4

1 に答える 1

2

ILを書き直さずに動作させる方法は次のとおりです。

簡単にするために、列挙型パラメーターをスキップしたため、メソッドのIDL定義は次のようになっていることに注意してください。

[
    object,
    uuid(E2375DCC-8B5B-4BD3-9F6A-A9C1F8BD8300),
    dual,
    helpstring("IDummy Interface"),
    pointer_default(unique)
]
interface IDummy : IDispatch
{
    [id(1)] HRESULT Fn([in, out] VARIANT *pParamArray, [out, retval]long *pVal);
};

次のように遅延バインディング呼び出しで呼び出すことができます。

INTEROPXLib.IDummy d = new INTEROPXLib.DummyClass();

object data = new object[3]; // method argument, i.e. pParamArray value

var t = typeof(INTEROPXLib.IDummy);
object[] args = new object[1]; // array which will contain all method arguments
args[0] = data; // data is the first argument, i.e. first element of args array

ParameterModifier[] pms = new ParameterModifier[1];
ParameterModifier pm = new ParameterModifier(1);
pm[0] = true; // pass the 1st argument by reference
pms[0] = pm;  // add pm to the array of modifiers 

// invoke Fn by name via IDispatch interface
var ret = t.InvokeMember("Fn", System.Reflection.BindingFlags.InvokeMethod, null, d, args, pms, null, null);
Console.Out.WriteLine("Result = " + ret);

便宜上、これをインターフェイスの拡張メソッドにラップすることをお勧めします。

于 2013-03-05T16:54:28.527 に答える