【发布时间】:2013-02-19 10:47:38
【问题描述】:
我有一个用 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- 我收到带有消息“VariantWrappers cannot be stored in Variants.”的ArgumentException - 我尝试过使用
Marshal.AllocHGlobal并使用Marshal.GetNativeVariantForObject(),但我只在COM 端得到int。
tlbimp 默认将相关参数编组为UnmanagedType.Struct。我不确定如何将tlbimp 编组为IntPtr,或者即使这会有所作为(我也尝试使用来自 CodePlex 的增强型tlbimp2,但它似乎无法识别我的请求IntPtr 在其配置文件中)。
我绝不是 Interop 专家,因此请随意提出一些对您来说可能显而易见的建议。
更新 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;
...
}
【问题讨论】:
-
你能给出接口的确切定义和方法的签名吗?
-
@ZdeslavVojkovic 在上面的问题中添加了 IDL 的相关部分、签名以及我如何称呼它。
-
我相信如果不重写生成的互操作 dll 中的编组信息,这是不可能的。如果您将重写以将数组声明为
IntPtr而不是ref object,那么它可以工作 -
我放弃了
TLBIMP,自己编写了互操作代码。即使我将数组声明为IntPtr并在调用该方法之前手动将数组编组为IntPtr,它仍然以VT_ARRAY | VT_VARIANT的形式出现在远端,而不是VT_BYREF | VT_ARRAY | VT_VARIANT。在这里完全被难住了——下一步是尝试制作一个 C++ 包装器并从 C# 调用它(如下面的@jacob-seleznev 建议的那样。 -
是的,这里与修改后的互操作 IL 相同...
标签: c# com interop marshalling com-interop