1

Тема: VBS: Особенность доступа к элементам коллекций

Доброго здоровья, уважаемые профессионалы и любители.

Экспериментируя с объектами WScript.Shell и Scripting.FileSystemObject обнаружил, что элементы одних коллекций доступны как для конструкций For Each <var> In <collection>, так и по численному индексу, а  элементы других - только для For Each <var> In <collection>.

Пример:

Option Explicit
Dim FSO, WS, FSOFoldCollect, SpecFoldColl, SpecFold, StrResult, I

Set WS = CreateObject("WScript.Shell")
Set SpecFoldColl = WS.SpecialFolders

'=============== это работает =================
StrResult=vbNullString
FOR Each SpecFold in SpecFoldColl
    StrResult = StrResult & SpecFold & vbCrLf
Next
MsgBox StrResult
'==============================================


'=============== и это работает ===============
StrResult=vbNullString
FOR I=0 to SpecFoldColl.Count-1
    StrResult = StrResult & SpecFoldColl.Item(I) & vbCrLf
Next

MsgBox StrResult
'==============================================


Set FSO = CreateObject("Scripting.FileSystemObject")
Set FSOFoldCollect = FSO.GetFolder(".").SubFolders

'=============== это работает =================
StrResult=vbNullString
FOR Each SpecFold in FSOFoldCollect
    StrResult = StrResult & SpecFold.Path & vbCrLf
Next

MsgBox StrResult
'==============================================

'============= а это не работает ==============
StrResult=vbNullString
FOR I=0 to FSOFoldCollect.Count-1
    StrResult = StrResult & FSOFoldCollect.Item(I).Path & vbCrLf
Next

MsgBox StrResult
'==============================================

Подскажите, это действительно так, или я что-то сделал неправильно, а элементы любой коллекции можно адресовать и по индексу тоже?

2

Re: VBS: Особенность доступа к элементам коллекций

Михаил Орлов пишет:

Подскажите, это действительно так

Да. В данном случае путь возвращается не по индексу, а по имени каталога.

3 (изменено: Xameleon, 2017-01-25 22:34:20)

Re: VBS: Особенность доступа к элементам коллекций

Всё зависит от того как реализован интерфейс объекта и каким образом интерпретатор реализует выполнение перебора. Для работы перебора через For Each в VBScript, требуется свойство или метод с dispID = -4, которое(ый) возвращает интерфейс - IUnknown.

К примеру у Scripting.Dictionary и коллекций Files / Drives объекта Scripting.FileSystemObject он реализован и называется _NewEnum.

Вот пример обращения к нему


Option Explicit

Dim dict, item

Set dict = CreateObject("Scripting.Dictionary")

dict.Add "a",1

MsgBox TypeName(dict.[_NewEnum])

For Each item in dict
	MsgBox item
Next

Вот в этой строке MsgBox TypeName(dict.[_NewEnum]) происходит запрос к нему. Когда интерпретатор работает с _NewEnum он на каждой итерации получает следующий элемент коллекции.

А для перебора по индексам достаточно иметь 2 свойства или метода - одно(ин) для получения элемента по индексу или имени и второе для получения размера коллекции. В случае с Dictionary это Item(Key) и Count.



Option Explicit

Dim dict, item, Key

Set dict = CreateObject("Scripting.Dictionary")

dict(0) = "a"
dict(1) = "b"

Dim i
For i = 0 to dict.Count
	MsgBox dict(i)
Next

В коде написано dict(i), так как метод Item умолчательный (св-во / метод с dispID = 0) и поэтому обращение равноценно dict.Item(i)

В итоге получается, что реализация остаётся на совести разработчика, который создаёт интерфейс коллекции.
Dictionary - частный случай реализации максимума. Там есть и _NewEnum и Item и Count.
У коллекции Files тоже реализован _NewEnum и Item и Count , но перебор по индексам не работает, потому что метод Item принимает параметр не числовой, а имя файла.


Option Explicit
Dim fso, folder, file
Set fso = CreateObject("Scripting.FileSystemObject")

Set folder = fso.GetFolder("C:\")

MsgBox folder.files("bootmgr").size

Но в принципе мог бы быть отдельно только _NewEnum без Item и Count

Вот как-то так. ) Надеюсь ни где не переврал и внёс некую ясность.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

4 (изменено: Xameleon, 2017-01-30 01:56:54)

Re: VBS: Особенность доступа к элементам коллекций

