... а я таки завершил дело.
Теперь это умеет ковырять массивы, регэкспы, классы (с помощью js-кода), Dictionary.
'Возвращает строку рекурсивного описания содержимого объекта oBj. Полная версия
private function vardump (byref oBj, byval sParentName, byval nEsting)
const nEsting_max = 20 'Максимальный уровень вложенности
const nPaddingSpace = 2 'Структурный отступ (на него сдвигаем потомков отн. родителей), число пробелов
const sUnknownValue = "(?)"
if VarType(sParentName) <> 8 then 'Антиглюк: Если не строка
on error resume next
sParentName = CStr(sParentName)
if Err.Number then 'Антиглюк: При неудачном преобразовании сбрасываем
sParentName = ""
end if
on error goto 0
end if
if not IsNumeric(nEsting) then 'Проверка nEsting
nEsting = nEsting_max
end if
dim sNoSpace4First, sRes
if sParentName = "" then 'У безымянного объекта не будет пробела перед типом
sNoSpace4First = ""
else
sNoSpace4First = " "
end if
if nEsting >= 0 then
dim sObj, i, nArrDims, nElCount, oDi, oJRes, oEl
nArrDims = -1
oJRes = false 'Является ли object составным по мнению JScript
set oDi = CreateObject("Scripting.Dictionary")
if IsObject(oBj) then 'Показать сущность
sObj = "(object)"
if VarType(oBj) = 8 then
dim sTmp
' on error resume next
sTmp = "'" & CStr(oBj) & "'"
if not Err then
sObj = sObj & sTmp
end if
' on error goto 0
end if
dim oSC, sJsCode
set oSC = CreateObject("ScriptControl")
oSC.Language = "JScript"
sJsCode = "function GetObjectProps(oBj, oA){ var sRes=false; if (typeof(oBj)=='object') for (var oEl in oBj) " & _
"{try {oA.Add(oEl, oBj[oEl]);} catch(e) {oA.Add(oEl, '" & sUnknownValue & "'); sRes=true}} return sRes;}"
oSC.AddCode(sJsCode)
oJRes = oSC.Run("GetObjectProps", oBj, oDi)
elseif isArray(oBj) then 'Делаем массиву описание вида "array[2x2x3=12]"
nArrDims = nGetArrayDims(oBj)
sObj = "array["
nElCount = 1
if nArrDims > 0 then
for i=1 to nArrDims
nElCount = nElCount * (UBound(oBj, i) + 1)
dim sDelim
if nArrDims = 1 then
sDelim = ""
elseif i = nArrDims then
sDelim = "="
else
sDelim = ","
end if
sObj = sObj & UBound(oBj, i) & sDelim
next
if nArrDims > 1 then
sObj = sObj & nElCount
end if
end if
sObj = sObj & "]"
elseif IsNull(oBj) then
sObj = "Null" 'CStr() не может обработать
else 'Остальных просто в строку
on error resume next
sObj = CStr(oBj)
if Err.Number then
sObj = sUnknownValue & " " & Err.Description
end if
on error goto 0
end if 'Показать сущность
dim sTab : sTab = Space(nPaddingSpace * (nEsting_max - nEsting))
sRes = sTab & sParentName & sNoSpace4First &"{" & TypeName(oBj) & "#" & VarType(oBJ) &"}=>" & sObj & "<" & VbCrLf
'Показ содержимого сущности, если оно имеется
if IsObject(oBj) then
if TypeName(oBj) = "Dictionary" then
for each oEl in oBj
dim sEl
if IsNumeric(oEl) then
if VarType(oEl) = 8 then '8 это String (да, бывают одновременно и IsNumeric, и String)
sEl = "'" & oEl & "'"
else
sEl = oEl
end if
else
sEl = """" & oEl & """"
end if
sRes = sRes & vardump(oBj(oEl), sParentName & "[" & sEl & "]", nEsting-1)
next
elseif TypeName(oBj) = "IRegExp2" then
sRes = sRes & sTab & Space(nPaddingSpace) & "Pattern=>" & oBj.Pattern & "< Global=" & Abs(oBj.Global) & _
" IgnoreCase=" & Abs(oBj.IgnoreCase) & " Multiline=" & Abs(oBj.Multiline) & VbCrLf
elseif oJRes then
for each oEl in oDi
sRes = sRes & vardump(oDi(oEl), sParentName & "." & oEl , nEsting-1)
next
else
on error resume next 'Пытаемся обработать объект как коллекцию
dim sResTmp : sResTmp = ""
for each oEl in oBj
if not IsEmpty(oEl) then
sResTmp = sResTmp & vardump(oEl, sParentName & "*", nEsting-1)
end if
if Err then
exit for
end if
next
if not Err then sRes = sRes & sResTmp
' sRes = sRes & vardump(oBj.Name, sParentName & ".Name", nEsting-1) 'Угадай свойство :)
on error goto 0
end if
elseif isArray(oBj) and nArrDims > 0 then 'Если массив с измерениями, выводим его элементы (рекурсией)
for i=1 to nArrDims 'Делаем словарь счетчиков измерений
oDi.Add i, 0
next
for each oEl in oBj 'Вывод элементов массива
if not IsEmpty(oEl) then 'Вывод только непустых значений
dim sObjName : sObjName = sParentName & "[" 'Составляем имя элемента "родитель[x,y,z]"
for i=1 to nArrDims
sObjName = sObjName & oDi.Item(i)
if i <> nArrDims then
sObjName = sObjName & ","
end if
next
sObjName = sObjName & "]"
sRes = sRes & vardump(oEl, sObjName, nEsting-1)
end if
oDi.Item(1) = oDi.Item(1) + 1
if nArrDims > 1 then 'Если "разрядов" более 1, делаем составной счетчик
for i=1 to nArrDims-1
if oDi.Item(i) > UBound(oBj, i) then
oDi.Item(i) = 0
oDi.Item(i+1) = oDi.Item(i+1) + 1
end if
next
end if 'составной счетчик
next 'Вывод элементов массива
end if 'Если массив
else
sRes = sParentName & " ! Nesting limit !" & VbCrLf
end if
vardump = sRes
end function
' Упрощенная версия: объект, его название
sub vard2 (byref oBj, byval sParentName)
on error resume next
WScript.Echo(vardump(oBj, sParentName, 20))
if Err then
WScript.Echo "vardump() Error " & Err.Number & " " & Err.Description
end if
end sub
' Упрощенная версия: только объект
sub vard(byref oBj)
call vard2(oBj, "")
end sub
'Возвращает число измерений массива
function nGetArrayDims(arr)
if not isArray(arr) then
nGetArrayDims = -1 'Не массив - индикатор ошибки
exit function
end if
dim nElCount, i, nCurElCount
nElCount = 0
for each i in arr 'Всего элементов в массиве
nElCount = nElCount + 1
next
if nElCount = 0 then
nGetArrayDims = 0 'Динамический, не определен
exit function
end if
nGetArrayDims = 1
nCurElCount = UBound(arr) + 1
while nElCount > nCurElCount
nGetArrayDims = nGetArrayDims + 1
nCurElCount = 1
for i = 1 to nGetArrayDims
nCurElCount = nCurElCount * (UBound(arr, i) + 1)
next
wend
end function
Использование:
vard переменная
vard2 переменная, "описание переменной, например ее имя"
Отдельно прилагается объект для тестирования.
'Объект для демонстрации возможностей
class MyObj
dim p1
public p2
private p3
property let prop1(value)
p1 = value
end property
property get prop1
prop1 = p1
end property
function a
a = true
end function
end class
dim obj
set obj = new MyObj
obj.p1 = "123"
dim oD : set oD = CreateObject("Scripting.Dictionary")
oD.Add "number1", 1
oD.Add "2", "22"
oD.Add 2.3, obj
oD.Add "Nothing", Nothing
dim oRE : set oRE = CreateObject("VBScript.RegExp")
oRE.IgnoreCase = true
oRE.Pattern = "\S+"
oRE.Global = true
oD.Add true, oRE
dim arr0()
oD.Add "пустой массив", arr0
dim arr1(2)
arr1(1) = 0.1
arr1(2) = CLng(1)
oD.Add "одномерный", arr1
dim arr2 : arr2 = Array(1, arr1, "4")
oD.Add "одномерный Array()", arr2
dim arr3(2,2,2)
arr3(1,1,1) = Null
arr3(1,2,1) = Now
oD.Add "многомерный", arr3
dim oFS : set oFS = CreateObject("Scripting.FileSystemObject")
dim oGF : set oGF= oFS.GetFile(WSH.ScriptName)
oD.Add "FileSystemObject", oFS
oD.Add "FileSystemObject Drives", oFS.Drives
oD.Add "SomeFile", oGF
oD.Add "WScript", WSH
oD.Add "Err", Err
dim oSC : set oSC = CreateObject("ScriptControl")
oSC.Language = "JScript"
oSC.AddCode("function inner(){return 22;}")
oSC.AddCode("function a2() {}")
oD.Add "ScriptControl.Procedures", oSC.Procedures
vard2 oD, "Составной объект"
' Выведет
' Составной объект {Dictionary#9}=>(object)<
' Составной объект["number1"] {Integer#2}=>1<
' Составной объект['2'] {String#8}=>22<
' Составной объект[2,3] {MyObj#9}=>(object)<
' Составной объект[2,3].prop1 {String#8}=>123<
' Составной объект[2,3].a {String#8}=>(?)<
' Составной объект[2,3].p1 {String#8}=>123<
' Составной объект[2,3].p2 {Empty#0}=><
' Составной объект["Nothing"] {Nothing#9}=>(object)<
' Составной объект[Истина] {IRegExp2#9}=>(object)<
' Pattern=>\S+< Global=1 IgnoreCase=1 Multiline=0
' Составной объект["пустой массив"] {Variant()#8204}=>array[]<
' Составной объект["одномерный"] {Variant()#8204}=>array[2]<
' Составной объект["одномерный"][1] {Double#5}=>0,1<
' Составной объект["одномерный"][2] {Long#3}=>1<
' Составной объект["одномерный Array()"] {Variant()#8204}=>array[2]<
' Составной объект["одномерный Array()"][0] {Integer#2}=>1<
' Составной объект["одномерный Array()"][1] {Variant()#8204}=>array[2]<
' Составной объект["одномерный Array()"][1][1] {Double#5}=>0,1<
' Составной объект["одномерный Array()"][1][2] {Long#3}=>1<
' Составной объект["одномерный Array()"][2] {String#8}=>4<
' Составной объект["многомерный"] {Variant()#8204}=>array[2,2,2=27]<
' Составной объект["многомерный"][1,1,1] {Null#1}=>Null<
' Составной объект["многомерный"][1,2,1] {Date#7}=>02.12.2010 18:33:34<
' Составной объект["FileSystemObject"] {FileSystemObject#9}=>(object)<
' Составной объект["FileSystemObject Drives"] {Drives#9}=>(object)<
' Составной объект["FileSystemObject Drives"]* {Drive#8}=>(object)'C:'<
' Составной объект["FileSystemObject Drives"]* {Drive#8}=>(object)'D:'<
' Составной объект["SomeFile"] {File#8}=>(object)'D:\00\Scripts\_base\test.vbs'<
' Составной объект["WScript"] {Object#8}=>(object)'Сервер сценариев Windows'<
' Составной объект["Err"] {Object#3}=>(object)<
' Составной объект["ScriptControl.Procedures"] {IScriptProcedureCollection#9}=>(object)<
' Составной объект["ScriptControl.Procedures"]* {IScriptProcedure#8}=>(object)'inner'<
' Составной объект["ScriptControl.Procedures"]* {IScriptProcedure#8}=>(object)'a2'<
Замечания
Наличие в {скобках} значения как TypeName(), так и VarType(), думаю, будет не лишним: можно наблюдать, как некоторые объекты (FileSystemObject.File, WSH и т.п.) возвращают в VarType() типы своих свойств по умолчанию, как правило - строк, а не 9 объекта.
Неочевидная особенность, впрочем упомянутая в документации.
Для запуска JScript вместо htmlfile используется ScriptControl. Функциональность та же, переменных и кода меньше. Плюс при ошибках во "внешнем" коде жалуется в исходный, а не молча падает как в htmlfile, что существенно облегчает отладку. А если посмотреть OLE/COM браузером в соответствующую библиотеку типов "Microsoft Script Control 1.0"... то можно открывать отдельную тему, посвященную изучению функциональности данного объекта: обработка ошибок, перечисление процедур/функций в коде, требуемых ими параметров, ограничение времени исполнения, несколько методов выполнения (Run, Eval, ExecuteStatement).
В связи с тем, что TypeName() возвращает конкретные названия "подтипов" можно и далее расширять функциональность, добавляя свойства известных объектов и коллекций (File, Files, Drive и др.) Я же остановился на классических множествах.