|
-
May 8th, 2011, 12:04 AM
#1
fill directory structure into treeview from local or ftp
this developed from a thread in vb6,
http://www.vbforums.com/showthread.php?t=649217
and i was pleased enough with the results i decided to move it to code bank
any recursive directory search is slow, even on the local machine, as it has to test every file to see if it is a folder
i am sure there would be better ways to do it, you only have to look at the way firefox ftp addin does it
to improve speed, it should be possible to only populate the root, then populate each of the nodes as they are opened, here is modified code, also solves unique key issue
for those who did not see the original thread
both sh as object and pathsep as string, need to be declared globally for the code to work correctly, feel free to change this if you do not like using global variables
vb Code:
Private Sub filltree()
Dim nod As Object, n As Object, initdir As Variant
If Not sh Is Nothing Then Exit Sub
pathsep = "\" 'declare globally
'initdir = "c:\"
If InStr(initdir, "/") > 0 Then pathsep = "/"
Set sh = CreateObject("shell.application")
Set n = sh.namespace(initdir)
If Not Right(initdir, 1) = pathsep Then initdir = initdir & pathsep
Set nod = tv.Nodes.Add(, , initdir, n.self.Name)
listdirs n, nod
nod.Expanded = True
End Sub
Sub listdirs(f As Object, nod As Object)
Dim nod2 As Object, fitem As Object, i As Integer
If f Is Nothing Then Exit Sub
For Each fitem In f.items
If fitem.isfolder Then
Set nod2 = tv.Nodes.Add(nod, tvwChild, nod.Key & fitem.Name & pathsep, fitem.Name)
' listdirs sh.namespace(fitem), nod2
End If
DoEvents
Next
End Sub
Private Sub tv_NodeClick(ByVal Node As MSComctlLib.Node)
If Node.Children > 0 Then Node.Expanded = True: Exit Sub
listdirs sh.namespace(Node.Key), Node
Node.Expanded = True
End Sub
the advantage here to using a shell object is you can use for either local of FTP folders and can start the tree from any level as supplied in initdir
as you might note i did this using late binding, you can add a reference to shell controls and automation, then change your variables to appropriate types, no other APIs or references are required, except treeview (tv) change name to suit
change ftp host, username and password as required
i tested on local harddrive runs seemlessly, pretty bloody good on ftp, even with very slow connection
i did some editing after pasting, so hope no errors
i am certain that similar code can be used with the ftp APIs to produce identical results for ftp, if desired
Last edited by westconn1; May 9th, 2011 at 04:58 AM.
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
-
May 8th, 2011, 03:14 AM
#2
Re: fill directory structure into treeview from local or ftp
for the purists amongst us and cos someone challenged me here is as API version to do the same (ftp only)
i found unless i reopened the ftp connection, enumerating the files failed to find any,
any improvements welcomed
all ftp code courtesy of allapi, just rearranged
module
vb Code:
Const FTP_TRANSFER_TYPE_UNKNOWN = &H0 Const FTP_TRANSFER_TYPE_ASCII = &H1 Const FTP_TRANSFER_TYPE_BINARY = &H2 Const INTERNET_DEFAULT_FTP_PORT = 21 ' default for FTP servers Const INTERNET_SERVICE_FTP = 1 Const INTERNET_FLAG_PASSIVE = &H8000000 ' used for FTP connections Const INTERNET_OPEN_TYPE_PRECONFIG = 0 ' use registry configuration Const INTERNET_OPEN_TYPE_DIRECT = 1 ' direct to net Const INTERNET_OPEN_TYPE_PROXY = 3 ' via named proxy Const INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4 ' prevent using java/script/INS Const MAX_PATH = 260 Private Type FILETIME dwLowDateTime As Long dwHighDateTime As Long End Type Private Type WIN32_FIND_DATA dwFileAttributes As Long ftCreationTime As FILETIME ftLastAccessTime As FILETIME ftLastWriteTime As FILETIME nFileSizeHigh As Long nFileSizeLow As Long dwReserved0 As Long dwReserved1 As Long cFileName As String * MAX_PATH cAlternate As String * 14 End Type Private Declare Function InternetCloseHandle Lib "wininet" (ByRef hInet As Long) As Long Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" (ByVal hInternetSession As Long, ByVal sServerName As String, ByVal nServerPort As Integer, ByVal sUserName As String, ByVal sPassword As String, ByVal lService As Long, ByVal lFlags As Long, ByVal lContext As Long) As Long Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" (ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, ByVal sProxyBypass As String, ByVal lFlags As Long) As Long Private Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean Private Declare Function FtpGetCurrentDirectory Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Long Private Declare Function FtpCreateDirectory Lib "wininet.dll" Alias "FtpCreateDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean Private Declare Function FtpRemoveDirectory Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" (ByVal hFtpSession As Long, ByVal lpszFileName As String) As Boolean Private Declare Function FtpRenameFile Lib "wininet.dll" Alias "FtpRenameFileA" (ByVal hFtpSession As Long, ByVal lpszExisting As String, ByVal lpszNew As String) As Boolean Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" (ByVal hConnect As Long, ByVal lpszRemoteFile As String, ByVal lpszNewFile As String, ByVal fFailIfExists As Long, ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, ByRef dwContext As Long) As Boolean Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" (ByVal hConnect As Long, ByVal lpszLocalFile As String, ByVal lpszNewRemoteFile As String, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean Private Declare Function InternetGetLastResponseInfo Lib "wininet.dll" Alias "InternetGetLastResponseInfoA" (lpdwError As Long, ByVal lpszBuffer As String, lpdwBufferLength As Long) As Boolean Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" (ByVal hFtpSession As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, ByVal dwContent As Long) As Long Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpvFindData As WIN32_FIND_DATA) As Long Const PassiveConnection As Boolean = True Public hConnection As Long, hOpen As Long, sOrgPath As String, pathsep As String Public Sub Enumdirs(tv As TreeView, nod As node) Dim pData As WIN32_FIND_DATA, hFind As Long, lRet As Long 'set the graphics mode to persistent ' Me.AutoRedraw = True 'create a buffer pData.cFileName = String(MAX_PATH, 0) 'find the first file hFind = FtpFindFirstFile(hConnection, "*.*", pData, 0, 0) 'if there's no file, then exit sub If hFind = 0 Then Exit Sub 'show the filename ' Me.Print Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1) Do 'create a buffer pData.cFileName = String(MAX_PATH, 0) 'find the next file lRet = InternetFindNextFile(hFind, pData) 'if there's no next file, exit do If pData.dwFileAttributes And vbDirectory Then tv.Nodes.Add nod, tvwChild, nod.Key & pathsep & cut(pData.cFileName), cut(pData.cFileName) End If If lRet = 0 Then Exit Do 'show the filename ' Me.Print Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1) Loop 'close the search handle InternetCloseHandle hFind End Sub Sub ShowError() Dim lErr As Long, sErr As String, lenBuf As Long 'get the required buffer size InternetGetLastResponseInfo lErr, sErr, lenBuf 'create a buffer sErr = String(lenBuf, 0) 'retrieve the last respons info InternetGetLastResponseInfo lErr, sErr, lenBuf 'show the last response info MsgBox "Error " + CStr(lErr) + ": " + sErr, vbOKOnly + vbCritical End Sub Function cut(p As String) As String cut = Left(p, InStr(p, Chr(0)) - 1) End Function Function setdir(hcon As Long, pth As String) As Boolean setdir = FtpSetCurrentDirectory(hConnection, pth) = 1 End Function Function getdir() Dim scurpath As String scurpath = String(MAX_PATH, 0) 'get the directory FtpGetCurrentDirectory hConnection, scurpath, Len(scurpath) getdir = cut(scurpath) End Function Function openftp() As Boolean server = user = pass = 'open an internet connection hOpen = InternetOpen("API-Guide sample program", INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0) 'connect to the FTP server hConnection = InternetConnect(hOpen, server, INTERNET_DEFAULT_FTP_PORT, user, pass, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0) 'create a buffer to store the original directory sOrgPath = String(MAX_PATH, 0) 'get the directory FtpGetCurrentDirectory hConnection, sOrgPath, Len(sOrgPath) If Len(cut(sOrgPath)) > o Then openftp = True End Function Sub closeftp() 'close the FTP connection InternetCloseHandle hConnection 'close the internet connection InternetCloseHandle hOpen End Sub
form with treeview (tv), change name to suit
vb Code:
Private Sub tv_NodeClick(ByVal node As MSComctlLib.node) If node.Children > 0 Then node.Expanded = True: Exit Sub 'listdirs sh.namespace(node.Key), node If openftp Then If setdir(hConnection, node.Key) Then Enumdirs tv, node closeftp End If node.Expanded = True End Sub Private Sub filltree() Dim nod As node, scurdir As String pathsep = "/" If openftp Then scurdir = getdir Set nod = tv.Nodes.Add(, , scurdir, scurdir) Enumdirs tv, nod closeftp Else: MsgBox "ftp failed to connect" End If nod.Expanded = True End Sub
Last edited by westconn1; May 8th, 2011 at 05:26 AM.
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
-
Oct 26th, 2011, 11:28 AM
#3
Re: fill directory structure into treeview from local or ftp
This is almost exactly what I need. I'm trying to make a treeview of a folder with several subfolders and subfolders of subfolders which this does perfectly. Now I also need to be able to list and access the files within those subfolders in the treeview. I've tried and tried and have to admit that this is beyond me. The next challenge and improvement to the code would be the above. It seems to me that it should be quite simple but I just can't manage it. Any ideas?
-
Oct 27th, 2011, 05:31 AM
#4
Re: fill directory structure into treeview from local or ftp
i have modified the code to list files as well
what you want to do with files when clicked?
i have just displayed msgbox
tested only briefly
vb Code:
Sub listdirs(f As Object, nod As Object) Dim nod2 As Object, fitem As Object, i As Integer If f Is Nothing Then Exit Sub For Each fitem In f.Items If fitem.IsFolder Then Set nod2 = tv.Nodes.Add(nod, tvwChild, nod.Key & fitem.Name & pathsep, fitem.Name) Else ' add file tv.Nodes.Add nod, tvwChild, nod.Key & fitem.Name, fitem.Name ' listdirs sh.namespace(fitem), nod2 End If DoEvents Next End Sub Private Sub tv_NodeClick(ByVal Node As MSComctlLib.Node) If Node.Children > 0 Then Node.Expanded = True: Exit Sub If Right(Node.Key, 1) = pathsep Then listdirs sh.NameSpace(Node.Key), Node Else MsgBox "this is a file " & node.key End If Node.Expanded = True End Sub
it was pretty easy to add this functionality, but, note apart from the code in this thread, i have never used a treeview, so some improvements could be possible
Last edited by westconn1; Oct 27th, 2011 at 05:40 AM.
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
-
Oct 27th, 2011, 09:03 AM
#5
Re: fill directory structure into treeview from local or ftp
Hi Westconn. I'll compare the two versions to see how you made the difference. This is quite a learning curve for me.
What I have is a directory with a list of English audio samples for teaching English. They come from different sources - relating to different textbooks. Each can have a sub directory for CD1, CD2, CD3 etc and under those, the list of tracks on each CD. I want to click on a track and play it. I have already got WMP working, with lots of research on this forum I might add and your update to your code works well.
There is only one problem though. When you displa the msgbox, Node.Key does not include the file extension (Which may be .mp3, .wav or .wma". I get an error message saying "the file you are trying to play has an extension (.) that doesn't coincide with the format of the file...".
Is there a way to append the filetype to Node.key?
-
Oct 27th, 2011, 03:29 PM
#6
Re: fill directory structure into treeview from local or ftp
Node.Key does not include the file extension (Which may be .mp3, .wav or .wma". I get an error message saying "the file you are trying to play has an extension (.) that doesn't coincide with the format of the file...".
i do not know why you have this problem, i definitely get the file extension in the message box, i have retested this morning on different computer, with correct result
the only thing i can think of, as this uses a shell object, is if you have file extensions for known file types not displayed in explorer windows, they are also not returned with the filename in the shell object
tested this and appears to be the case, so try changing line 8 to
vb Code:
tv.Nodes.Add nod, tvwChild, fitem.path, fitem.Name
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
-
Oct 28th, 2011, 08:32 AM
#7
Re: fill directory structure into treeview from local or ftp
That is perfect. Thank you very much Westconn. I know it looks like you've done all the work and I've just copied the code, but rest assured, I will examine it in detail and learn from it.
-
Aug 30th, 2015, 11:24 AM
#8
Re: fill directory structure into treeview from local or ftp
Two problems with your last addition
You have the following code
Code:
Private Sub tv_NodeClick(ByVal Node As MSComctlLib.Node)
If Node.Children > 0 Then Node.Expanded = True: Exit Sub
If Right(Node.Key, 1) = pathsep Then
listdirs sh.NameSpace(Node.Key), Node
Else
MsgBox "this is a file " & Node.Key
End If
Node.Expanded = True
End Sub
1)
Here you check if directory name (which was clicked on in the treeview) has a "/" on the end of the line:
If Right(Node.Key, 1) = pathsep
However, shouldn't everything be a directory name since the treeview only contains directory names and the name was clicked on as a treeview node. So why the check? Also, the names do not end with a "/" so control goes to the Else clause which is incorrect.
2)
In the 1st If clause you have this:
listdirs sh.NameSpace(Node.Key), Node
but sh.NameSpace() causes error 91 object variable or With block not set. I see no where in the code where it is set.
Am I supposed to set this to something?
And yes, I added a reference to Microsoft Shell Controls and Automation
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Aug 30th, 2015, 12:12 PM
#9
Re: fill directory structure into treeview from local or ftp
Two problems with your last addition
You have the following code
Code:
Private Sub tv_NodeClick(ByVal Node As MSComctlLib.Node)
If Node.Children > 0 Then Node.Expanded = True: Exit Sub
If Right(Node.Key, 1) = pathsep Then
listdirs sh.NameSpace(Node.Key), Node
Else
MsgBox "this is a file " & Node.Key
End If
Node.Expanded = True
End Sub
1)
Here you check if directory name (which was clicked on in the treeview) has a "/" on the end of the line:
If Right(Node.Key, 1) = pathsep
However, shouldn't everything be a directory name since the treeview only contains directory names and the name was clicked on as a treeview node. So why the check? Also, the names do not end with a "/" so control goes to the Else clause which is incorrect.
2)
In the 1st If clause you have this:
listdirs sh.NameSpace(Node.Key), Node
but sh.NameSpace() causes error 91 object variable or With block not set. I see no where in the code where it is set.
Am I supposed to set this to something?
And yes, I added a reference to Microsoft Shell Controls and Automation
EDIT: OK, I added this line in Form_Load():
Set sh = CreateObject("shell.application")
This appears to take care of the 2nd issue stated above. But now it creates another issue. When it calls:
listdirs sh.NameSpace(Node.Key), Node but in sub listdirs:
Sub listdirs(f As Object, nod As Object)
f is always Nothing
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Aug 31st, 2015, 05:26 AM
#10
Re: fill directory structure into treeview from local or ftp
1)
Here you check if directory name (which was clicked on in the treeview) has a "/" on the end of the line:
this code is from post #4, read the preamble to post #4, if you are not interested in files at all then you do not need the code from post #4, but if you use that code from post #4, you should use all the code from that post
EDIT: OK, I added this line in Form_Load():
this should not have been required, as it is taken care of in filltree procedure (see post #1), which is needed for other reasons as well, and could in turn be called from formload, a command button or any other event or procedure
i retested the code from posts #1 and 4 together, created a new project, copied and pasted code from here, all worked correctly, without error,
but i decided i did not like the way the directories and files loaded into the treeview nodes, so i modified as below, by this method the files could either be loaded into the treeview after the directories, or treated absolutely separately to do whatever, or ignored completely
Code:
Sub listdirs(f As Object, nod As Object)
Dim nod2 As Object, fitem As Object, i As Integer
If f Is Nothing Then Exit Sub
' list all folders first
For Each fitem In f.items
If fitem.isfolder Then Set nod2 = tv.Nodes.Add(nod, tvwChild, nod.Key & fitem.Name & pathsep, fitem.Name)
' note the above adds pathsep to node key for folders
Next
For Each fitem In f.items
If Not fitem.isfolder Then tv.Nodes.Add nod, tvwChild, nod.Key & fitem.Name, fitem.Name
' the above line can be replaced, to put the files into any other controls
' or this entire second for loop can be commented out
Next
End Sub
reposted here for completeness in this thread
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
-
Aug 31st, 2015, 02:22 PM
#11
Re: fill directory structure into treeview from local or ftp
I appreciate all the effort and the code you put together and I must say it is the best I have come across so far and I just wish I had enough knowledge to re-write it so as not to use internal functions or whatever is used behind the scenes. As good as it is I have the same problem with it as I do with those using APIs like those from the Inet family and they just fall short of retrieving all the directories and files. This seems to be a problem with the internal elements (be it Inet APIs or Shell Object) and not the code of the programmer. The only way around this dilemma is to write raw code getting the first directory returned by the FTP Site and start there looking for sub directories within that directory and keep drilling down until I have retrieved all inner sub directories. I know this means using a recursive sub which I have never really done one correctly but I guess I will have to learn.
Using raw code I can inspect the returned listings looking for hidden files and directory pointers which is exactly what the other approaches fail to do.
OK, do you know of any code that uses recursive directory searching without the aide of APIs, etc. OK if not. I will try to figure out what steps I need to take and build upon that.
Again, thanks, westconn, I still think your above two methods are great
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Aug 31st, 2015, 04:36 PM
#12
Re: fill directory structure into treeview from local or ftp
after some searching, i believe you are correct, the only way to retrieve hidden files etc, is to use a client /server type application based on winsock control or dll, there is some example using the dll here http://www.vbforums.com/showthread.p...=1#post4296109
or a basic winsock ftp http://www.a1vbcode.com/app-3000.asp
edit:
i tested the sample program linked, with some minor change the code will return all files including hidden, but some more changes would be needed to complete the modification as a working sample
Last edited by westconn1; Sep 1st, 2015 at 05:26 AM.
i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next
dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part
come back and mark your original post as resolved if your problem is fixed
pete
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|