Передача байтового массива в структуре к объекту com - PullRequest
0 голосов
/ 04 марта 2019

Я написал C ++ COM-сервер (вне процесса) и клиент так:

idl (интерфейс IDispatch):

typedef[uuid(0952A366-20CC-4342-B590-2D8920D61613)]
    struct MyStruct{
    LONG                id;
    BYTE*               data;
    } MyStruct;

[helpstring("")] HRESULT foo([out] MyStruct* pStreamInfo);

сервер:

STDMETHODIMP foo(MyStruct* myStruct)
{
  myStruct.id = 7;
  myStruct.data = pData; // pData is a pointer to some data of variable length
  return S_OK;
}

client:

MyStruct ms;
hr = comObj->foo(&ms);

Код будет работать нормально, кроме случаев добавления строки myStruct.data = pData;, которая приводит к сбою сервера.Назначение памяти в клиенте, например, ms.data = new BYTE[1000], не помогает, так как указатель все еще достигает foo как NULL.

Какое решение будет, 1. предпочтительно наиболее простым для стороны клиента, так как интерфейс будетбудет использоваться различными пользователями 2. Будет ли другое решение, если интерфейс используется клиентом C # 3. Если data должен быть вне структуры (я надеюсь, что нет), есть ли ссылка на полный пример.

1 Ответ

0 голосов
/ 05 марта 2019

Как уже упоминали другие в комментариях, вы не можете передать необработанный массив таким образом.Как минимум, вы должны скопировать байтовый массив в SAFEARRAY байтов (SAFEARRAY(BYTE) в IDL).Я только что проверил приведенный ниже код с помощью собственного прокси / заглушки (скомпилированного из кода P / S, сгенерированного midl.exe), и я смог получить данные по сети.

Если вы хотите использоватьстандартный P / S, такой как PSDispatch ({00020420-0000-0000-C000-000000000046}) или PSOAInterface ({00020424-0000-0000-C000-000000000046}), или, если вы хотите использовать VBA в качестве клиента, вам, возможно, придется преобразовать это в SAFEARRAY(VARIANT) и /или поместите получившийся защитный луч в VARIANT.Попробуйте самый простой подход - сначала просто используйте SAFEARRAY(BYTE), потому что это тот, который требует наименьших затрат.(A SAFEARRAY(VARIANT) использует в 16 раз больше памяти, чем SAFEARRAY(BYTE), потому что VARIANT имеет длину 16 байтов. И вам придется вручную преобразовывать каждый байт в VARIANT, в отличие от простых вызовов memcpyпокажите ниже.)

Упаковка байтового массива в SAFEARRAY: (Обратите внимание, что это копирует байтовый массив в SAFEARRAY. Вы можете разбираться с внутренними элементами SAFEARRAY struct для предотвращения копирования, но вы будете делать что-то нестандартным образом.)

/// <summary>Packs an array of bytes into a SAFEARRAY.</summary>
/// <param name="count">The number of bytes.</param>
/// <param name="pData">A reference to the byte array. Not read if count is 0.</param>
/// <param name="pResult">Receives the packed LPSAFEARRAY on success.</param>
HRESULT PackBytes(ULONG count, const BYTE* pData, /*[ref]*/ LPSAFEARRAY* pResult)
{
    // initialize output parameters
    *pResult = NULL;

    // describe the boundaries of the safearray (1 dimension of the specified length, starting at standard index 1)
    SAFEARRAYBOUND bound{ count, 1 };

    // create the safearray
    LPSAFEARRAY safearray = SafeArrayCreate(VT_UI1, 1, &bound);
    if (!safearray)
        return E_OUTOFMEMORY;

    // when there is actually data...
    if (count > 0)
    {
        // begin accessing the safearray data
        BYTE* safearrayData;
        HRESULT hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
        if (FAILED(hr))
        {
            SafeArrayDestroy(safearray);
            return hr;
        }

        // copy the data into the safearray
        memcpy(safearrayData, pData, count);

        // finish accessing the safearray data
        hr = SafeArrayUnaccessData(safearray);
        if (FAILED(hr))
        {
            SafeArrayDestroy(safearray);
            return hr;
        }
    }

    // set output parameters
    *pResult = safearray;

    // success
    return S_OK;
}

Распаковка байтового массива из SAFEARRAY: (Примечаниечто это копирует байтовый массив из SAFEARRAY. Вы можете поиграться с внутренностями структуры SAFEARRAY, чтобы предотвратить копирование, но вы будете делать что-то нестандартным образом. Также вы можете выбратьиспользуйте данные непосредственно из SAFEARRAY, указав код потребления между SafeArrayAccessData и SafeArrayUnaccessData.)

/// <summary>Unpacks an array of bytes from a SAFEARRAY.</summary>
/// <param name="safearray">The source SAFEARRAY.</param>
/// <param name="pCount">A pointer to a ULONG that will receive the number of bytes.</param>
/// <param name="ppData">A pointer to a BYTE* that will receive a reference to the new byte array.
/// This array must be deallocated (delete []) when the caller is done with it.</param>
HRESULT UnpackBytes(LPSAFEARRAY safearray, /*[out]*/ ULONG* pCount, /*[out]*/ BYTE** ppData)
{
    // initialize output parameters
    *pCount = 0;
    *ppData = NULL;

    // validate the safearray element type (must be VT_UI1)
    VARTYPE vartype;
    HRESULT hr = SafeArrayGetVartype(safearray, &vartype);
    if (FAILED(hr))
        return hr;
    if (vartype != VT_UI1)
        return E_INVALIDARG;

    // validate the number of dimensions (must be 1)
    UINT dim = SafeArrayGetDim(safearray);
    if (dim != 1)
        return E_INVALIDARG;

    // get the lower bound of dimension 1
    LONG lBound;
    hr = SafeArrayGetLBound(safearray, 1, &lBound);
    if (FAILED(hr))
        return hr;

    // get the upper bound of dimension 1
    LONG uBound;
    hr = SafeArrayGetUBound(safearray, 1, &uBound);
    if (FAILED(hr))
        return hr;

    // if the upper bound is less than the lower bound, it's an empty array
    if (uBound < lBound)
        return S_OK;

    // calculate the count of the bytes
    ULONG count = uBound - lBound + 1;

    // create buffer
    BYTE* pData = new BYTE[count];
    if (!pData)
        return E_OUTOFMEMORY;

    // begin accessing the safearray data
    BYTE* safearrayData;
    hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
    if (FAILED(hr))
    {
        delete[] pData;
        return hr;
    }

    // copy the data
    memcpy(pData, safearrayData, count);

    // finish accessing the safearray data
    hr = SafeArrayUnaccessData(safearray);
    if (FAILED(hr))
    {
        delete[] pData;
        return hr;
    }

    // set output parameters
    *pCount = count;
    *ppData = pData;

    // success
    return S_OK;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...