- Код: Выделить всё
Dim w As Object, v
Set w = New Class1
v = w.a(12)(34)
v = w.b(12)(34)
Что означает запись "(34)"? Если вызванный метод вернёт объект, то это будет вызов его свойства по умолчанию; а если он вернёт массив, то это будет взятие элемента массива.
- Код: Выделить всё
' Содержимое Class1
Option Explicit
Public Function a(x)
Set a = Me
End Function
Public Function b(x)
b = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
End Function
' Метод по умолчанию
Public Function c(x)
c = x * 2
End Function
Приведённый вначале код присвоит в v сначала с(34)=68, а затем 34-ый элемент массива внутри b (пятёрку).
Ясно, что поскольку связывание позднее, компилятор не знает заранее, будет "(34)" применено к массиву или объекту. Код в обоих случаях генерируется одинаково универсальный.
То же верно и для нескольких индексов в правых скобках (просто лень было писать тестовый пример).
Как это делается? и как нам сделать то же самое?
Предположим, у нас есть вариант (с неизвестным содержимым) и набор индексов для него (в массиве вариантов). Раз число индексов неизвестно заранее, значит готовой магией мы воспользоваться не можем. Как реализовать аналогичную функциональность?
Готовая магия компилируется в вызов функции __vbaVarIndexLoad; но увы -- это cdecl-функция с переменным числом аргументов, так что вызывать её через Declare не удастся. Единственный выход -- писать переходник.
- Код: Выделить всё
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Sub Call_VarIndexLoad Lib "user32" Alias "CallWindowProcA" (lpProc As Any, Result As Variant, Source As Variant, ByVal cIndices As Long, ByVal pIndices As Long)
Public Function VarIndexLoad(Source As Variant, Indices() As Variant) As Variant
Dim code(14) As Long
code(0) = &H56EC8B55
code(1) = &H10758B57
code(2) = &HE0D1C68B
code(3) = &HC88BE0D1
code(4) = &HE0D1E0D1
code(5) = &HFC8BE02B
code(6) = &H14758B56
code(7) = &HF08BA5F3
code(8) = &HFF0C75FF
code(9) = &H15FF0875
code(10) = VarPtr(code(14))
code(11) = &H30CC483
code(12) = &HC95E5FE6
code(13) = &H10C2
code(14) = GetProcAddress(GetModuleHandle("msvbvm60.dll"), "__vbaVarIndexLoad")
Call_VarIndexLoad code(0), VarIndexLoad, Source, UBound(Indices) - LBound(Indices) + 1, VarPtr(Indices(LBound(Indices)))
End Function
' использование:
Dim Indices() As Variant
Indices = Array(34)
v = VarIndexLoad(w.a(12), Indices)
v = VarIndexLoad(w.b(12), Indices)
Готово! у нас есть Универсальный Индексер, принимающий переменное число индексов. Даже если мы знаем, что Source -- это объект, нет никакого стандартного способа вызвать его свойство по умолчанию с переменным числом аргументов. Собственно, для этого ограниченного случая я свой код и писал; уже потом я сообразил, что он вышел универсальнее, чем мне нужно было.
Ну, и вопрос на засыпку: а как вызвать метод объекта по имени, если число его аргументов неизвестно заранее? В лоб, через CallByName, очевидно, не удастся. Завтра, если никто не догадается сам, запощу отгадку