Об указателях в VB6

Хакер дает советы, раскрывает секреты и делится своими мыслями по поводу программирования.

Модератор: Хакер

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: Об указателях в VB6

Сообщение ger_kar » 12.03.2017 (Вс) 7:39

nouyana писал(а):PowerBasic поддерживает массивы пользовательских типов. Единственное, что он мне не дал сделать - это объявить внутри пользовательского типа строку переменной длины - можно только фиксированной.
Все правильно, в VB6 хоть и можно формально объявить в составе структуры строку переменной длины, по факту в любом случае в составе структуры будет только указатель на эту строку, в PowerBasic можно сделать точно так же, достаточно в составе структуры объявить соответствующий указатель.
Бороться и искать, найти и перепрятать

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Об указателях в VB6

Сообщение Хакер » 12.03.2017 (Вс) 10:53

nouyana писал(а):Мне казалось, что это логично и для функции VarPtr: зачем возвращать ссылку на ссылку, если можно вернуть ссылку на значение? Я и сейчас не понимаю, зачем сделали именно так. Наверное, когда разберусь со свойствами о которых ты писал в этой теме, мне станет всё понятно.

Нет, вряд ли разберёшься, потому что те свойства тут не причём.

Вообще, изнутри, VarPtr возвращает ровно то, что ей передали. Какое-то 32-битное число ей передать, ровно-то 32-битное число она возвратит.
Другое дело в том, что в зависимости от того, как она будет объявлена, можно заставить VB передать ей либо значение переменной, либо адрес переменной. Поэтому в самой VarPtr нет вообще никакой ни магии, ни логики (в значении «алгоритм»). Это пустка, тупо возвращающая переданный ей параметр.

nouyana писал(а):Я не знаю, как писать TLB-шки и, вероятно, не скоро с этим разберусь, а пока написал свойство At для обычного модуля. Может, кому пригодится:

Это малополезно и не так эффективно. Нужно использовать TLB: только там можно получить свойство с типом параметра As Any.

nouyana писал(а):Решил сделать что-то более полезное - написать свойство AtV, которое бы возвращало любую переменную (через тип Variant) по её указателю. Может, кто подскажет, почему не получилось?
Код: Выделить всё
Sub Main()
   Dim s1   As String
   Dim s2   As String
   Dim addr As Long
   
   s1 = "Hello, world!"
   addr = VarPtr(s1)
   s2 = AtV(addr)
   Debug.Print s2
End Sub

Public Property Get AtV(ByVal addr As Long) As Variant
   AtV = fnAtV(addr)
End Property
Private Function fnAtV(ByVal addr As Long, _
              Optional ByRef refVar As Variant) As Variant
   Dim ptrAddress As Long
   ptrAddress = VarPtr(addr) + 4
   PutMem4 ptrAddress, addr
   
   ' Дальше идёт ошибка 458: Переменная использует
   ' тип, не поддерживаемый в Visual Basic
   fnAtV = refVar
End Function

Предполагаю, это потому, что тип Variant тоже содержит ссылку. Получается, что ptrAddress - это уже ссылка на ссылку на ссылку. Есть предложения, как решить задачу?

Нет, не по этой причине, а потому что ты написал полную ерунду.

VARIANT — это 16-байтная структура с полями. Первое поле называется «vt», имеет размер 2 байта, и хранит тип содержащегося в VARIANT-е значения. Например VT_BOOL, VT_SHORT, VT_LONG, VT_BSTR, и т.п.

refVar содержит 32-битный указатель на 16-байтную структуру VARIANT.
Записав в refVar значение, являющееся адресом какой-то левой переменной, ты сделал полную глупость. VB думает, что по этому адресу найдёт 16-байтную структуру VARIANT и посмотрит на поле vt. А на далее там содержится указатель не на 16-байтную структуру VARIANT, а на строковую переменную. VB об этом не догадывается, читает первые 2 байта по адресу (думая, что это поле «vt»), и видит там значение, которое не соответствует ни одному из знакомых ему, точнее разрешённых допустимых значений поля vt. И умывает руки.

А по сути, случилось так, что младшее слово StrPtr(s1) совпало бы с каким-то VT_xxxx, то функция выполнила бы дальше ещё более бессмысленное действие, скорее всего закончившееся бы падением процесса.

Более того, и самое важное: ты взялся сделать вообще невозможную вещь. Невозможно в принципе написать такую функцию AtV. Ты передаёшь ей адрес 0x1000BC58, а она что должна вернуть? Байт по адресу 0x1000BC58? Integer по адресу 0x1000BC58? Long по адресу 0x1000BC58? Строку по адресу 0x1000BC58? Boolean по адресу 0x1000BC58? Откуда ей знать, что по этому адресу тебя интересует? Откуда ей знать, что там лежит, сколкьо байтов брать (1, 2, 4, 8, N) и как эти байты интерпретировать?