Решил добавить в эту тему пример создания WSC (Windows Script Component) с поддержкой механизмов коллекции.
Реализовано:
1) Перебор через For Each Key in object
2) Перебор по индексам и именам object.Item(i) / object.Item("Key")
3) Умолчательное свойство Item - object("Key")
4) Поддержка Bang оператора (!). Доступ к элементам в формате object!key (Пример на VBA в комплекте)
5) События при вызове методов коллекции

Post's attachments

dictionaryWrapper.zip 11.26 kb, 11 downloads since 2017-01-29 

You don't have the permssions to download the attachments of this post.
Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

5 (изменено: Михаил Орлов, 2017-01-30 15:36:57)

Re: VBS: Особенность доступа к элементам коллекций

Xameleon
Спасибо, не ожидал настолько полный ответ.
Просто, после первого знакомства с коллекциями решил, что, раз коллекция - стандартный объект Visual Basic, то набор стандартных свойств и методов, описанных в документации по языку, должен присутствовать всегда и быть единообразным в любой коллекции. Оказалось, надо было просто читать дальше.

6

Re: VBS: Особенность доступа к элементам коллекций

Поскольку речь зашла о свойстве SpecialFolders, хочу поинтересоваться: каким образом можно получить значения всех ключей коллекции, возвращаемой этим свойством? На странице MSDN с описанием WshSpecialFolders Object перечислено 16 пунктов, а в книге Stein Borge, Automating Windows Administration - 17 (добавился AppData). Всего же их 18, что видно из вывода нижеследующего кода:

Set oWshShell = CreateObject("WScript.Shell")
Set cSpecialFolders = oWshShell.SpecialFolders

sResult = "#1" & vbCrLf & vbCrLf
For Each sSpecialFolder In cSpecialFolders
    sResult = sResult & sSpecialFolder & vbCrLf
Next

sResult = sResult & vbCrLf & "#2" & vbCrLf & vbCrLf
For i = 0 to cSpecialFolders.Count - 1
    sResult = sResult & i & " = " & cSpecialFolders(i) & vbCrLf
Next

aFolderNames = Array(_
	"AllUsersDesktop",_
	"AllUsersStartMenu",_
	"AllUsersPrograms",_
	"AllUsersStartup",_
	"Desktop",_
	"AppData",_
	"PrintHood",_
	"Templates",_
	"Fonts",_
	"NetHood",_
	"StartMenu",_
	"SendTo",_
	"Recent",_
	"Startup",_
	"Favorites",_
	"MyDocuments",_
	"Programs"_
)

sResult = sResult & vbCrLf & "#3" & vbCrLf & vbCrLf
For Each sFolderName In aFolderNames
    sResult = sResult & sFolderName & " = " & cSpecialFolders(sFolderName) & vbCrLf
Next

WScript.Echo sResult

У меня результат следующий:

#1

C:\Users\Public\Desktop
C:\ProgramData\Microsoft\Windows\Start Menu
C:\ProgramData\Microsoft\Windows\Start Menu\Programs
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
C:\Users\DELL\Desktop
C:\Users\DELL\AppData\Roaming
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Templates
C:\WINDOWS\Fonts
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Network Shortcuts
C:\Users\DELL\Desktop
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\SendTo
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Recent
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
C:\Users\DELL\Favorites
C:\Users\DELL\Documents
C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs

#2

0 = C:\Users\Public\Desktop
1 = C:\ProgramData\Microsoft\Windows\Start Menu
2 = C:\ProgramData\Microsoft\Windows\Start Menu\Programs
3 = C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
4 = C:\Users\DELL\Desktop
5 = C:\Users\DELL\AppData\Roaming
6 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
7 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Templates
8 = C:\WINDOWS\Fonts
9 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Network Shortcuts
10 = C:\Users\DELL\Desktop
11 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu
12 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\SendTo
13 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Recent
14 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
15 = C:\Users\DELL\Favorites
16 = C:\Users\DELL\Documents
17 = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs

#3

AllUsersDesktop = C:\Users\Public\Desktop
AllUsersStartMenu = C:\ProgramData\Microsoft\Windows\Start Menu
AllUsersPrograms = C:\ProgramData\Microsoft\Windows\Start Menu\Programs
AllUsersStartup = C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
Desktop = C:\Users\DELL\Desktop
AppData = C:\Users\DELL\AppData\Roaming
PrintHood = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
Templates = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Templates
Fonts = C:\WINDOWS\Fonts
NetHood = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Network Shortcuts
StartMenu = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu
SendTo = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\SendTo
Recent = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Recent
Startup = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
Favorites = C:\Users\DELL\Favorites
MyDocuments = C:\Users\DELL\Documents
Programs = C:\Users\DELL\AppData\Roaming\Microsoft\Windows\Start Menu\Programs

