Cложный вопрос-- передача структуры в вызов API-функции в VB

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Cложный вопрос-- передача структуры в вызов API-функции в VB

Сообщение Leon_ » 12.07.2004 (Пн) 10:13

Может и не сложный, но я не справляюсь :?
Имеется API-подобная библиотека, написанная на VC++, которая экспортирует кроме прочего функцию:
Код: Выделить всё
BOOL __stdcall GetTcpipBoundAdaptersInfo ( HANDLE hOpen, PTCP_AdapterList pAdapters )

где
Код: Выделить всё
struct _TCP_AdapterList
{
   unsigned long   m_nAdapterCount;
   unsigned char   m_szAdapterNameList[ ADAPTER_LIST_SIZE ][ ADAPTER_NAME_SIZE ];
   HANDLE         m_nAdapterHandle [ ADAPTER_LIST_SIZE ];
   unsigned int      m_nAdapterMediumList[ ADAPTER_LIST_SIZE ];
   unsigned char   m_czPermanentAddress[ ADAPTER_LIST_SIZE ][ ETHER_ADDR_LENGTH ];
   unsigned char   m_czCurrentAddress[ ADAPTER_LIST_SIZE ][ ETHER_ADDR_LENGTH ];
   unsigned short   m_usMTU [ ADAPTER_LIST_SIZE

} TCP_AdapterList, *PTCP_AdapterList;

Мне нужно вызвать эту функцию в VB. Что я делаю:
Код: Выделить всё
'Some size constants
Const ADAPTER_NAME_SIZE = 256
Const ADAPTER_LIST_SIZE = 32
Const ETHER_ADDR_LENGTH = 6

Public Declare Function GetTcpipBoundAdaptersInfo Lib "ndisapi.dll" (ByVal hOpen As Long, ByRef Adapters As TCP_AdapterList) As Boolean

Public Type TCP_AdapterList
    m_nAdapterCount  As Long                            'Number of adapters
    m_szAdapterNameList(ADAPTER_LIST_SIZE)  As String * ADAPTER_NAME_SIZE 'Array of adapter names
    m_nAdapterHandle(ADAPTER_LIST_SIZE)     As Long     'Array of adapter handles, this are key handles for any adapter relative operation
    m_nAdapterMediumList(ADAPTER_LIST_SIZE) As Integer  'List of adapter mediums
    m_czPermanentAddress(ADAPTER_LIST_SIZE) As String * ETHER_ADDR_LENGTH 'permanent (hardware) ethernet address
    m_czCurrentAddress(ADAPTER_LIST_SIZE)   As String * ETHER_ADDR_LENGTH 'current (configured) ethernet address
    m_usMTU(ADAPTER_LIST_SIZE)              As Integer  'current adapter MTU
End Type

В модуле формы:
Код: Выделить всё
Dim AdList       As TCP_AdapterList
         . . . . .
        GetTcpipBoundAdaptersInfo hFilt, AdList          'hFilt уже открыт

Вот такое длинное вступление. Так вот AdList.m_szAdapterNameList(i) и AdList.m_nAdapterCount заполняются значениями. Остальные члены AdList пусты. Код библиотеки получает мою структуру и верно заполняет ее (посмотрел в отладчике VC), видимо я неправильно передаю ее из VB? :?
Что я делаю не так? Как надо?

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 12.07.2004 (Пн) 11:39

Есть два момента.

1. C'шный "unsigned int" соответствует VB'шному "Long", а не "Integer". Это для мембера m_nAdapterMediumList

2. Размеры VB'шного массива начинаются с 0 (по крайней мере без установки Option Base 1) и потому нужно учитывать "лишний" по сравнению с С элемент. Т.е. вместо

m_szAdapterNameList(ADAPTER_LIST_SIZE) As String * ADAPTER_NAME_SIZE 'Array of adapter names

нужно

m_szAdapterNameList(ADAPTER_LIST_SIZE-1) As String * ADAPTER_NAME_SIZE 'Array of adapter names

или

m_szAdapterNameList(1 To ADAPTER_LIST_SIZE) As String * ADAPTER_NAME_SIZE 'Array of adapter names
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 12.07.2004 (Пн) 12:02

1. Ок, спасибо. (Правда, ничего не изменилось) Где можно подробнее узнать про соответсвие типов C и VB? Ссылки?

2. Спасибо. В моем примере (см.) размерность массива и в VC такая же (индексирование массива в VC тоже с 0-го элемента)

Помогите, please, задача не получается. :?

codemaster
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 604
Зарегистрирован: 13.02.2004 (Пт) 13:35

Сообщение codemaster » 12.07.2004 (Пн) 12:53


Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 12.07.2004 (Пн) 14:24

Leon_ писал(а):2. Спасибо. В моем примере (см.) размерность массива и в VC такая же (индексирование массива в VC тоже с 0-го элемента)


Количество элементов в массиве должно быть уменьшено на единицу по сравнению с С: ADAPTER_LIST_SIZE - 1 вместо прдыдущего ADAPTER_LIST_SIZE.

m_szAdapterNameList(ADAPTER_LIST_SIZE - 1) As String

Т.е. С'шный массив a[N] и индексация a[0],a[1],...,a[N-1], а в VB'шный массив a(N) и индексация a(0),a(1),...,a(N).

В С'шном массиве N - это количество элементов, а в VB'шном N - это индекс последнего элемента.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 12.07.2004 (Пн) 14:50

:roll: А. Ну да, верно. Но это дела не меняет.
В итоге, кроме AdList.m_szAdapterNameList(i) и AdList.m_nAdapterCount остальные члены AdList пусты. По крайней мере, m_szAdapterNameList и m_czPermanentAddress я одинаково объявляю в VB, библиотека их заполняет, а мне приходят значения только m_szAdapterNameList ... :cry:

codemaster
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 604
Зарегистрирован: 13.02.2004 (Пт) 13:35

Сообщение codemaster » 12.07.2004 (Пн) 15:37

Leon_ писал(а)::roll: А. Ну да, верно. Но это дела не меняет.
В итоге, кроме AdList.m_szAdapterNameList(i) и AdList.m_nAdapterCount остальные члены AdList пусты. По крайней мере, m_szAdapterNameList и m_czPermanentAddress я одинаково объявляю в VB, библиотека их заполняет, а мне приходят значения только m_szAdapterNameList ... :cry:


Если не сложно кинь кусок кода!

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 12.07.2004 (Пн) 16:05

Я в самом вверху уже подробно привел контекст ошибки, но подробно см. здесь:

Надеюсь, кто-нибудь разберется. Не оставьте в беде, други :)
Вложения
ListAdapters.zip
(3.21 Кб) Скачиваний: 44

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 12.07.2004 (Пн) 16:16

