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, ведь она возвращает не адрес самого массива, а адрес
переменной, являющейся массивом.