В блоках #1 и 2# перечислены все 18 элементов коллекции, перебором и по индексам, в блоке #3 - 17 элементов по найденным ключам, ключ для одного из элементов, который должен располагаться, по сути, между ключами NetHood и StartMenu (в блоке #2 он либо с индексом 4, либо 10) мне найти так и не удалось.

Щт Уккщк Куыгьу Туче

7

Re: VBS: Особенность доступа к элементам коллекций

omegastripes пишет:

в блоке #2 он либо с индексом 4, либо 10

Так он идёт 5-м в блоке #3. Зачем повторный?

8

Re: VBS: Особенность доступа к элементам коллекций

То что он повторный - это скорее совпадение. Но однозначно элементов в коллекции 18, а ключей известно 17. Каков еще один ключ?

Щт Уккщк Куыгьу Туче

9

Re: VBS: Особенность доступа к элементам коллекций

omegastripes
Не знаю и особо знать не хочу. Походит на "погоню попа за дешевизной".
Есть подозрение, что этот элемент просто объединён под одним именем.
Оверквотинг запрещён. Убрано.

10

Re: VBS: Особенность доступа к элементам коллекций

Flasher пишет:

Есть подозрение, что этот элемент просто объединён под одним именем.

Следуя данной логике, в коллекции 2 одинаковых ключа?

Щт Уккщк Куыгьу Туче

11

Re: VBS: Особенность доступа к элементам коллекций

Ключ может быть один, а коллекция может содержать объект со ссылкой на этот ключ, как, например, в случае %WinDir%, ведущей в %SystemRoot%.

12 (изменено: Xameleon, 2017-02-01 20:39:55)

Re: VBS: Особенность доступа к элементам коллекций

Сделал на скорую руку проверку соответствия при переборе по индексу с результатом перебора по именам. Видимо и правда просто дубль. Несовпавших элементов не обнаружил. Flasher правильно говорит. Можно было и код не писать.


Option Explicit
Dim oWshShell, _
	cSpecialFolders, _
	aFolderNames, _
	sFolderName, _
	i

Set oWshShell = CreateObject("WScript.Shell")