Код: Выделить всё
Public Type TCP_AdapterList
    m_nAdapterCount  As Long                            'Number of adapters
    m_szAdapterNameList(ADAPTER_LIST_SIZE - 1) As String * ADAPTER_NAME_SIZE 'Array of adapter names
    m_nAdapterHandle(ADAPTER_LIST_SIZE - 1)   As Long     'Array of adapter handles, this are key handles for any adapter relative operation
    m_nAdapterMediumList(ADAPTER_LIST_SIZE - 1) As Long 'List of adapter mediums
    m_czPermanentAddress(ADAPTER_LIST_SIZE - 1) As String * ETHER_ADDR_LENGTH 'permanent (hardware) ethernet address
    m_czCurrentAddress(ADAPTER_LIST_SIZE - 1) As String * ETHER_ADDR_LENGTH 'current (configured) ethernet address
    m_usMTU(ADAPTER_LIST_SIZE - 1)            As Integer  'current adapter MTU
End Type
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 12.07.2004 (Пн) 16:54

Изменил размеры массивов и m_nAdapterMediumList на Long. Не помогло..

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 13.07.2004 (Вт) 5:43

Leon_ писал(а):Изменил размеры массивов и m_nAdapterMediumList на Long. Не помогло..

Внесу и я свои пять копеек.
Короче, не должно быть никакой разницы какую именно структуру ты туда подаёшь. Единственное важно, чтобы эта структура была такого же размера в байтах. Можешь вообще (если точно знаешь размер исходной структуры байтах, а если не знаешь, то самое главное, чтобы НЕ МЕНЬШЕ, И ВСЁ!) послать туда следующее:
Код: Выделить всё
Public Type MyT
ByteValue(1 to BytesLengthOfStruct) as Byte
End Type

