Don't know, as I can get any string literal ("C:\z\") to work but cannot get (variable) to work... and I also tried a byte array both unicode and ansi...
Wohohoho, Muhahaha!!! Okay, finally got it to work via the use of a variable but there is one caveat I just noticed of which I will get into a moment, but first the code that works...
Code:
Option Explicit
Dim ShellObject As Object
Private Sub Form_Load()
Set ShellObject = CreateObject("shell.application")
ScanDir "C:\a\"
End Sub
Public Sub ScanDir(DirToScan As String)
Dim FolderCollection As Object
Dim SingleFolder As Object
Set FolderCollection = ShellObject.namespace(Trim(DirToScan)) 'go figure???
For Each SingleFolder In FolderCollection.items
If SingleFolder.isfolder Then
'ScanDir SingleFolder.Name 'uncomment for recursive search
Debug.Print "Found Folder: " & SingleFolder.Name
Else
Debug.Print "Found File: " & SingleFolder.Name
End If
Next
End Sub
Okay, the result of this are...
Found File: acmsetup.exe
Found File: acmsetup.hlp
Found File: adc.h
Found File: adoctint.h
Found File: adoid.h
Found File: adoint.h
Found File: adojet.h
Found File: adojet.idl
Found File: adomd.h
Found File: adomd.idl
Found File: anchorwi.gif
Found File: br.gif
Found File: clientsc.gif
Found File: cmdtree.h
Found Folder: comct332.cab
Found Folder: comctl32.cab
Found Folder: ComDlg32.CAB
Found File: comments.gif
Found File: dispex.dll
Found File: divbgn.gif
Found File: divend.gif
Found File: eula.txt
Found File: formbgn.gif
Found File: formend.gif
Found File: icrsint.h
Found File: intro.dll
Found File: jetoledb.h
Found File: jetoledb.idl
Found File: jetoledb.lib
Found File: jscript.dll
Found Folder: Mci32.cab
Found File: msado15.h
Found File: msado15.idl
Found Folder: MSAdoDc.CAB
Found Folder: MSBind.CAB
Found Folder: mscdrun.cab
Found Folder: MSChrt20.CAB
Found Folder: MSComCt2.CAB
Found Folder: MSComCtl.CAB
Found File: msdadc.h
Found File: msdaguid.h
Found File: msdaora.h
Found File: msdaosp.h
Found File: msdasc.h
Found File: msdasc.lib
Found File: msdasql.h
Found Folder: MSDatGrd.CAB
Found Folder: MSDatLst.CAB
Found File: msdatsrc.h
Found File: msdatsrc.tlb
Found Folder: msdbrptr.cab
Found Folder: MSDERUN.CAB
Found File: msdshape.h
Found Folder: MSFlxGrd.CAB
Found Folder: MSHFlxGd.CAB
Found Folder: msinet.cab
Found File: msjetodb.h
Found Folder: MSMask32.CAB
Found Folder: Msrdc20.cab
Found Folder: msrdo20.cab
Found File: msremote.h
Found File: msscript.ocx
Found File: msse.dll
Found File: mssetup.dll
Found Folder: msstdfmt.cab
Found Folder: Msvbvm60.cab
Found File: msvbvm60.dbg
Found File: msvcep.dll
Found Folder: MSWcRun.CAB
Found Folder: mswinsck.cab
Found File: mswless.dep
Found File: mswless.ocx
Found File: ocdb.h
Found File: odbc32.lib
Found File: odbccp32.lib
Found File: odbcinst.h
Found Folder: Oleaut.cab
Found File: oleaut32.dbg
Found File: oledb.h
Found File: oledb.lib
Found File: oledbdep.h
Found File: oledberr.h
Found File: osptk.lib
Found File: pbgn.gif
Found File: pend.gif
Found File: persist.h
Found Folder: PicClp32.CAB
Found File: readme.htm
Found File: redist.txt
Found Folder: RichTx32.CAB
Found File: selfreg.dll
Found File: setup.ini
Found File: setup.tdf
Found File: setupsp6.exe
Found File: setupsp6.lst
Found File: simpdata.h
Found File: simpdata.tlb
Found File: sp698ent.dll
Found File: sp698ent.inf
Found File: sp698ent.stf
Found File: spanbgn.gif
Found File: spanend.gif
Found File: sql.h
Found File: sqlext.h
Found File: sqloledb.h
Found File: sqltypes.h
Found File: sqlucode.h
Found File: style.gif
Found Folder: TabCtl32.CAB
Found File: toc.htm
Found File: unknownt.gif
Found Folder: vbrun60.cab
Found File: vbscript.dll
Found File: vcredist.exe
Found Folder: VS6sp61.cab
Found Folder: VS6sp62.cab
Found Folder: VS6sp63.cab
Found Folder: VS6sp64.cab
Do you see the caveat I am talking about in the above list??? (It is reporting cab files as folders and this really depends upon your point of view...)
Different folder...
Found File: Borland_Delphi_8.rar
Found Folder: Delphi_7.zip
...as you could consider cab's, zip's, rar's, and so on as folders because they hold other files like a folder but call me a traditionilst, I consider them to be files... (reason rar is reported as a file is because I don't have a rar file reader on this computer... err so I should say I don't have a commercial rar file reader but I do have a vb6 project that will extract rar's and/or convert them into zip's (that is still under very slow construction...))
So finally, (since we have highjacked this thread), I believe we have the problem of late binding this portion of the shell object solved...
This Trim() of a variable to make it work is really strange...
I agree that a zip or cab is a file rather than a folder.
The clou is to get more information about an item before proceeding:
The namespace() method returns a Folder or Folder3 object.
To get more information, the .self property returns its FolderItem interface.
The FolderItem provides a .Type property and allows to use the .GetDetails() method of the folder object like this.
Watch that the Folder object contains a collection of FolderItem objects (not of Folder objects)
Assume we get a Folder object from the Namespace method.
Code:
Dim fo As Folder3, i%
Set fo = SHL.NameSpace("C:\test\test.zip") 'or other zip file you have
Debug.Print TypeName(fo), fo.Self.IsBrowsable 'shows that fo is Folder3 object and that it is browsable
Debug.Print fo.Self.Type 'type of the FolderItem of fo
For i = 0 To 9
Debug.Print "Detail" & i & ": "; fo.GetDetailsOf(fo.Self, i)
Next
Try the same with a normal folder and a normal file. The results of the GetDetailsOf() method are different for each type of object (file, folder, zip file). I have not found detailed information, but on folders and files you will find the filesize, the creation and modification dates and more.
What really worries me is: the Type string and the Details info you get is literally in the local language of your operating system.
Yes I agree that the need to use the Trim Function to get it to work is strange, very strange indeed.
As for retrieving that information based upon local info, well that would/could make things easier/harder for one if they are making a international program. Harder because if you needed to know the return value you would have to include it in your resource file for the country for comparisons and build your logic around the resource file reference. Easier if you only needed it for display.
What worries me is what would happen to the speed increase we are seeing using this code as we would now need to do some, as you called it, heavy error checking (ref my dir search code) to test for each of these types and what actions we would need to do based upon those types...
I think it is time I stop trying to late bind these different things and go to early binding as you have so I can explore these objects (scribble, scribble, scribble, RrrriiiiPP, then the sound of paper fluttering as it spins to land on the round tuit pile that always seems to grow and never shrink...).
Hehehe.
Yes, I'd recommend the early binding, too. It's much more fun to get intellisense give you all the choice you need. It is also interesting to examine the objects in the object browser (F2).
The FolderItem interface and the Folder3 object are the relevant objects.
I will try to make this little expansion to my folder scanner benchmark:
To stop the loop from scanning archives I will examine the .Type property, or compare name extension against a set of ".zip.cab.rar..." to treat them like a file.
Just one more note on the late binding I have been playing with. I found, by accident, another thread doing some of what we are doing in this thread and in their call to the potential recursive function, they use As Variant. So no need for Trim() on the .Namespace(variablename) 'go figure???
Also WoF, I know you will be interested in this..., the properties they use (GetDetailsOf) go from -1 to 34 where -1 equals the tooltop in windows explorer when you hover with the mouse. Comments are always 14 on XP with a disclaimer that it may be different with other OS's and a comment that the only items that are always available for a folder are -1 to 4.
So, one would wonder that there should be a way to do a for each on this... (scribble...flutter...d a m n!!!)
This IS interesting. I know from experiments that the #3 is modification date and propert #4 is the modification date.
Strange enough all literal answers like Type are given in the localized language. My german system says "ZIP komprimierter Ordner" for a zip file, but the dates are given in an english/american format.
It would be interesting if there is a non-localized way of determining the item type...
What is that with the Variant type? Where do I have to put the As Variant declaration?
Just one more note on the late binding I have been playing with. I found, by accident, another thread doing some of what we are doing in this thread and in their call to the potential recursive function, they use As Variant. So no need for Trim() on the .Namespace(variablename) 'go figure???
Apparently the function wants a variant or a pointer as a parameter. If memory serves a variant datatype is in effect a pointer to the memory location of the given data.
Trim() returns a variant where-as Trim$() returns a string This would explain the reason it worked with Trim() but not with a string.
Using the last code example I gave, it would be changed to...
Code:
Option Explicit
Dim ShellObject As Object
Private Sub Form_Load()
Set ShellObject = CreateObject("shell.application")
ScanDir "C:\a\"
End Sub
Public Sub ScanDir(DirToScan As Variant)
Dim FolderCollection As Object
Dim SingleFolder As Object
Set FolderCollection = ShellObject.namespace(DirToScan)
For Each SingleFolder In FolderCollection.items
If SingleFolder.isfolder Then
'ScanDir SingleFolder.Name 'uncomment for recursive search
Debug.Print "Found Folder: " & SingleFolder.Name
Else
Debug.Print "Found File: " & SingleFolder.Name
End If
Next
End Sub
and datamiser, (hits self on head), well duh, that makes sense now that you remind me of it... and come to think of it, being that this portion was designed for scripting and in scripting you cannot type variables...
I have made a little application which helps explore the shell automation and makes use of the shell32.FolderItem and shell32.Folder objects.
A TreeView is filled starting from the Desktop, like an explorer tree would be, although I put in all the non-folders (files/items), too.
Clicking on a node in the tree will display infos in the left TextBox.
It will display all interesting info including those obtained by GetDetailsOf() method of the (parent) folder. A Detail is only displayed, if the returned string is not "".
It appears that some objects like a local drive may return up to 40 DetailsOf.
It is a bit bewildering what comes back from this method.
If the object is a diskdrive, Detail #0 will be literally "Name", Detail #1 will be literally "Size" whereas when you go into the drive and click a file, #0 will be the real name of the file and #1 will be its real size.
I'm still a little confused about using the shell objects, because of the localized DetailOf strings, but it seems they are a rather good alternative to the FSO.
Look how the tree is able to browse all Desktop Namespaces, including Nethood, Control Panel, etc. You cannot do this with FSO.
If you look at the code, a few simple changes can make the tree only display folders like a real explorer tree, so as you could implement a ListView to display the files in that folder.
Well it is just a start-off to play a little with shell32 stuff.
Apparently the function wants a variant or a pointer as a parameter. If memory serves a variant datatype is in effect a pointer to the memory location of the given data.
Trim() returns a variant where-as Trim$() returns a string This would explain the reason it worked with Trim() but not with a string.
If the function wants a pointer, why dont give it what it wants? Try VarPtr() with a String variable. This way there would be no need of using the Variant DataType.
If you had read my posts on the subject, you would have seen that I tried both varptr and strptr and both did not work... and it was also I who found that the trim and the variant solutions...
I don't know if you guys got any further with the shell method, or even care but after a lot of trial and error I just had to share:
The FolderItems3 collection has a filter property: Filter(grfFlags As Long, bstrFileSpec As String)
The FileSpec is the familiar *.??? stuff and you can use multiple specs separated by a ; .
The grfFlags is a combo of the values in the SHCONTF enumeration.
So: if clItems is a FolderItems3
Code:
clItems.Filter SHCONTF_FOLDERS Or SHCONTF_INCLUDEHIDDEN, "*"
For Each oFolderItem In clItems
If oFolderItem.IsFileSystem Then
colFolders.Add oFolderItem.GetFolder
numDirs = numDirs + 1
End If
Next oFolderItem
clItems.Filter SHCONTF_NONFOLDERS Or SHCONTF_INCLUDEHIDDEN, "*"
For Each oFolderItem In clItems
numFiles = numFiles + 1
Next oFolderItem
I am using this in a non-recursive procedure, I am also scanning the entire drive. For some reason the folderitem isfolder test calls zips and cabs folders. The filter function seems to weed those out when done this way. The snippet I posted gives me the same number of files and folders that I get when I select everything in the C:\ folder and choose properties, so I think these are the proper results.
In order to use this method you need to run through folders and files separately. It's not a speed hit though, because each item is still only done once. It also gives you the chance to use the FileSpec argument.
Hope someone finds this helpful.
By the way: SHCONTF_INCLUDEHIDDEN = &H80, SHCONTF_NONFOLDERS = &H40 , SHCONTF_FOLDERS = &H20
Helpful it is. I was already wondering about how to implement pattern matching.
You could possibly do it in one pass:
Code:
clItems.Filter SHCONTF_FOLDERS Or SHCONTF_NONFOLDERS Or SHCONTF_INCLUDEHIDDEN, "*"
For Each oFolderItem In clItems
If oFolderItem.IsFileSystem Then
If oFolderItem.IsFolder Then
colFolders.Add oFolderItem.GetFolder
numDirs = numDirs + 1
Else
numFiles=NumFiles+1
End If
End If
Next oFolderItem
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.