Ответ 1
Да, вы можете сделать это: это требует небольшого знания указателей и концепции "разыменования"...
Вот код для этого:
Public Function VariantTypeName (ByRef MyVariant) As String
'Возвращает расширенное имя типа переменной, указывающее
'будь то простой тип данных (например, Long Integer) или
"Вариант, содержащий данные этого типа, например:" Вариант/Длинные "
"iType теперь содержит VarType входящего параметра
'в сочетании с поразным флагом VT_BYREF, указывающим, что это
'было передано по ссылке. Другими словами, это указатель,
'не структура данных переменной (или ее копии)
(Объявления для функции API CopyMemory
- несколько абзацев ниже).
Это нуждается в некотором объяснении, потому что семейство языков Visual Basic предназначено для защиты вас от деталей реализации переменных и их типов - и от концепции указателей в частности - и мой код действительно связан с небольшим боковым мышлением.
Проще говоря, ваши переменные имеют имя - строку типа "intX", которую вы видите в своем коде; область памяти, выделенная для хранения фактических данных; и адрес для этой памяти.
Этот адрес будет фактически для начала памяти, выделенной для переменной, и переменная будет реализована как структура в памяти, определяемая смещениями, фактическим данным с размером (или длиной) данных - и, для сложных типов, путем смещения адресов к другим структурам в памяти. Эти размеры и смещения предопределены: они являются фактической реализацией переменной, и разработчикам VBA редко нужно знать об этом - мы объявляем тип, и все это сделано для нас.
Первое, что вам нужно знать сегодня, это то, что первые два байта в адресе переменной в VBA перечисляемый тип var: как работает функция VarType().
Когда программа передает этот адрес, вместо передачи скопированного распределения данных в памяти он передает этот адрес в качестве указателя . Да, я упрощаю некоторые из них, но разработчики VBA действительно знают разницу между получением указателя и копией данных: он в идентификаторах ByRef
и ByVal
мы используем для входящих параметров, когда объявляем функцию.
VBA и VB очень и очень хорошо защищают нас от деталей: настолько хорошо, что мы не можем использовать VarType
и TypeName
, чтобы обнаружить, что мы было передано значение или ссылка на него; или даже ссылку на ссылку, на ссылку.
Это важно, потому что вариант является оберткой для других переменных, и структура дает вам указатель на переменную, содержащуюся с типом var, чтобы описать ее: однако мы не можем знать, что в VBA - мы проходим прямо по линии, обозначенной адресом, вплоть до данных, которые мы собираемся использовать, а VBA VarType
никогда не говорит нам, что мы коснулись косвенно несколько переходов через последовательные адреса, определенные указателями.
Однако эта информация существует, если вы готовы смотреть на эти два байта за указателем, используя вызов API.
Как я уже сказал, эти два байта содержат тип var - но там больше: они содержат тип var в сочетании с побитовым маркером VT_BYREF
, указывающим, что это ссылка или указатель на сохраненный тип данных, а не сами данные. Таким образом, этот код будет достоверно рассказывать вам ваш тип var, с небольшим боковым мышлением, чтобы получить доступ к VBA, когда мы предпочли бы, чтобы это было не так:
Открытая функция DereferencedType (ByRef MyVar) как длинная
Dim iType As Integer
На первый взгляд эта функция кажется самонадеянной: я передаю переменную - вариант или простой тип - по ссылке, поэтому она всегда будет объединена с VT_BYREF
. И я все равно прокомментировал арифметику 'modulo'...
... И как это работает: передайте ему простую переменную, и он скажет вам, что вы передали переменную по ссылке:
Dim str1 As String
str1 = "Сто Сотня"
Debug.Print "String Variable:" и DereferencedType (str1)
... И вы получаете вывод vbString OR VT_BYREF
:
String Variable: 16392
Но если вы передадите нашу функцию в виде строки, реализация VBA варианта будет защищать вас от всей этой сложности в отношении указателей и передачи по ссылке - вплоть до данных - и даст вам ваши данные со всей ненужной информацией:
Dim varX As Variant
varX = "Сто Сотня"
Debug.Print "String Variant:" и DereferencedType (varX)
... И вы получите результат:
Вариант строки: 8
Я оставлю вас для кода OR или NOT для возвращаемых значений с помощью VT_BYREF
, чтобы дать вам метку "Variant/" для расширенных дескрипторов строк Variant/String и Варианты/Длинные выходы.
[Edit: сделал это в верхней части ответа, реализован как VariantTypeName
]
Я рекомендую вам объявить вызов API CopyMemory, как показано, с условными константами компилятора для всех сред, с которыми вы, вероятно, столкнетесь:
#If VBA7 And Win64 Then ' 64 bit Excel under 64-bit Windows 'Использовать LongLong и LongPtr
Private Declare Sub CopyMemory Lib "kernel32" Псевдоним "RtlMoveMemory" _ (Назначение как любое, _ Источник как любой, _ ByVal Length As Long)
#End If
Между тем, более сложный вопрос - получение варианта/объекта/диапазона - потребует дальнейшей работы. Я могу сказать вам, что ваш вариант содержит диапазон, и я могу сказать, что это вариант, а не сам диапазон: но я не могу спуститься по цепочке деклараций, чтобы показать, что объект был объявлен как "объект", теперь, когда он указывает на диапазон:
VarX устанавливается равным переменной объекта диапазона: varX: type = 8204 Range Dereferenced Type = 9 rng1: type = 8204 Range Dereferenced Type = 16393
Здесь код, который сгенерировал это, и полный вывод:
Открытый Sub TestVar()