Разумеется тогда вместо
Код: Выделить всё
Public Declare Function GetTcpipBoundAdaptersInfo Lib "ndisapi.dll" (ByVal hOpen As Long, ByRef Adapters As TCP_AdapterList) As Boolean

нужно
Код: Выделить всё
Public Declare Function GetTcpipBoundAdaptersInfo Lib "ndisapi.dll" (ByVal hOpen As Long, ByVal Adapters As Long) As Boolean

Обращаться соответственно
Код: Выделить всё
Dim lMT as MyT
GetTcpipBoundAdaptersInfo hOpen, VarPtr(lMT)

И всё. Не работать не может! А уже затем заморачиваться тем, как вынуть нужные тебе значения из структуры MyT...
С уважением, Approximator.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 13.07.2004 (Вт) 6:15

Leon_ писал(а):Изменил размеры массивов и m_nAdapterMediumList на Long. Не помогло..


У меня все работает - как передается, так и возвращается.

Пиши, какие конкретно затруднения.

PS
Единственная трудность может быть связана с определением строк как String * N. Такая запись не предполагает наличия завершающего нуля. Поэтому с такими строками нужно немного более внимательно работать.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 13.07.2004 (Вт) 6:22

Vi писал(а):
Leon_ писал(а):Изменил размеры массивов и m_nAdapterMediumList на Long. Не помогло..


У меня все работает - как передается, так и возвращается.

А я предлагаю автору топика объявить структуру так, как это сделал ты, но передавать её не ByRef, а передавать туда указатель на неё VarPtr(). В этом случае точно всё сработает.
С уважением, Approximator.

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 13.07.2004 (Вт) 8:59

:cry: Спасибо, конечно, за помощь. Но все не то..
Ни один из предложенных способов, ни их разумные комбинации к успеху ни привели.
Структура передается и возвращается, но не все ее члены заполнены так, как это происходит внутри библиотеки. Видимо не зря m_szAdapterNameList имеет префикс sz ("zero" string), а m_czPermanentAddress и m_czCurrentAddress cz (char string). A я пытался их объявить подобно.
Самое интересное, что выболняя VB EXE-шник, я не получаю значений в m_nAdapterMediumList. Выполняя DEBUG библиотеки из VC++ (там есть такая возможность, надо лишь указать путь к вызывающему EXE), выводимый результат уже как надо.. Короче, что тут мутить, всем спасибо, тема закрыта.
Vi писал(а):У меня все работает - как передается, так и возвращается.
Как это может работать, когда я библиотеку не выкладывал, а? :wink:

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 13.07.2004 (Вт) 9:04

Кстати, по совету Approximator, передавал и массив байтов lMT. Потом через LSet присваивал результат своей структуре.
LSet AdList = lMT
После присваивания AdList содержит не больше полезных значений, чем при прямой его передаче в библиотеку. Действительно, массив lMTсодержит какие-то похожие на правду значения (проверить трудно -- поиск в байтовом массиве (8900)). Но как тогда с ним просто работать -- ума ни приложу. :?

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 13.07.2004 (Вт) 9:17

У меня есть работающий пример обращения к библиотке на VC++. Размер структуры там (sizeof(AdList)) такой же, как у меня в VB (Len(AdList)). Это к вопросу о возможной разнице размера передаваемой структуры AdList..

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 13.07.2004 (Вт) 10:26

Leon_ писал(а):Как это может работать, когда я библиотеку не выкладывал, а? :wink:


Ну, что мы все здесь без рук, что ли?

VB код:

Код: Выделить всё
Const ADAPTER_NAME_SIZE = 256
Const ADAPTER_LIST_SIZE = 32
Const ETHER_ADDR_LENGTH = 6