If InStr(1,WScript.FullName,"cscript",1) <= 0 Then 
	oWshShell.Run("cscript """ & WScript.ScriptFullName & """")
	WScript.Quit
End If

Set cSpecialFolders = oWshShell.SpecialFolders

aFolderNames = Array(_
	"AllUsersDesktop",_
	"AllUsersStartMenu",_
	"AllUsersPrograms",_
	"AllUsersStartup",_
	"Desktop",_
	"AppData",_
	"PrintHood",_
	"Templates",_
	"Fonts",_
	"NetHood",_
	"StartMenu",_
	"SendTo",_
	"Recent",_
	"Startup",_
	"Favorites",_
	"MyDocuments",_
	"Programs"_
)

For i=0 to cSpecialFolders.Count - 1
	For Each sFolderName in aFolderNames
		if cSpecialFolders(i) = cSpecialFolders(sFolderName) Then
			WScript.Echo( Right("0" & i,2) & ") [" & sFolderName & "] " & cSpecialFolders(i))
		End if
	Next
Next

With WScript
	.StdOut.WriteLine vbCrlf & "Для завершения работы скрипта нажмите ENTER"
	WScript.StdIn.ReadLine
End With

Вот что говорит:

0) [AllUsersDesktop] C:\Users\Public\Desktop
1) [AllUsersStartMenu] C:\ProgramData\Microsoft\Windows\Start Menu
2) [AllUsersPrograms] C:\ProgramData\Microsoft\Windows\Start Menu\Programs
3) [AllUsersStartup] C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
4) [Desktop] C:\Users\Developer\Desktop
5) [AppData] C:\Users\Developer\AppData\Roaming
6) [PrintHood] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
7) [Templates] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Templates
8) [Fonts] C:\Windows\Fonts
9) [NetHood] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Network Shortcuts
10) [Desktop] C:\Users\Developer\Desktop
11) [StartMenu] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Start Menu
12) [SendTo] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\SendTo
13) [Recent] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Recent
14) [Startup] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
15) [Favorites] C:\Users\Developer\Favorites
16) [MyDocuments] C:\Users\Developer\Documents
17) [Programs] C:\Users\Developer\AppData\Roaming\Microsoft\Windows\Start Menu\Programs

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

13

Re: VBS: Особенность доступа к элементам коллекций

Вроде не дубль:

Desktop (local)
powershell (new-object -com WScript.Shell).SpecialFolders.item(0)

Desktop (domain)  - All Users (4) - Specific User (10)
powershell (new-object -com WScript.Shell).SpecialFolders.item(4)
powershell (new-object -com WScript.Shell).SpecialFolders.item(10)

http://trilobite.ch/blog/powershell/24-joomla

14

Re: VBS: Особенность доступа к элементам коллекций

Это в данном описании. А по факту что мы видим? All Users (0), а не (4).

15

Re: VBS: Особенность доступа к элементам коллекций

Нет домена чтоб проверить.

16

Re: VBS: Особенность доступа к элементам коллекций

Чтобы убедиться в указанном несоответствии, домен не нужен.

17

Re: VBS: Особенность доступа к элементам коллекций

Как ни странно, если пролистать wshom.ocx в hex-редакторе, видны все те же 17 ключей:

00001400  A6 BB 56 06 50 00 72 00 6F 00 67 00 72 00 61 00  ª╗V.P.r.o.g.r.a.
00001410  6D 00 73 00 00 00 00 00 4D 00 79 00 44 00 6F 00  m.s.....M.y.D.o.
00001420  63 00 75 00 6D 00 65 00 6E 00 74 00 73 00 00 00  c.u.m.e.n.t.s...
00001430  46 00 61 00 76 00 6F 00 72 00 69 00 74 00 65 00  F.a.v.o.r.i.t.e.
00001440  73 00 00 00 53 00 74 00 61 00 72 00 74 00 75 00  s...S.t.a.r.t.u.
00001450  70 00 00 00 52 00 65 00 63 00 65 00 6E 00 74 00  p...R.e.c.e.n.t.
00001460  00 00 00 00 53 00 65 00 6E 00 64 00 54 00 6F 00  ....S.e.n.d.T.o.
00001470  00 00 00 00 53 00 74 00 61 00 72 00 74 00 4D 00  ....S.t.a.r.t.M.
00001480  65 00 6E 00 75 00 00 00 4E 00 65 00 74 00 48 00  e.n.u...N.e.t.H.
00001490  6F 00 6F 00 64 00 00 00 46 00 6F 00 6E 00 74 00  o.o.d...F.o.n.t.
000014A0  73 00 00 00 54 00 65 00 6D 00 70 00 6C 00 61 00  s...T.e.m.p.l.a.
000014B0  74 00 65 00 73 00 00 00 50 00 72 00 69 00 6E 00  t.e.s...P.r.i.n.
000014C0  74 00 48 00 6F 00 6F 00 64 00 00 00 41 00 70 00  t.H.o.o.d...A.p.
000014D0  70 00 44 00 61 00 74 00 61 00 00 00 44 00 65 00  p.D.a.t.a...D.e.
000014E0  73 00 6B 00 74 00 6F 00 70 00 00 00 41 00 6C 00  s.k.t.o.p...A.l.
000014F0  6C 00 55 00 73 00 65 00 72 00 73 00 53 00 74 00  l.U.s.e.r.s.S.t.
00001500  61 00 72 00 74 00 75 00 70 00 00 00 41 00 6C 00  a.r.t.u.p...A.l.
00001510  6C 00 55 00 73 00 65 00 72 00 73 00 50 00 72 00  l.U.s.e.r.s.P.r.
00001520  6F 00 67 00 72 00 61 00 6D 00 73 00 00 00 00 00  o.g.r.a.m.s.....
00001530  41 00 6C 00 6C 00 55 00 73 00 65 00 72 00 73 00  A.l.l.U.s.e.r.s.
00001540  53 00 74 00 61 00 72 00 74 00 4D 00 65 00 6E 00  S.t.a.r.t.M.e.n.
00001550  75 00 00 00 41 00 6C 00 6C 00 55 00 73 00 65 00  u...A.l.l.U.s.e.
00001560  72 00 73 00 44 00 65 00 73 00 6B 00 74 00 6F 00  r.s.D.e.s.k.t.o.
00001570  70 00 00 00 53 48 47 65 74 50 61 74 68 46 72 6F  p...SHGetPathFro
Щт Уккщк Куыгьу Туче