Возврат SAFEARRAY пользовательских типов интерфейса к VB6 через COM - PullRequest
5 голосов
/ 12 апреля 2011

Можно ли вернуть массив определенных интерфейсных объектов из COM-функции C ++ (VC6) клиенту VB6? Я просмотрел сеть и не смог найти ничего, что описывает то, что мне нужно сделать. Я видел много передаваемых типов BSTR и VARIANT, но мне нужен какой-то способ, чтобы на стороне клиента использовался тип интерфейса, который я возвращаю внутри массива.

Что я предполагаю, что мне нужно сделать
- Используйте SAFEARRAY
- Используйте SAFEARRAY с типом VT_UNKNOWN, что в свою очередь означает, что мне нужно поместить объекты в массив как объекты IUnknown.

С этого момента я в тупике. Можно ли интерпретировать тип IUnknown в VB6 и каким-то образом превратить его в нужный мне тип? Или я поступаю совершенно неправильно ...

Пояснение:
Интерфейсы, размещаемые в коллекции, используются для имитации структуры. По сути, мне нужно передать обратно массив структур.

Ответы [ 3 ]

3 голосов
/ 14 апреля 2011

Я придумала решение, которое подходит для моих целей, несмотря на то, что оно не совсем то, что я изложил в этом вопросе.

Мое решение состояло в том, чтобы создать функцию COM, которая принимает SAFEARRAY в качестве параметра и изменяет его, а не возвращает созданный массив. Клиент VB6 создает массив и передает его в C ++ для заполнения. Я предполагаю, что в будущем будет использоваться функция-предшественник, которую VB6 вызывает для определения необходимого размера массива. Для справки вот фрагменты кода:

Функция интерфейса:

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

Где 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);
};

И реализуется ReturnStruct

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

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;
}

На стороне VB:

Dim obj As ATL_SERVICETESTLib.CTestInterface
Set obj = New CTestInterface

Dim Result As Long
Dim RetStructs(3) As ReturnStruct

Result = obj.PopulateWithStruct(RetStructs())

Есть какие-нибудь комментарии по этому подходу?

2 голосов
/ 12 апреля 2011

VB создаст QueryInterface за кулисами, когда вы назначите IUnknown для определенного типа интерфейса, поэтому он должен просто работать.

Я не знаю, можете ли вы передать массив пользовательского типа в VB6, вся документация, которую я могу найти в Интернете, останавливается на VS2003, но я ожидаю, что это будет возможно.

1 голос
/ 19 июня 2012

Вы можете обернуть вещь в вариант, и тогда это сработает.

IDL:

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

касты:

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;
} 

В.Б:

Dim arr
arr = obj.MyListProp

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