Public Type TCP_AdapterList
    m_nAdapterCount  As Long                            'Number of adapters
    m_szAdapterNameList(ADAPTER_LIST_SIZE - 1) As String * ADAPTER_NAME_SIZE 'Array of adapter names
    m_nAdapterHandle(ADAPTER_LIST_SIZE - 1)   As Long     'Array of adapter handles, this are key handles for any adapter relative operation
    m_nAdapterMediumList(ADAPTER_LIST_SIZE - 1) As Long 'List of adapter mediums
    m_czPermanentAddress(ADAPTER_LIST_SIZE - 1) As String * ETHER_ADDR_LENGTH 'permanent (hardware) ethernet address
    m_czCurrentAddress(ADAPTER_LIST_SIZE - 1) As String * ETHER_ADDR_LENGTH 'current (configured) ethernet address
    m_usMTU(ADAPTER_LIST_SIZE - 1)            As Integer  'current adapter MTU
End Type

Public Declare Function GetTcpipBoundAdaptersInfo Lib ".\moddll" _
      Alias "_GetTcpipBoundAdaptersInfo@8" (ByVal hOpen As Long, ByRef Adapters As TCP_AdapterList) As Boolean

Sub Main()
  Dim AdList       As TCP_AdapterList
       
  With AdList
    .m_nAdapterCount = 3
    .m_szAdapterNameList(0) = "xxx1" & vbNullChar
    .m_szAdapterNameList(1) = "xxx2" & vbNullChar
    .m_szAdapterNameList(ADAPTER_LIST_SIZE - 2) = "zzz1" & vbNullChar
    .m_szAdapterNameList(ADAPTER_LIST_SIZE - 1) = "zzz2" & vbNullChar
    .m_nAdapterHandle(0) = 20
    .m_nAdapterHandle(1) = 21
    .m_nAdapterHandle(ADAPTER_LIST_SIZE - 2) = 30
    .m_nAdapterHandle(ADAPTER_LIST_SIZE - 1) = 31
    .m_nAdapterMediumList(0) = 200
    .m_nAdapterMediumList(1) = 201
    .m_nAdapterMediumList(ADAPTER_LIST_SIZE - 2) = 300
    .m_nAdapterMediumList(ADAPTER_LIST_SIZE - 1) = 301
    .m_czPermanentAddress(0) = "ttt1" & vbNullChar
    .m_czPermanentAddress(1) = "ttt2" & vbNullChar
    .m_czPermanentAddress(ADAPTER_LIST_SIZE - 2) = "uuu1" & vbNullChar
    .m_czPermanentAddress(ADAPTER_LIST_SIZE - 1) = "uuu2" & vbNullChar
    .m_czCurrentAddress(0) = "ppp1" & vbNullChar
    .m_czCurrentAddress(1) = "ppp2" & vbNullChar
    .m_czCurrentAddress(ADAPTER_LIST_SIZE - 2) = "ooo1" & vbNullChar
    .m_czCurrentAddress(ADAPTER_LIST_SIZE - 1) = "ooo2" & vbNullChar
    .m_usMTU(0) = 40
    .m_usMTU(1) = 41
    .m_usMTU(ADAPTER_LIST_SIZE - 2) = 50
    .m_usMTU(ADAPTER_LIST_SIZE - 1) = 51
  End With
  GetTcpipBoundAdaptersInfo 0, AdList          'hFilt уже открыт
  Stop
End Sub


C++ код:
Код: Выделить всё
#define ADAPTER_NAME_SIZE 256
#define ADAPTER_LIST_SIZE 32
#define ETHER_ADDR_LENGTH 6

typedef struct _VTCP_AdapterList
{
   unsigned long   m_nAdapterCount;
   unsigned char   m_szAdapterNameList[ADAPTER_LIST_SIZE][ADAPTER_NAME_SIZE];
   long                  m_nAdapterHandle[ADAPTER_LIST_SIZE];
   unsigned int      m_nAdapterMediumList[ADAPTER_LIST_SIZE];
   unsigned char   m_czPermanentAddress[ADAPTER_LIST_SIZE][ETHER_ADDR_LENGTH];
   unsigned char   m_czCurrentAddress[ADAPTER_LIST_SIZE][ETHER_ADDR_LENGTH];
   unsigned short   m_usMTU[ADAPTER_LIST_SIZE];

} VTCP_AdapterList, *PVTCP_AdapterList;

