Передача массивов строк из COM в C#

#c# #c #visual-c #com-interop #safearray

#c# #c #visual-c #com-взаимодействие #safearray

Вопрос:

Мне нужно получить доступ к методам C # в COM dll через COM-подобный интерфейс. Один из методов требует, чтобы массив строк передавался в качестве входных данных.

Я создаю SAFEARRAY и передаю его во взаимодействие COM. Однако, похоже, это не работает, поскольку я вижу исключение на уровне взаимодействия. (System.Runtime.Службы взаимодействия.Исключение SafeArrayTypeMismatchException).

Очевидно, что ожидается разница в типах.

Вставляем код сюда:

Вызываемый метод C #:

 public long DoIt3(int nFiles, string[] fileNames);
  

Код C , вызывающий тот же:

 int _tmain()
{
TCHAR *fileNames[128] = { TEXT("C:\Program Files\IBM\RTC.NET"),
                          TEXT("C:\KIRAN\Work\RFT"), TEXT(".\bin\Debug") };

SAFEARRAY *pSA = CreateSafeStringArray(3, fileNames);

_tprintf(TEXT("%d"), pIManaged->DoIt3(3, pSA));

SafeArrayDestroy(pSA);
}

static SAFEARRAY *CreateSafeStringArray(long nElements, TCHAR *elements[])
{
SAFEARRAYBOUND saBound[1];

saBound[0].cElements = nElements;
saBound[0].lLbound = 0;

SAFEARRAY *pSA = SafeArrayCreate(VT_VARIANT, 1, saBound);

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix  )
{
    VARIANT v;

    VariantInit(amp;v);

    v.vt = VT_BSTR;
    v.bstrVal = elements[ix];

    long rgIndicies[1];

    rgIndicies[0] = ix   saBound[0].lLbound;

    HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, amp;v);

    _tprintf(TEXT("%d"), hr);

    VariantClear(amp;v);
}

return pSA;
}
  

Любые идеи / предложения приветствуются.

Комментарии:

1. Как вы генерируете вызываемую во время выполнения оболочку для ComObject — кода c #?

2. Довольно неясно, как вы вызываете метод C #. Но вы создали объект[], а не строку[]. Передайте VT_BSTR в SafeArrayCreate() и соответствующим образом скорректируйте остальной код.

3. Не уверен насчет C , но чтобы заставить VBA-клиент вызывать C # CCW, единственный способ заставить его работать — объявить аргумент string array как ref : public long DoIt3(int nFiles, ref string[] fileNames);

4. @Joe Это было потому, что клиенты VBA всегда передают параметры варианта как VT_BYREF

Ответ №1:

Я понял это! Работает следующий код:

 static SAFEARRAY *CreateSafeStringArray(long nElements, TCHAR *elements[])
{
SAFEARRAYBOUND saBound[1];

saBound[0].cElements = nElements;
saBound[0].lLbound = 0;

SAFEARRAY *pSA = SafeArrayCreate(VT_BSTR, 1, saBound);

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix  )
{
    BSTR pData = SysAllocString(elements[ix]);

    long rgIndicies[1];

    rgIndicies[0] = saBound[0].lLbound   ix;

    HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, pData);

    _tprintf(TEXT("%d"), hr);
}

return pSA;
}
  

Спасибо за все ваши предложения!

Ответ №2:

В случае массива строк BSTR вы можете установить значения BSTR непосредственно в своем массиве, а также вам нужно выделить память для ваших элементов BSTR, для этого вы могли бы использовать ATL / MFC CString:

 ...
psa = SafeArrayCreate( VT_BSTR, 1, saBound);
HRESULT hr = SafeArrayLock( psa );
//TODO: test for hr success

if (pSA == NULL)
{
    return NULL;
}

for (int ix = 0; ix < nElements; ix  )
{
    long rgIndicies[1];
    rgIndicies[0] = ix   saBound[0].lLbound;
    CString tempstr(elements[ix]);

    ((BSTR*)psa->pvData)[ix] = tempstr.AllocSysString();
    _tprintf(TEXT("%d"), hr);
}

hr = SafeArrayUnlock( psa );
//TODO: test for hr success
...
  

Комментарии:

1. Спасибо, yms! Попробую. Но, похоже, этот подход достигается путем манипулирования внутренними полями. Существует ли стандартный способ сделать это — возможно, аргументы, которые я передаю SafeArrayPutElement(), неверны.