Page 1 of 2
Recursive directory search
Posted: Mon Dec 04, 2006 10:59 pm
by Clutch
Code updated for 5.20+
Code: Select all
Procedure SearchDirectory(dir$, pattern$, List dList.s(), level.l = 0)
Protected eName$
NewList Dirs.s()
If (level = 0)
ClearList(dList())
EndIf
If Right(dir$, 1) <> "\"
dir$ + "\"
EndIf
If ExamineDirectory(0, dir$, "")
While NextDirectoryEntry(0)
If DirectoryEntryType(0) = #PB_DirectoryEntry_Directory
eName$ = DirectoryEntryName(0)
If (eName$ <> ".") And (eName$ <> "..")
AddElement(Dirs())
Dirs() = eName$ + "\"
EndIf
EndIf
Wend
FinishDirectory(0)
If ExamineDirectory(0, dir$, pattern$)
While NextDirectoryEntry(0)
eName$ = DirectoryEntryName(0)
If (eName$ <> ".") And (eName$ <> "..")
AddElement(dList())
dList() = dir$ + eName$
If DirectoryEntryType(0) = #PB_DirectoryEntry_Directory
dList() + "\"
EndIf
EndIf
Wend
FinishDirectory(0)
EndIf
EndIf
If ListSize(Dirs())
ForEach Dirs()
SearchDirectory(dir$ + Dirs(), pattern$, dList(), level + 1)
Next
EndIf
If (level = 0)
ForEach dList()
dList() = Mid(dList(), Len(dir$) + 1, Len(dList()))
Next
SortList(dList(), 2)
EndIf
EndProcedure
NewList FilesAndFolders.s()
SearchDirectory("C:\WINDOWS", "sys*", FilesAndFolders())
Debug "Found " + Str(ListSize(FilesAndFolders())) + " object(s)"
ForEach FilesAndFolders()
Debug FilesAndFolders()
Next
End
Re: Recursive directory search
Posted: Fri Dec 08, 2006 4:58 pm
by NoahPhense
Very nice!
- np
Posted: Fri Dec 08, 2006 6:49 pm
by AND51
Clutch has got 53 lines. I've got just 34.
- My procedure...
- does not use a protected linked list (saves memory)
- does not examine the directory twice - only if recursive is enabled
- supports optional recursivity
- returns number of found items (Clutch, why don't you use ProcedureReturn?)
- is faster than Clutch's one
- does not need Clutch's level-parameter
- has got optional pattern$ parameter (empty pattern lists all items)
Code: Select all
Procedure.l SearchDirectory(dir$, yourLinkedList.s(), pattern$="", recursive=1)
Static level.l=-1
If Not Right(dir$, 1) = "\"
dir$+"\"
EndIf
Protected dir.l=ExamineDirectory(#PB_Any, dir$, pattern$)
If dir
While NextDirectoryEntry(dir)
If DirectoryEntryName(dir) <> "." And DirectoryEntryName(dir) <> ".."
AddElement(yourLinkedList())
For n=CountString(dir$, "\")-level To CountString(dir$, "\")
yourLinkedList()+StringField(dir$, n, "\")+"\"
Next
yourLinkedList()+DirectoryEntryName(dir)
If DirectoryEntryType(dir) = #PB_DirectoryEntry_Directory
yourLinkedList()+"\"
EndIf
EndIf
Wend
FinishDirectory(dir)
EndIf
Protected all.l=ExamineDirectory(#PB_Any, dir$, "")
If all
While NextDirectoryEntry(all)
If DirectoryEntryType(all) = #PB_DirectoryEntry_Directory And DirectoryEntryName(all) <> "." And DirectoryEntryName(all) <> ".."
level+1
SearchDirectory(dir$+DirectoryEntryName(all)+"\", yourLinkedList(), pattern$, recursive)
level-1
EndIf
Wend
FinishDirectory(all)
EndIf
ProcedureReturn CountList(yourLinkedList())
EndProcedure ; 34 lines by AND51
NewList FilesAndFolders.s()
found = SearchDirectory("C:\WINDOWS", FilesAndFolders(), "sys*", 1)
ForEach FilesAndFolders()
Debug FilesAndFolders()
Next
Debug found
Posted: Fri Dec 08, 2006 8:13 pm
by Clutch
AND51 wrote:Clutch has got 53 lines. I've got just 34.
- My procedure...
- does not use a protected linked list (saves memory)
- does not examine the directory twice - only if recursive is enabled
- supports optional recursivity
- returns number of found items (Clutch, why don't you use ProcedureReturn?)
- is faster than Clutch's one
- does not need Clutch's level-parameter
- has got optional pattern$ parameter (empty pattern lists all items)
:roll:
I may be missing something here, but I'm not quite sure how you determined it to be faster than what I posted. Try successive calls to both as follows:
On yours:
Code: Select all
NewList FilesAndFolders.s()
num.q = GetTickCount_()
For i = 1 To 10
found = SearchDirectory("C:\WINDOWS", FilesAndFolders(), "sys*", 1)
ForEach FilesAndFolders()
Debug FilesAndFolders()
Next
Debug found
ClearList(FilesAndFolders()) ;Must add this, as it's left up to the caller
Next i
Debug GetTickCount_() - num
On mine:
Code: Select all
NewList FilesAndFolders.s()
num.q = GetTickCount_()
For i = 1 To 10
SearchDirectory("C:\WINDOWS", "sys*", FilesAndFolders())
Debug "Found " + Str(CountList(FilesAndFolders())) + " object(s)"
ForEach FilesAndFolders()
Debug FilesAndFolders()
Next
Next i
Debug GetTickCount_() - num
End
Yours averaged 53 seconds, mine 2.5 here. Even with only a single call (commenting out the For i/Next i), my code ran consistently 3 times faster. Although, I'm not really sure any of this really matters.
Also, your optional recursivity is broken, by the way. Seems it doesn't matter what is passed for the 'recursive' parameter.
You're right about the ProcedureReturn, though. Good job.
Posted: Fri Dec 08, 2006 9:06 pm
by AND51
I will look at my code again. I've made tweo versions, but publsihed one only due to pattern$-problems...
Posted: Fri Dec 08, 2006 10:01 pm
by Dr. Dri
you cannot make a perf test with the debugger
Dri
Posted: Fri Dec 08, 2006 10:27 pm
by Clutch
Dr. Dri wrote:you cannot make a perf test with the debugger
Dri

Yikes, that's right! I'm getting the same speed from either procedure without using the debugger.
Posted: Fri Dec 08, 2006 11:27 pm
by AND51
I'm too lazy to re-code my procedure. I think I will do that later. If ExamineDirectory() wouldn't have this bug as I postet in an other sub-forum, I could present you my shorter version...
Posted: Sat Dec 09, 2006 4:57 am
by NoahPhense
heh..
Thanks Clutch, not concerned about saving memory.
The only thing that would make me switch is something that was full API.
- np
Posted: Sat Dec 09, 2006 8:59 pm
by Clutch
You're welcome, NoahPhense. Glad you like it.

Posted: Wed Dec 13, 2006 8:15 pm
by dagcrack
Bugs? - Use WINAPI directly then.
Posted: Sat Dec 16, 2006 10:32 am
by Jacobus
hello,
You can to be quick to list in ListIconGadget() for example.
Hide the gadget for the work and show again after.
With the first code of Clutch...
Code: Select all
Procedure SearchDirectory(dir$, pattern$, list.s(), level.l = 0)
Protected eName$
NewList Dirs.s()
If (level = 0)
ClearList(list())
EndIf
If Right(dir$, 1) <> "\"
dir$ + "\"
EndIf
If ExamineDirectory(0, dir$, "")
While NextDirectoryEntry(0)
If DirectoryEntryType(0) = #PB_DirectoryEntry_Directory
eName$ = DirectoryEntryName(0)
If (eName$ <> ".") And (eName$ <> "..")
AddElement(Dirs())
Dirs() = eName$ + "\"
EndIf
EndIf
Wend
FinishDirectory(0)
If ExamineDirectory(0, dir$, pattern$)
While NextDirectoryEntry(0)
eName$ = DirectoryEntryName(0)
If (eName$ <> ".") And (eName$ <> "..")
AddElement(list())
list() = dir$ + eName$
If DirectoryEntryType(0) = #PB_DirectoryEntry_Directory
list() + "\"
EndIf
EndIf
Wend
FinishDirectory(0)
EndIf
EndIf
If CountList(Dirs())
ForEach Dirs()
SearchDirectory(dir$ + Dirs(), pattern$, list(), level + 1)
Next
EndIf
If (level = 0)
ForEach list()
list() = Mid(list(), Len(dir$) + 1, Len(list()))
Next
SortList(list(), 2)
EndIf
EndProcedure
; NewList FilesAndFolders.s()
;
; SearchDirectory("C:\WINDOWS", "sys*", FilesAndFolders())
;
; Debug "Found " + Str(CountList(FilesAndFolders())) + " object(s)"
; ForEach FilesAndFolders()
; Debug FilesAndFolders()
; Next
;
; End
;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
;To do quickly in GUI
Enumeration
#win
#Listicon
#BtnStart
#BtnClose
EndEnumeration
Global NewList FilesAndFolders.s()
If OpenWindow(#win,0,0,500,350, "Files and folders", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_TitleBar)<>0 And CreateGadgetList(WindowID(#win))<>0
ListIconGadget(#Listicon, 5,10,490,300, "Results", 1000 , #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection|#PB_ListIcon_MultiSelect)
ButtonGadget(#BtnStart,5 ,320 ,100 ,20,"Starting")
ButtonGadget(#BtnClose,125 ,320 ,100 ,20,"Close")
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
Select EventGadget()
Case #BtnStart
StartTime = ElapsedMilliseconds()
SearchDirectory("C:\", "*.*", FilesAndFolders(),1)
HideGadget(#Listicon,1); hide Listicongadget to be quick. Comment this line to see the difference
ForEach FilesAndFolders()
AddGadgetItem( #Listicon,-1,FilesAndFolders())
Next
PastTime = ElapsedMilliseconds()-StartTime
SetGadgetItemText(#Listicon,-1,"Found " + Str(CountList(FilesAndFolders())) + " object(s) - Time : "+Str(PastTime/1000)+" seconds",0)
HideGadget(#Listicon,0); after work, show again. Comment this line to see the difference
Case #BtnClose
ClearList(FilesAndFolders()); Necessary, I wouldn't be so sure...
ClearGadgetItemList(#Listicon)
Event = #PB_Event_CloseWindow
EndSelect
EndIf
Until Event = #PB_Event_CloseWindow
End
EndIf
Same with AND51 code.
Thanks to you for that.
Posted: Sat Dec 16, 2006 3:39 pm
by netmaestro
I don't like the HideGadget approach, it's faster but it doesn't look good with the ListIcon gadget disappearing briefly while it gets filled and then reappearing. I'd recommend another solution:
Code: Select all
SendMessage_(GadgetID(#Listicon),#WM_SETREDRAW, #False, 0)
; fifty million AddGadgetItems...
SendMessage_(GadgetID(#Listicon),#WM_SETREDRAW, #True, 0)
Which is just as effective but looks much better.
Posted: Sat Dec 16, 2006 4:50 pm
by Jacobus
Which is just as effective but looks much better.
Yes, That way it's just as well and you can to add a message : " wait a moment please". (I don't know all apis)
Posted: Wed Dec 20, 2006 12:23 pm
by Shardik
netmaestro wrote:
I don't like the HideGadget approach, it's faster but it doesn't look good with the ListIcon gadget disappearing briefly while it gets filled and then reappearing.
What do you think about the (in my opinion

) most user friendly approach of hiding the gadget but displaying a search animation and setting the cursor to an hour glass?
Add the following two procedures to the top of the code example of Jacobus:
Code: Select all
Procedure StartAnimation(AnimationLib.S, AnimationID.L, WindowID.L, xPos.L, yPos.L)
#AnimationIDSearchComputer = 152
AnimationAreaHandle.L
AnimationLibHandle.L
WaitCursorHandle.L
If AnimationAreaHandle = 0
AnimationAreaHandle = CreateWindowEx_(0, "SysAnimate32", "", #ACS_AUTOPLAY | #ACS_CENTER | #ACS_TRANSPARENT | #WS_CHILD | #WS_VISIBLE | #WS_CLIPCHILDREN | #WS_CLIPSIBLINGS, xPos, yPos, 280, 50, WindowID(WindowID), 0, GetModuleHandle_(0), 0)
AnimationLibHandle = LoadLibrary_(AnimationLib)
SendMessage_(AnimationAreaHandle, #ACM_OPEN, AnimationLibHandle, AnimationID)
While WindowEvent() : Wend
EndIf
WaitCursorHandle = LoadCursor_(0, #IDC_WAIT)
SetCursor_(WaitCursorHandle)
EndProcedure
Procedure StopAnimation()
Shared AnimationAreaHandle.L
Shared AnimationLibHandle.L
Shared WaitCursorHandle.L
If AnimationAreaHandle <> 0
DestroyWindow_(AnimationAreaHandle)
AnimationAreaHandle = 0
FreeLibrary_(AnimationLibHandle)
EndIf
If WaitCursorHandle <> 0
SetCursor_(LoadCursor_(0, #IDC_ARROW))
WaitCursorHandle = 0
EndIf
EndProcedure
and change the #BtnStart code block to
Code: Select all
Case #BtnStart
StartTime = ElapsedMilliseconds()
HideGadget(#Listicon,1); hide Listicongadget to be quick. Comment this line to see the difference
StartAnimation("Shell32.DLL", #AnimationIDSearchComputer, #win, 110, 130)
SearchDirectory("C:", "*.*", FilesAndFolders(),1)
ForEach FilesAndFolders()
AddGadgetItem( #Listicon,-1,FilesAndFolders())
Next
StopAnimation()
PastTime = ElapsedMilliseconds()-StartTime
SetGadgetItemText(#Listicon,-1,"Found " + Str(CountList(FilesAndFolders())) + " object(s) - Time : "+Str(PastTime/1000)+" seconds",0)
HideGadget(#Listicon,0); after work, show again. Comment this line to see the difference
and see the difference...
