Возвращает SAFEARRAY пользовательских типов интерфейса в VB6 через COM

#c #com #vb6 #visual-c -6 #safearray

#c #com #vb6 #visual-c -6 #safearray

Вопрос:

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

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

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

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

Ответ №1:

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

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

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

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

Где IReturnStruct — это интерфейс, содержащий значения свойств, действующий как struct:

 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, amp;lLowerBound);
    SafeArrayGetUBound(*ppArray, 1, amp;lUpperBound);

    long lArraySize = lUpperBound - lLowerBound;

    VARTYPE type;
    SafeArrayGetVartype(*ppArray, amp;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 **>(amp;pRetStruct));
            if (SUCCEEDED(hr))
            {
                pRetStruct->Initialise();
                hr = SafeArrayPutElement(*ppArray, (long*)amp;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:

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

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

Ответ №3:

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

idl:

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

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**)amp;pUnk) == S_OK)
    {
        saVars.Add(pUnk);
    }
}

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

vb:

 Dim arr
arr = obj.MyListProp

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