Ей просто неоткуда это знать.

nouyana писал(а):Экономистам (как я) довольно часто приходится работать с массивами пользовательских типов (таблицы, по сути). Я только что понял, что такой массив нельзя присвоить вариантной переменной, и к обозначенной выше задаче добавилась ещё одна.

Это не правда. Во-первых, здесь даже не важно, массив или не массив.

Если для пользовательского типа (UDT) существует типоописание в TLB, то запросто можно будет присвоить варианту и просто такую структуру, и массив таких структур.

nouyana писал(а): Но для этого мне нужно как-то передать ссылку на этот массив в DLL-ку. Хакер, можешь подсказать как получить такую ссылку?


Declare Sub Xxxx Lib "xxxxx" (ByRef arr() As MY_STRUCT)
или
Declare Sub Xxxx Lib "xxxxx" (ByRef FirstElementOfArray As MY_STRUCT, ByVal NumberOfElements As Long)
можно и в VARIANT обернуть при желании (надо будет описать MY_STRUCT в TLB), но смысла в этом почти ноль.

nouyana писал(а):Проблемы, как минимум, две:
1. Массив пользовательского типа нельзя присвоить вариантной переменной.

Можно (но в твоём случае не нужно).

Mikle писал(а):на сам массив можно получить указатель с помощью такой функции:
Код: Выделить всё
Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (Arr() As Any) As Long

Вот только функцию нужно называть не ArrPtr, а ArrVarPtr или VarPtrArr, ведь она возвращает не адрес самого массива, а адрес переменной, являющейся массивом.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

nouyana
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 104
Зарегистрирован: 29.01.2016 (Пт) 17:42

Re: Об указателях в VB6

Сообщение nouyana » 12.03.2017 (Вс) 15:19

nouyana писал(а):Осталось проверить, как это работает с DLL-кой из PowerBasic, но у меня уже 3:20 ночи, так что я пошёл спать, отпишусь в следующем посте.

Во общем ничего у меня с PowerBasic не получилось:
Код: Выделить всё
$COMPILE DLL

Declare Function PutMem4 Lib "msvbvm60" _
   (ByVal pDst As Long, ByVal NewValue As Long) As Long

FUNCTION subTest ALIAS "subTest" _
            (ByVal sAddr  As Long, _
    Optional ByRef refArr As Variant) EXPORT as long

   Dim ptrAddr As Long
   
   ptrAddr = VarPtr(sAddr) + 4  'Получаем адрес refArr
   PutMem4 ptrAddr, sAddr       'Пишем в него ссылку на MyVar
   refArr(1).lonVar = 5         'Заполняем массив MyVar
   refArr(1).strVar = "test"
End FUNCTION

При попытке заполнить массив (refArr(1).lonVar = 5) во время компиляции выдал ошибку типа данных: Error 516: DefType, Type id (?%&!#$), or AS... required: REFARR
Странно. VB6 никогда вопросов по типу Variant не задаёт - всё компилирует на ура. Получается, что передавать Variant в Dll - это плохой вариант :D . Надо объявлять UDT и в VB6 и в PB, а там проблема со строками переменной длины. Может, можно как-то заставить PB всё откомпилировать? Нафига мы объявляли UDT в публичном классе? Он же должен быть доступен из DLL? Тип Variant должен всю остальную работу сам делать, вроде. Может, в PB какие-нибудь параметры компиляции нужно указать, чтобы не умничал? :)

nouyana
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 104
Зарегистрирован: 29.01.2016 (Пт) 17:42

Re: Об указателях в VB6

Сообщение nouyana » 12.03.2017 (Вс) 15:28

Хакер писал(а):Более того, и самое важное: ты взялся сделать вообще невозможную вещь. Невозможно в принципе написать такую функцию AtV. Ты передаёшь ей адрес 0x1000BC58, а она что должна вернуть? Байт по адресу 0x1000BC58? Integer по адресу 0x1000BC58? ... Откуда ей знать, что там лежит, сколько байтов брать (1, 2, 4, 8, N) и как эти байты интерпретировать?

Ты, как всегда, прав. Поэтому AtV будет у меня принимать только ссылки на Variant и возвращать только вариантные переменные (по аналогии с subTest). Благо, я теперь научился запихивать в этот вариант UDT. Осталось научиться обмениваться им с DLL наиболее лёгким способом - и жизнь удалась :)

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Об указателях в VB6

Сообщение Хакер » 12.03.2017 (Вс) 15:46

