简体   繁体   English

通过COM将自定义接口类型的SAFEARRAY返回到VB6

[英]Return SAFEARRAY of custom interface types to VB6 through COM

Is it possible to return an array of defined interface objects from a C++ COM function (VC6) to a VB6 client? 是否可以从C ++ COM函数(VC6)向VB6客户端返回已定义接口对象的数组? I've scoured the web and haven't been able to come across anything that describes what I need to do. 我已经在网上搜寻了内容,却没有发现任何描述我需要做的事情。 I've seen a lot of passing BSTR and VARIANT types, but I need some way to actually have the client side utilise the interface type that I return inside the array. 我已经看到了很多传递BSTR和VARIANT类型的方法,但是我需要一些方法来使客户端实际利用我在数组内部返回的接口类型。

What I assume I'll need to do 我认为我需要做的
- Use a SAFEARRAY -使用SAFEARRAY
- Use the SAFEARRAY with the VT_UNKNOWN type, which in turns means I need to place the objects into the array as IUnknown objects. -使用VT_UNKNOWN类型的SAFEARRAY,这又意味着我需要将这些对象作为IUnknown对象放入数组中。

From here on in I'm stumped. 从这里开始,我很困惑。 Is it possible to interpret an IUnknown type in VB6, and somehow turn it into the type that I require? 是否可以解释VB6中的IUnknown类型,并以某种方式将其转换为我需要的类型? Or am I going about this in the complete wrong way... 还是我以完全错误的方式来解决这个问题...

Clarification: 澄清:
The interfaces being placed in the collection are being used to mimic a struct. 放置在集合中的接口用于模仿结构。 I essentially need to pass back an array of structs. 我本质上需要传回结构数组。

I've come up with a solution that is suitable for my purposes, despite not being exactly what I set out in the question. 尽管提出的问题与我提出的目标不完全相同,但我已经提出了适合自己目的的解决方案。

My solution was to create a COM function that takes a SAFEARRAY as a parameter and modifies it, instead of returning a created array. 我的解决方案是创建一个以SAFEARRAY作为参数并对其进行修改的COM函数,而不是返回创建的数组。 The VB6 client instantiates the array, and passes it to C++ for populating. VB6客户端实例化该数组,并将其传递给C ++进行填充。 I envision that future usage will include a precursor function which VB6 calls to determine the required size of the array. 我设想将来的用法将包括VB6调用的前驱函数,以确定所需的阵列大小。 For reference, here's the code snippets: 作为参考,下面是代码片段:

Interface function: 接口功能:

[id(4), helpstring("method PopulateWithStruct")] HRESULT PopulateWithStruct([in,out]SAFEARRAY (IReturnStruct*)*ppArray, [out,retval] long*plResult);

Where IReturnStruct is an interface containing property values, acting as a struct: 其中IReturnStruct是包含属性值的接口,用作结构:

interface IReturnStruct : IDispatch
{
    [propget, id(1), helpstring("property num1")] HRESULT num1([out, retval] long *pVal);
    [propget, id(2), helpstring("property str1")] HRESULT str1([out, retval] BSTR *pVal);
};

And is implemented by ReturnStruct 并由ReturnStruct实现

[
    uuid(843870D0-E3B3-4123-82B4-74DE514C33C9),
    helpstring("ReturnStruct Class")
]
coclass ReturnStruct
{
    [default] interface IReturnStruct;
};

PopulateWithStruct has the following definition: PopulateWithStruct具有以下定义:

STDMETHODIMP CCTestInterface::PopulateWithStruct(SAFEARRAY **ppArray, long *plResult)
{
    long lLowerBound = -1;
    long lUpperBound = -1;
    SafeArrayGetLBound(*ppArray, 1, &lLowerBound);
    SafeArrayGetUBound(*ppArray, 1, &lUpperBound);

    long lArraySize = lUpperBound - lLowerBound;

    VARTYPE type;
    SafeArrayGetVartype(*ppArray, &type);

    if (lArraySize > 0)
    {
        for ( int i = lLowerBound; i < lUpperBound; ++i)
        {
            CComPtr<CReturnStruct> pRetStruct;
            HRESULT hr = CoCreateInstance(__uuidof(ReturnStruct), NULL, CLSCTX_ALL, __uuidof(IUnknown), reinterpret_cast<void **>(&pRetStruct));
            if (SUCCEEDED(hr))
            {
                pRetStruct->Initialise();
                hr = SafeArrayPutElement(*ppArray, (long*)&i, pRetStruct);
                if (FAILED(hr))
                {
                    return hr;
                }
                pRetStruct.Release();
            }
        }
        SafeArrayUnaccessData(*ppArray);
    }

    *plResult = 1;

    return S_OK;
}

On the VB side: 在VB端:

Dim obj As ATL_SERVICETESTLib.CTestInterface
Set obj = New CTestInterface

Dim Result As Long
Dim RetStructs(3) As ReturnStruct

Result = obj.PopulateWithStruct(RetStructs())

Any comments on this approach? 对这种方法有何评论?

VB will do a QueryInterface behind the scenes when you assign the IUnknown to a particular interface type, so that should Just Work. 当您将IUnknown分配给特定的接口类型时,VB将在后台执行QueryInterface,因此应该可以正常工作。

I don't know if you can pass an array of user-defined type to VB6, all the documentaiton I can find on the web stops at VS2003, but I would expect it would be possible. 我不知道是否可以将用户定义类型的数组传递给VB6,我可以在Web上找到的所有文档都停留在VS2003,但是我希望这是可能的。

You can wrap the thing in a variant and then it works. 您可以将其包装在一个变体中,然后起作用。

idl: IDL:

[propget, id(10), helpstring("blabla")]
HRESULT MyListProp([out, retval] VARIANT *ppsaList); 

cpp: cpp:

STDMETHODIMP CAnyClass::get_MyListProp(/*[out, retval]*/ VARIANT* ppsaList)
{
HRESULT hr = S_OK;

if (ppsaList== NULL)
{
    return E_INVALIDARG;
}

CComSafeArray <IDispatch*> saVars;

// I have my objects in a list m_List that I am copying to saVars
for (std::list<IMyObj*>::iterator it = m_List.begin();
     it != m_List.end();
     ++it)
{
    IDispatch* pUnk = NULL;
    if ((*it)->QueryInterface(IID_IDispatch, (void**)&pUnk) == S_OK)
    {
        saVars.Add(pUnk);
    }
}

CComVariant varReturn (saVars.Detach());
varReturn.Detach(ppsaList);
return S_OK;
} 

vb: vb:

Dim arr
arr = obj.MyListProp

' these will all work
ub = UBound(arr)
lb = LBound(arr)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM