如何通过引用将数组从C#传递到非托管COM(VT_BYREF) [英] How to pass an array from C# to unmanaged COM by reference (VT_BYREF)

查看:0
本文介绍了如何通过引用将数组从C#传递到非托管COM(VT_BYREF)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用C++编写的COM组件,我不能更改其源代码,它的一个方法的参数之一是VARIANT *pParamArray。使用tlbimp,我可以为其创建托管存根,并将C#中的数组传递给它。

遗憾的是,COM组件期望通过引用传递其数组-有对pParamArray->vt != (VT_BYREF | VT_ARRAY | VT_VARIANT)的显式检查,如果未通过该检查,则返回错误。

我有COM组件的PDB和源代码,因此我同时调试C#和非托管代码。我可以看到我的object[]的C#数组被作为VT_ARRAY | VT_VARIANT传递,据我所知,这本质上是一个SAFEARRAY

如何显式告诉C#我希望通过引用传递它,以便远端的类型具有VT_BYREF掩码?

  • 我已尝试将其放入VariantWrapper-我得到一个ArgumentException,消息为"VariantWrappers cannot be stored in Variants."
  • 我尝试了Marshal.AllocHGlobal和使用Marshal.GetNativeVariantForObject(),但在COM端只得到了int

tlbimp默认情况下将相关参数编组为UnmanagedType.Struct。我不确定如何使tlbimp将其编组为IntPtr,或者这是否会产生影响(我也尝试使用CodePlex中增强的tlbimp2,但它似乎无法识别我在其配置文件中对IntPtr的请求)。

我绝对不是Interop专家,所以请随意提出一些您可能觉得显而易见的建议。

更新%1

应@ZdesavVojkovic的要求,以下是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;

    ...
}

推荐答案

以下是如何在不重写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);

为方便起见,最好将其包装到接口上的扩展方法中。

这篇关于如何通过引用将数组从C#传递到非托管COM(VT_BYREF)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