extern "C" __declspec(dllexport) BOOL __stdcall GetTcpipBoundAdaptersInfo( long hOpen, PVTCP_AdapterList pAdapters )
{
   strncpy( &(pAdapters->m_czPermanentAddress[0][0]), "zxzxzxzx", ETHER_ADDR_LENGTH );
   strncpy( &(pAdapters->m_czPermanentAddress[1][0]), "xcxc1234", ETHER_ADDR_LENGTH );
   return 0;
}

Да и вообще, что ручками в GetTcpipBoundAdaptersInfo меняешь, то прекрасно передается обратно в VB.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 13.07.2004 (Вт) 10:48

Approximator писал(а):А я предлагаю автору топика объявить структуру так, как это сделал ты, но передавать её не ByRef, а передавать туда указатель на неё VarPtr(). В этом случае точно всё сработает.


Дело в том, что VarPtr(a) в общем случае не эквивалентен ByRef a. Почему?

Потому что передача структуры со строками во внешнюю DLL производится с преобразованием строк из UNICODE в ABSTR. Реально VB'шная структура БОЛЬШЕ чем таковая на C из-за 2-байтности строк.

Для VarPtr преобразование строк не производится никогда.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Approximator
Постоялец
Постоялец
 
Сообщения: 572
Зарегистрирован: 26.06.2004 (Сб) 3:10

Сообщение Approximator » 14.07.2004 (Ср) 2:09

Vi писал(а):
Approximator писал(а):А я предлагаю автору топика объявить структуру так, как это сделал ты, но передавать её не ByRef, а передавать туда указатель на неё VarPtr(). В этом случае точно всё сработает.


Дело в том, что VarPtr(a) в общем случае не эквивалентен ByRef a. Почему?

Потому что передача структуры со строками во внешнюю DLL производится с преобразованием строк из UNICODE в ABSTR. Реально VB'шная структура БОЛЬШЕ чем таковая на C из-за 2-байтности строк.

Для VarPtr преобразование строк не производится никогда.


Да, какая фиг разница. Главное, чтобы передаваемая структура была не меньше требуемой. А если ОЧЕНЬ нужно обратное преобразование можно будет сделать и в VB. Ладно, похоже, что автор топика не особо нуждается ни в твоих, ни в моих советах (так мне показалось по его реакции).
С уважением, Approximator.

Leon_
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 333
Зарегистрирован: 19.05.2004 (Ср) 16:31
Откуда: Moscow

Сообщение Leon_ » 14.07.2004 (Ср) 7:48

:D Нет, напротив, все сказанное в этом топике очень полезно для меня. Одно дело -- изучить Help и сформировать свою точку зрения (есть смешная и острая шутка про т.зрения), другое дело -- узнать что по этому поводу говорят коллеги.

