SAFEARRAY как средство возврата массива
От: AlexGin Беларусь  
Дата: 16.07.18 05:21
Оценка:
Доброе время суток, уважаемые коллеги!

Я сделал COM In-Process сервер (в виде DLL).
Эта штука на C++ делает некоторую вычислительную работу, а массив результатов должен вернуться в головное приложение.

Мне хотелось бы использовать SAFEARRAY — как средство возврата массива.

Для этого, метод (или свойство "get") должно иметь [out] параметр вида:

    [propget, id(7)] HRESULT Array1([out, retval] SAFEARRAY** pVal);


Вот рекомендации:
https://msdn.microsoft.com/en-us/magazine/mt795188.aspx

Метод заполнения массива должен выглядеть примерно так:
STDMETHODIMP CMyComComponent::DoSomething(/* [out] */ SAFEARRAY** ppsa) noexcept
{
  try
  {
    // Create a safe array storing 'count' BYTEs
    const LONG count = /* some count value */;
    CComSafeArray<BYTE> sa(count);
    // Fill the safe array with some data
    for (LONG i = 0; i < count; i++)
    {
      sa[i] = /* some value */;
    }
    // Return ("move") the safe array to the caller
    // as an output parameter
    *ppsa = sa.Detach();
  }
  catch (const CAtlException& e)
  {
    // Convert ATL exceptions to HRESULTs
    return e;
  }
  // All right
  return S_OK;
}



Проблема — в том, что MIDL компилятор всё время ругается, на попытку компиляции вышеуказанного свойства propget, id(7)] HRESULT Array1... :

AlexGinTest1.idl(25): error MIDL2139: type of the parameter cannot derive from void or void * : [ Type 'PVOID' ( Parameter 'pVal' ) ]
AlexGinTest1.idl(25): error MIDL2105: pointee / array does not derive any size : [ Field 'rgsabound' of Struct 'tagSAFEARRAY' ( Parameter 'pVal' ) ]


Просматривал ссылочки на материалы по применению SAFEARRAY:
https://blogs.msmvps.com/gdicanio/2011/02/04/simplifying-safearray-programming-with-ccomsafearray
https://www.codeproject.com/Articles/2452/Passing-C-Object-in-ATL-DLL-Server

Но понять, как решить данную проблему пока невозможно.

Какие есть мысли по этому поводу?

P.S. Попытки сделать предоставление массива клиентом, с заполнением на сервере (и дальнейшем использовании-освобождении на клиенте),
и методы доступа по типу:

    [id(7)] HRESULT SetSafeArray([in] SAFEARRAY(byte)** ptrArr);
    [id(8)] HRESULT GetSafeArray([out] SAFEARRAY(byte)** ptrOutArr);


Проходят через MIDL компилятор, но срезаются ошибками C++ компиляции:

Error C2259 'ATL::CComObject<T>': cannot instantiate abstract class AlexGinTest1 c:\program files (x86)\microsoft visual studio 14.0\vc\atlmfc\include\atlcom.h 2000

Отредактировано 16.07.2018 7:33 AlexGin . Предыдущая версия . Еще …
Отредактировано 16.07.2018 5:43 AlexGin . Предыдущая версия .
Отредактировано 16.07.2018 5:26 AlexGin . Предыдущая версия .
Отредактировано 16.07.2018 5:23 AlexGin . Предыдущая версия .
Re: SAFEARRAY как средство возврата массива
От: Alexander G Украина  
Дата: 16.07.18 06:30
Оценка: 4 (1)
Здравствуйте, AlexGin, Вы писали:

AG>Попытки сделать предоставление массива клиентом, с заполнением на сервере (и дальнейшем использовании-освобождении на клиенте), и методы доступа по типу:


В .odl/.idl SAFEARRAY уже подразумевает одну косвенность, при этом для Set её достаточно, для Get нужно две, чтобы выделять на стороне клиента

    [id(7)] HRESULT SetSafeArray([in] SAFEARRAY(byte) ptrArr);
    [id(8)] HRESULT GetSafeArray([out] SAFEARRAY(byte)* ptrOutArr);


При этом в С++:

STDMETHODIMP CMyComComponent::SetSafeArray(/* [in] */ SAFEARRAY* ppsa) {...} // не забирает владение
STDMETHODIMP CMyComComponent::GetSafeArray(/* [out] */ SAFEARRAY** ppsa) {...} // выделяет на стороне реализации
Re: SAFEARRAY как средство возврата массива
От: AlexGin Беларусь  
Дата: 16.07.18 15:51
Оценка:
Понял я в чем проблема!!!

