我在IDL / C ++ / VBA中混合DISPID_VALUE和varargs的行为非常奇怪。
我的IDL文件中有以下定义
[
uuid(78fc63e0-fdb5-11e1-a21f-0800200c9a66),
dual
]
interface IPyObj : IDispatch
{
// ...
[propget, id(DISPID_VALUE)]
HRESULT Item(
[in] VARIANT* Key,
[out, retval] VARIANT* Result
);
// ...
};
// ...
[entry("PyTuple"), helpstring("Builds a Python tuple object"), vararg]
HRESULT __stdcall PyTuple(
[in] SAFEARRAY(VARIANT)* Elements,
[out, retval] VARIANT* Result
);
IPyObj::get_Item
函数存储一个IDispatch
指针,类型为VT_DISPATCH
,在VBA的即时窗口中通过检查确认:
?VarType(myPyObj(1))
9
?VarType(myPyObj.Item(1))
9
?VarType(myPyObj(1)) = vbObject
True
当我按如下方式调用PyTuple
函数时
PyTuple(myPyObj.Item(1))
在函数内部我按预期找到了VT_DISPATCH
,但是当我这样称呼它时:
PyTuple(myPyObj(1))
调试器中的我发现SAFEARRAY
包含VT_I4
!
发生了什么事?
我注意到vararg
函数似乎只发生这种行为,而不是VARIANT*
参数的函数。
在回应ben的评论时,这是由PyTuple
调用的函数,它读取SAFEARRAY。在下面的行中放置断点,我可以看到pData[k]
包含VT_I4
。我再说一遍,“bug”只发生在使用默认属性和vararg函数的特定组合中。
PyObject* SafeArrayToTuple(SAFEARRAY* pSA)
{
if(pSA->cDims != 1)
throw Exception() << "Array is not one-dimensional while converting to Python tuple.";
int len = (int) pSA->rgsabound->cElements;
PyNewRef ret(PyTuple_New(len));
VARIANT* pData;
{
AutoSafeArrayAccessData ad(pSA, (void**) &pData);
for(int k=0; k<len; k++)
// breakpoint here
PyTuple_SET_ITEM(ret.ptr, k, VariantToPy(&pData[k]));
}
return ret.detach();
}
请注意,使用SafeArrayGetElement
代替SafeArrayAccessData
不会改变任何内容。