nouyana писал(а):Поэтому AtV будет у меня принимать только ссылки на Variant и возвращать только вариантные переменные

И какой в этом смысл?

nouyana писал(а):Нафига мы объявляли UDT в публичном классе? Он же должен быть доступен из DLL?

Я не давал таких советов. UDT нужно объявить в TLB, TLB подключить к проекту и носить с собой (т.е. вместе с EXE). Никакие DLL и публичные классы в принципе не нужны, хотя и возможны в качестве альтернативного способа генерации TLB (в этом случае TLB генерируется самим VB и вшивается в DLL).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

nouyana
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 104
Зарегистрирован: 29.01.2016 (Пт) 17:42

Re: Об указателях в VB6

Сообщение nouyana » 12.03.2017 (Вс) 17:11

Хакер писал(а):И какой в этом смысл?

Такой же, как и у других свойств, которые ты предлагал:
Хакер писал(а):Объявляено три свойства: At, AtW, AtB — для типов Long, Integer и Byte. Названия даны по аналогии с AscW и AscB.

AtV - для типа Variant.
Хакер писал(а):UDT нужно объявить в TLB, TLB подключить к проекту и носить с собой (т.е. вместе с EXE).

Хакер, у каждого своя профессия. Если все посвятят свою жизнь электронике и программированию как ты, то, например, экономикой (как я) заниматься будет некому. Ты ведь уже знаешь, что писать TLB я не умею. Зачем тыкать в это пальцем? Если хочешь мне (и другим форумчанам) помочь - напиши что-нибудь содержательное в этой теме. Мы будем благодарны тебе и за небольшой пост и за большую статью. А со своей стороны, как человек гуманитарный, я советую тебе прочитать небольшой трактат Аристотеля "Риторика" или, хотя бы, вторую его главу.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Об указателях в VB6

Сообщение Хакер » 13.03.2017 (Пн) 1:07

nouyana писал(а):Такой же, как и у других свойств, которые ты предлагал:

Нет, не такой же. У тех свойств большой смысл: можно спроецировать в память какой-нибудь файл и разбирать (по сути — читать) его теми свойствами: в разных структурах зачастую поля имеют тип Byte/Integer/Long. Можно делать какие-то модификации каких-либо полей каких-либо структур в памяти.

Но что-то я представить себе не могу, чтобы мне хоть когда-то нужно было «узнать» (прочитать) Variant по заданному адресу. Пожалуй, ни разу такая задача не вставала. Другое дело — по указателю получить доступ к отдельным полям структуры VARIANT. Но тут на помощь приходят уже описанные мной свойства, то есть вновь того свойства, которое ты придума, не надо.

nouyana писал(а):AtV - для типа Variant.

Более того, ты наверное огорчишься, но я с полной гарантией могу тебе заявить, что невозможно используя GetMem4 реализовать такое свойство AtV, как ты описал/хочешь.

Конечно, если оставить тебя с такой информацией, ты просто ничего не сделаешь, и может быть так и следовало бы сделать (ибо ты всё равно идёшь не той дорогой), но для справедливости стоит всё-таки сказать, что помимо GetMem4 рантайм экспортирует GetMemVar и PutMemVar. Ну и конечно, можно банально воспользоваться соответствующим образом объявленной функцией VariantCopy (или VariantCopyInd — что предпочтительнее, если не хочешь выстрелить себе в ногу) уже не рантайма, из библиотеки OLEAUT32.DLL.

nouyana писал(а):Ты ведь уже знаешь, что писать TLB я не умею. Зачем тыкать в это пальцем?

Сегодня не умеешь, завтра умеешь. Не умеешь — научись. Если есть конкретные вопросы, задавай их в том топике. Или сходи почитай в Википедии статью о TLB, она целиком написана мной (не знаю, дописывал ли её кто-то другой с момента создания).
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

nouyana
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 104
Зарегистрирован: 29.01.2016 (Пт) 17:42

Re: Об указателях в VB6

Сообщение nouyana » 13.03.2017 (Пн) 8:46

Хакер писал(а):ибо ты всё равно идёшь не той дорогой

Ну, коли уж ты не в первый раз обращаешься к даосизму, процетирую Лао-цзы:
Лао-цзы писал(а):
  • Умный каждый день пополняет свои знания. Мудрый каждый день стирает лишнее.
  • Никогда не осуждайте человека, пока не пройдете долгий путь в его ботинках.
  • У хорошего путешественника нет точных планов и намерения попасть куда-то.
  • Если зрячий идет к пропасти, останавливающий его подобен слепцу.

За GetMemVar, PutMemVar и статью в Википедии - спасибо. Буду разбираться.

Пред.

Вернуться в МануAll

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

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

    TopList  
cron