#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(), неверны.