Это должно быть просто. Но я не нахожу решения. Может быть, потому что он такой старый (VB и COM)
Я пытаюсь вернуть небольшой массив целых чисел (или даже просто байтов, как я пробовал в этом примере для вопроса) COM-зарегистрированной внутрипроцессной DLL, написанной на C с использованием ATL, в приложение VB6.
Я действительно нашел много информации о том, чтобы пойти другим путем.
Насколько я понимаю, VARIANT - это решение, поскольку просто массив на основе C, переданный обратно в приложение VB, должен быть упорядочен и должен иметь известный размер.
У меня есть то преимущество, что я разрабатываю COM-объект, а также тестирую его в приложении VB. Хотя мой навык работы с VB близок к нулю.
Десятки других вызовов работают, как и ожидалось, отправляя данные и получая целые числа или BSTR из DLL COM и используя их в VB. Однако этот массив необработанных значений не работает.
Сначала я подумал, что неправильно кодирую VB, потому что очень мало знаю о VB. Затем я подумал, что это должен быть код C, который неправильно создает массив VARIANT. Теперь я не знаю, что думать.
Мои результаты заключаются в том, что вызов в VB возвращается, а в окнах VB Watch отображается VARIANT, но без содержимого (буквально пустое, не описываемое как «пустой» или «null» или что-то еще). (Почему VB настаивает на том, чтобы первая буква переменной была заглавной, можно только догадываться...)
Когда я пытаюсь получить доступ к индексу из VARINAT, происходит сбой с выходом индекса за пределы.
Я попробовал «ReDim» и получил больше ВАРИАНТОВ, все говорят «пусто». Но, по крайней мере, они говорят мне «пустой».
Таким образом, похоже, что ВАРИАНТ, возвращающийся из DLL, ничего не содержит.
Я попробовал несколько VT_ из заголовка OLE и либо получил пустой VARIANT, либо получил ошибку, что «тип автоматизации не поддерживается».
Код C++ на COM-сервере приведен ниже (очищен для публикации). Я пробовал типы VT_UI1, VT_UI4, VT_INT и некоторые другие:
STDMETHODIMP CClass::getArrayData(VARIANT* pVal, long *result) {
unsigned char arry[4];
unsigned long count;
unsigned char *pData;
arry[0] = 5; arry[1] = 6; arry[2] = 7; arry[3] = 8; // Just throw some data in
count = 4;
*result = count;
pVal->vt = VT_ARRAY |VT_UI1; // A VT of type unsigned bytes+array
pVal->parray = SafeArrayCreateVector(VT_UI1,0,count); // Create 4 elements
SafeArrayAccessData(pVal->parray, (void**) &pData); // Lock the memory for access
memcpy(pData, arry, count * sizeof(unsigned char)); // Copy the bytes in
SafeArrayUnaccessData(pVal->parray); // Release it
return S_OK; // And we're happy...
}
Запись IDL для этого
[id(13), helpstring("method getArrayData")] HRESULT getArrayData([out] VARIANT* pVal, [out, retval] long * result);
Код ВБ:
Private Sub getdata_Click()
Dim result As Integer
Dim data() As Variant
Dim value As Integer
Dim strvalue As String
result = mInterface.getArrayData(data)
value = data(1)
strvalue = CStr(value)
ListBox.AddItem (" " + strvalue)
End Sub
Приложение VB возвращает значение 4, поэтому я знаю, что многое работает. Но в массиве ничего нет.
У меня нет идей. Цель здесь состоит в том, чтобы вернуть очень небольшой массив значений в VB, чтобы приложение VB могло получить к ним доступ с помощью индексации: данные (0), данные (1), данные (2) ....
Я действительно не хочу писать дюжину или около того функций, чтобы возвращать значения по одному.
Любая помощь ТИА.
Скотти
Я бы немного нервничал из-за доступа к чему-либо на pVal
как существующему, когда у него есть out
в IDL. Это может быть или не быть безопасным.
И я бы держался подальше от любых типов элементов, которые не имеют родного типа VB. Я бы не стал доверять VB правильному поведению с чем-либо беззнаковым, и я не помню, существует ли целочисленный тип VB размером в байт. Я думаю, что подписанные I2 или I4 должны быть безопасными.
@Craig Спасибо за эти советы. Это было очищено для этого вопроса. Фактические данные представляют собой 32-битные целые числа, которые будут проверены конечным продуктом. VB был просто моим тестовым инструментом для проверки работы службы COM. И, надеюсь, чему-то научиться.
Вы были недалеки от решения. С вашим определением getArrayData
вы можете просто вызвать его из VB следующим образом:
Dim b As Variant
Dim result As Integer
result = mInterface.getArrayData(b)
Однако вы также можете определить его как свойство в .idl, например:
[id(2), propget] // note propget
HRESULT ArrayData([out, retval] VARIANT* pVal);
как это в C/C++ (вам не нужен результат, так как массив является самоописательным):
HRESULT STDMETHODCALLTYPE get_ArrayData(VARIANT* pVal) ...
и так в ВБ:
Dim a As Variant
a = mInterface.ArrayData
Вот часы VB для вариантов a и b:
PS: VB(6) устарел, а COM нет, он до сих пор везде используется в Windows :-)
Благодарить. Мне пришлось изучить ваш ответ поближе, вы должны были сказать: «Уберите скобки из объявления Variant». Это был вариант, который я не пробовал. Кроме того, спасибо за совет по возврату массива, я тоже надеялся сделать что-то подобное.
Я не думаю, что
data
должен быть массивомVariant
, я думаю, что это должен быть скалярVariant
(который затем содержит массив). Вы выполняете переключение на VB, изменяя тип элемента сVariant
на (что-то еще).