По поводу своей задачи -- она так и не решена полностью. (Выполнение из VB неполно, но выполнение из Debug'а библиотеки --Ok) Но я получил наводку, что это наверняка из-за "выравнивания структур при компиляции под Win32", различные директивы /Zp препроцессора.. :? Видимо с этим :?: надо на сайт VCStreets :lol:
Может, кто подкинет ссылку на подходящий форум VC++?

codemaster
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 604
Зарегистрирован: 13.02.2004 (Пт) 13:35

Сообщение codemaster » 14.07.2004 (Ср) 11:24

посмотри MSDN
http://support.microsoft.com/default.as ... -US;171583
Elements of a UDT in Visual Basic use a 4-byte alignment. The default Visual C++ alignment is on 8-byte boundaries, so you have to explicitly set a smaller alignment

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 14.07.2004 (Ср) 15:04

Leon_ писал(а):По поводу своей задачи -- она так и не решена полностью. (Выполнение из VB неполно, но выполнение из Debug'а библиотеки --Ok) Но я получил наводку, что это наверняка из-за "выравнивания структур при компиляции под Win32", различные директивы /Zp препроцессора..

Про выравнивание тебе сказали, но использование DLLки в VB не зависит от ее версии (Debug она или Release). Поэтому налицо разные установки в проекте. Так что посмотри "Settings..." для своего проекта в VC и проверь на вкладке "Code Generation" выравнивание структур.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

maxV
Новичок
Новичок
 
Сообщения: 34
Зарегистрирован: 12.07.2004 (Пн) 18:26

Сообщение maxV » 16.07.2004 (Пт) 10:59

Если я правильно понял, нужно передать массив с пользовательским типом данных в API функцию. Если в C++ в API фунцию передается адрес описания массива, то в VB передается первый элемент массива. И все.
MSDN
Arrays

Like UDTs, arrays must be passed by reference, and for the same reasons. The big difference is that Windows knows the size of a UDT used by an API function, but it doesn’t know how many elements an array contains. In fact, that’s usually the reason you pass arrays to Windows—so that you can give varying numbers of elements. This means, however, that the API function needs to ask for the length in a separate parameter.


This requirement highlights another difference between C and Basic. Basic always knows how many elements an array contains (just as it knows the size of a string). C doesn’t, forcing the C programmer to keep track. Windows, in the C tradition, has the same requirement. This isn’t true of COM Automation, however. From the interface, you might almost think that COM Automation was written in Basic. It wasn’t, but Visual Basic types had a strong influence on the design. COM Automation supports a type called the safe array that just happens to have exactly the same format as a Basic array. Or perhaps it’s the other way around. In any case, arrays passed to COM Automation functions are more intuitive than those passed to Windows API functions.


For example, the Polygon API function (discussed in Chapter 7) passes an array of POINT variables (which we must call POINTL variables, as noted on page 66) along with the number of points in the array. Polygon connects the dots. The function looks like this on the C side:

BOOL Polygon( HDC hdc, // Handle of device context CONST POINT * lpPoints, // Address of polygon’s vertices int nCount // Count of polygon’s vertices);

The Basic UDT and declaration look like this:

Type POINTL x As Long y As LongEnd Type
Declare Function Polygon Lib "GDI32" (ByVal hdc As Long, _ lpPoints As POINTL, ByVal nCount As Long) As Long

Notice that the lpPoints parameter is passed by reference. You might think this is because POINTL is a user-defined type and UDTs are always passed by reference. Not so. The reason lpPoints is passed by reference is that in C an array is actually the address of the first element. If you pass a variable by reference, you’re actually passing its address, which is just what C (and Windows) thinks an array is. Another way to see this is to think of a variable as an array with one element. That’s why the Polygon declaration looks exactly as it would if the function took one by-reference POINTL variable instead of an array of them.


To pass an array, you pass the first element. Here’s an example:

Dim ptPoly(1 To 5) As POINTLFor i = 1 To 5 ‘ Calculate each point§NextCall Polygon(hdc, ptPoly(1), 5)

This gives you lots of rope—enough to hang yourself. For example, you don’t have to pass the start of the array or all the elements in it. The call

Call Polygon(hdc, ptPoly(2), 3)

passes the second through fourth elements of the array.


If you look at arrays in legal terms, the contract isn’t worth the polish on the shoes of the lawyer who wrote it. Windows wants an array, but Basic can’t be sure that’s what you’re giving. Windows wants the number of elements in the array, but Basic isn’t going to count them. It’s up to you to tell the truth. If you claim that the array contains 10 elements when it really has 5, Windows will happily use those last 5 elements whether they exist or not. Your machine might head south in a hurry.


When you look up the syntax for declarations, you’ll see that it’s possible to put empty parentheses on a parameter to indicate that you’re passing an array. Don’t try it with API functions. This feature is used for calling COM DLLs that know about Basic-style safe arrays with an encoded size. If COM had a GDI library, the ComPolygon function might look like this:

Declare Function ComPolygon Lib “COMGDI” (ByVal hdc As Long, _ lpPoints() As POINTL) As Boolean§Call ComPolygon(hdc, ptPoly)

You’ll get errors if you try to pass an entire array to a C-style procedure that expects the first element of an array, or if you try to pass the first element of an array to a COM-style function that expects the entire array.


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 20

    TopList