1) Прежде всего, чтобы было проще править все коды, следует выбрать тип интерфейса: Custom (а не Dual)
— тогда интерфейс моего компонента будет наследником IUnknown (а не IDispatch), что позволит упростить все коды проекта.
В этом случае — наш компонент все равно будет в состоянии работать с сервером на базе .NET (C#) приложения.

2) Методы работы с SAFEARRAY в файле *.idl выглядят так:
...
    [] HRESULT SetSafeArray1([in] SAFEARRAY(byte) inputArray);
    [] HRESULT SetSafeArray2([in] SAFEARRAY(double) inputArray);
    [] HRESULT GetSafeArray1([out] SAFEARRAY(byte)* ptrArray);
    [] HRESULT GetSafeArray2([out] SAFEARRAY(double)* ptrArray);
...


C++ файлы:
В заголовочнике *.h:
class ATL_NO_VTABLE CAlexGinAGT2 :
    ...
{
    ...
   public:
    STDMETHOD(SetSafeArray1)(SAFEARRAY * inputArray);
    STDMETHOD(SetSafeArray2)(SAFEARRAY * inputArray);
    STDMETHOD(GetSafeArray1)(SAFEARRAY ** ptrArray);
    STDMETHOD(GetSafeArray2)(SAFEARRAY ** ptrArray);
    ...
};


B файле кодов *.cpp:
#include "atlsafe.h" // It is for CComSafeArray support
...
STDMETHODIMP CMyClass::SetSafeArray1(SAFEARRAY * inputArray)
{
    // TODO: Add your implementation code here
    // see:
    // https://stackoverflow.com/questions/12484109/how-to-iterate-through-safearray
    m_vectBytes.clear();
    SAFEARRAY* saValues = inputArray;
    byte* pVals;
    HRESULT hr = SafeArrayAccessData(saValues, (void**)&pVals); // direct access to SA memory
    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        SafeArrayGetLBound(saValues, 1, &lowerBound);
        SafeArrayGetUBound(saValues, 1, &upperBound);

        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            byte btVal = pVals[i];
            m_vectBytes.push_back(btVal);            
        }
        SafeArrayUnaccessData(saValues);
    }
    /* SafeArrayDestroy(saValues); */
    return S_OK;
}

STDMETHODIMP CMyClass::SetSafeArray2(SAFEARRAY * inputArray)
{
    // TODO: Add your implementation code here
    
    m_vectDoubles.clear();
    SAFEARRAY* saValues = inputArray;
    double* pVals;
    HRESULT hr = SafeArrayAccessData(saValues, (void**)&pVals); // direct access to SA memory
    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        SafeArrayGetLBound(saValues, 1, &lowerBound);
        SafeArrayGetUBound(saValues, 1, &upperBound);

        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            double dbVal = pVals[i];
            m_vectDoubles.push_back(dbVal);            
        }
        SafeArrayUnaccessData(saValues);
    }
    /* SafeArrayDestroy(saValues); */

    return S_OK;
}


STDMETHODIMP CMyClass::GetSafeArray1(SAFEARRAY ** ptrArray)
{
    // TODO: Add your implementation code here
    // see:
    // https://msdn.microsoft.com/en-us/magazine/mt795188.aspx
    try
    {
        // Create a safe array storing 'count' BYTEs
        const LONG count = m_vectBytes.size(); 
        CComSafeArray<BYTE> sa(count);
        // Fill the safe array with some data
        for (LONG i = 0; i < count; i++)
        {
            sa[i] = m_vectBytes[i];
        }
        // Return ("move") the safe array to the caller
        // as an output parameter
        *ptrArray = sa.Detach();
    }
    catch (const CAtlException& e)
    {
        // Convert ATL exceptions to HRESULTs
        return e;
    }
    return S_OK;
}


STDMETHODIMP CMyClass::GetSafeArray2(SAFEARRAY ** ptrArray)
{
    // TODO: Add your implementation code here
    try
    {
        // Create a safe array storing 'count' BYTEs
        const LONG count = m_vectBytes.size();
        CComSafeArray<DOUBLE> sa(count);
        // Fill the safe array with some data
        for (LONG i = 0; i < count; i++)
        {
            sa[i] = m_vectDoubles[i];
        }
        // Return ("move") the safe array to the caller
        // as an output parameter
        *ptrArray = sa.Detach();
    }
    catch (const CAtlException& e)
    {
        // Convert ATL exceptions to HRESULTs
        return e;
    }
    return S_OK;
}


ПРИМЕЧАНИЕ:
В среде мастера (визарда) вводим все методы работы с SAFEARRAY
(так как в комбо-боксе выбора типов данных в окне метода/свойства нет SAFEARRAY, то вводим вручную),
после чего потребуется также вручную подкорректировать указанные выше три файла.

На стороне .NET клиента, типу данных SAFEARRAY соответствует тип: System.Array.
Отредактировано 18.07.2018 19:13 AlexGin . Предыдущая версия . Еще …
Отредактировано 17.07.2018 7:07 AlexGin . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.