Need help understanding a recursive process, please

E

Ed from AZ

I had a macro I built last year that would iterate through my
Favorites folder and its subfolders and write the names of the links
into a Word doc. I copied the bulk of it from somewhere, and never
really thought through the process of how it worked. Well, it seems
IT made another midnight push, and several of my macros are gone. (I
suspect the push somehow ate a chunk of my Normal template!)

I'm now trying to rebuild this macro, but am getting hung up on the
concept of the recursive process. I'm using SHELL, and it's working
fine through the first level. I capture the name of the top-level
folder ("Favorites"), then iterate through the links directly under
that, and that works great. I can then iterate through the subfolders
and capture the links in each of those. Again, no problem. I have
the "iterate links" and iterate subfolders" as two separate macros
called as needed by the master loop.

But if a subfolder contains a subfolder, it's going to want to call
the same procedure that's already running to iterate through _that_
subfolder. Likewise if there's a fourth-level subfolder.

So how do I fold this back on itself without causing problems? I
originally got this from a Script sample, and while it did return the
names, it also locked into an endless loop and I had to End the
processes - all six of them! I could just scour the net for a working
sample - I'm sure there's a bunch of them. But I'm hoping to learn
something along the way, too. Any help is greatly appreciated.

Ed
 
K

Karl E. Peterson

Ed said:
I had a macro I built last year that would iterate through my
Favorites folder and its subfolders and write the names of the links
into a Word doc. I copied the bulk of it from somewhere, and never
really thought through the process of how it worked. Well, it seems
IT made another midnight push, and several of my macros are gone. (I
suspect the push somehow ate a chunk of my Normal template!)

Bad IT! Bad!!!
I'm now trying to rebuild this macro, but am getting hung up on the
concept of the recursive process. I'm using SHELL, and it's working
fine through the first level. I capture the name of the top-level
folder ("Favorites"), then iterate through the links directly under
that, and that works great. I can then iterate through the subfolders
and capture the links in each of those. Again, no problem. I have
the "iterate links" and iterate subfolders" as two separate macros
called as needed by the master loop.

Shell? What for?
But if a subfolder contains a subfolder, it's going to want to call
the same procedure that's already running to iterate through _that_
subfolder. Likewise if there's a fourth-level subfolder.

I build an array of subfolders, then call the scan function on each one of them in
turn.
So how do I fold this back on itself without causing problems? I
originally got this from a Script sample, and while it did return the
names, it also locked into an endless loop and I had to End the
processes - all six of them! I could just scour the net for a working
sample - I'm sure there's a bunch of them. But I'm hoping to learn
something along the way, too. Any help is greatly appreciated.

Well, here's my method: http://vb.mvps.org/samples/DirDrill

The general idea is to have a single scan function. It iterates through all the
objects in the folder, building an array of all the folders it encounters, and doing
whatever it wants or needs to do with the other files. Then, while still in the san
function, but presumably after processing the found files, call that scan function
again once for each of the found folders. I think it's easier to see than explain.

Bottom line, every recursive routine needs an "exit condition" - something that will
prevent it from calling itself again. In this case, it'd be the failure to find
anymore subfolders. At that point, things start uncoiling back up the chain.
Here's the "classic" example for you to ponder:

Public Function Factorial(ByVal n As Integer) As Long
If n > 12 Then
' Overflow, does not compute
ElseIf n > 1 Then
Factorial = n * Factorial(n - 1)
Else
Factorial = 1
End If
End Function

Step through that with the F8 key, observing what happens and how variables change.
Beware of numeric and/or stack overflow possibilities!
 
E

Ed from AZ

Bad IT!  Bad!!!

We keep trying to keep them from doing their business in our space,
but they just don't listen!!

Shell?  What for?

Well, because that was the sample I found to try and rework and learn
from??

Also, because I'm iterating through my Favorites internet shortcuts
list, trying to capture the namr and URL path of each link to write
them as hyperlinks in a Word doc.

With SHELL, with everything Dim'd as Object, I have :
For Each objItem In objFolder.Items
If objItem.IsLink Then
Set objLink = objItem.GetLink
MsgBox objItem.Name
MsgBox objLink.Path
End If
Next

But in FSO, the File Object doesn't seem to have a Link property, and
I can't get the http path from the object.
(Incidently, I did find the original code I first modified. I lifted
it from here:
http://msdn.microsoft.com/en-us/library/aa164475(office.10).aspx
But I can't get the link properties from the object.)

I'm also using Word 2007, if that makes any difference.

And thank you very much for the response and the example. I found
your DirDrill, but it looked very overwhelming to understand. But
when I have time, I'll explore these and see if I can figure out some
things.

Ed
 
C

Cindy M.

Hi Ed,
We keep trying to keep them from doing their business in our space,
but they just don't listen!!

And the thing to learn from this is:

1. Export the modules regularly, to back them up (as plain text)

2. Don't keep your macro tools in the Normal template. Once you have it
refined to a tool (done tweaking) copy it to another template in your
Startup folder (so that it loads globally). There it's less likely to get
trashed... And IT should be backing it up for you :)

Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update Jun 17 2005)
http://www.word.mvps.org

This reply is posted in the Newsgroup; please post any follow question or
reply in the newsgroup and not by e-mail :)
 
K

Karl E. Peterson

Ed said:
We keep trying to keep them from doing their business in our space,
but they just don't listen!!

I understand, believe me. Condolences...
Well, because that was the sample I found to try and rework and learn
from??
Oh.

Also, because I'm iterating through my Favorites internet shortcuts
list, trying to capture the namr and URL path of each link to write
them as hyperlinks in a Word doc.

With SHELL, with everything Dim'd as Object, I have :
For Each objItem In objFolder.Items
If objItem.IsLink Then
Set objLink = objItem.GetLink
MsgBox objItem.Name
MsgBox objLink.Path
End If
Next

Ah said:
But in FSO,

I hate FSO. It's an abomination. Great for scripting (VBScript) but not for use in
languages that have native file i/o support.
the File Object doesn't seem to have a Link property, and
I can't get the http path from the object.
(Incidently, I did find the original code I first modified. I lifted
it from here:
http://msdn.microsoft.com/en-us/library/aa164475(office.10).aspx
But I can't get the link properties from the object.)

I'm sure I'd be of no help with that.
I'm also using Word 2007, if that makes any difference.

Nope. VBA is still VBA. Thank god.
And thank you very much for the response and the example. I found
your DirDrill, but it looked very overwhelming to understand. But
when I have time, I'll explore these and see if I can figure out some
things.

Okay, try this...

Option Explicit

Private WithEvents m_dir As CDirDrillVB

Private Sub Command1_Click()
If m_dir Is Nothing Then
Set m_dir = New CDirDrillVB
End If
With m_dir
.Folder = "C:\Documents and Settings\Karl\Favorites"
.Pattern = "*.url"
.Recursive = True
.BeginSearch
End With
End Sub

Private Sub m_dir_FileFound(ByVal FileSpec As String, ByVal FileSize As Variant,
Cancel As Boolean)
Dim Url As String
' Get 2nd line, starting with 5th char
Url = Mid$(Split(ReadFile(FileSpec), vbCrLf)(1), 5)
Debug.Print " - "; m_dir.ExtractName(FileSpec, True); " <"; Url; ">"
End Sub

Private Sub m_dir_FolderStart(ByVal FolderSpec As String, Cancel As Boolean)
Debug.Print FolderSpec
End Sub

Public Function ReadFile(ByVal FileName As String) As String
Dim hFile As Long
On Error GoTo Hell
hFile = FreeFile
Open FileName For Binary As #hFile
ReadFile = Space$(LOF(hFile))
Get #hFile, , ReadFile
Close #hFile
Hell:
End Function

After you've added CDirDrill to your project, all the setup work is done in that
button click event. There, you tell CDirDrill where to look, what to look for, and
to do so recursively. Then you tell it to GO! CDirDrill then fires events
everytime it changes directories and everytime it finds a matching file. You can
react to those events. As it happens, Shortcut/Link files are really just raw text
with a .URL extension. They look like this, inside:

[InternetShortcut]
URL=http://www.microsoft.com/isapi/redir.dll?prd=ie&ar=windows
Modified=E0814A6F9D23C701A2

Actually, these are INI files, but that's a whole deeper level. <g> Anyway, you
want the 2nd line, starting with the 5th character. That's all there is to it!
 
E

Ed from AZ

That's all there is to it!

Easy for you to say!! <G>

Thanks so much, Karl, for all your time and help. I don't know what
I'd do without these groups and the nice people who come here to
help. Keep floundering, I guess, but certainly not move forward!

Cheers!
Ed

We keep trying to keep them from doing their business in our space,
but they just don't listen!!

I understand, believe me.  Condolences...
Well, because that was the sample I found to try and rework and learn
from??
Oh.

Also, because I'm iterating through my Favorites internet shortcuts
list, trying to capture the namr and URL path of each link to write
them as hyperlinks in a Word doc.
With SHELL, with everything Dim'd as Object, I have :
For Each objItem In objFolder.Items
  If objItem.IsLink Then
      Set objLink = objItem.GetLink
      MsgBox objItem.Name
      MsgBox objLink.Path
  End If
Next

Ah said:
But in FSO,

I hate FSO.  It's an abomination.  Great for scripting (VBScript) butnot for use in
languages that have native file i/o support.
the File Object doesn't seem to have a Link property, and
I can't get the http path from the object.
(Incidently, I did find the original code I first modified.  I lifted
it from here:
http://msdn.microsoft.com/en-us/library/aa164475(office.10).aspx
But I can't get the link properties from the object.)

I'm sure I'd be of no help with that.
I'm also using Word 2007, if that makes any difference.

Nope.  VBA is still VBA.  Thank god.
And thank you very much for the response and the example.  I found
your DirDrill, but it looked very overwhelming to understand.  But
when I have time, I'll explore these and see if I can figure out some
things.

Okay, try this...

   Option Explicit

   Private WithEvents m_dir As CDirDrillVB

   Private Sub Command1_Click()
      If m_dir Is Nothing Then
         Set m_dir = New CDirDrillVB
      End If
      With m_dir
         .Folder = "C:\Documents and Settings\Karl\Favorites"
         .Pattern = "*.url"
         .Recursive = True
         .BeginSearch
      End With
   End Sub

   Private Sub m_dir_FileFound(ByVal FileSpec As String, ByVal FileSize As Variant,
Cancel As Boolean)
      Dim Url As String
      ' Get 2nd line, starting with 5th char
      Url = Mid$(Split(ReadFile(FileSpec), vbCrLf)(1), 5)
      Debug.Print " - "; m_dir.ExtractName(FileSpec, True); " <"; Url; ">"
   End Sub

   Private Sub m_dir_FolderStart(ByVal FolderSpec As String, Cancel As Boolean)
      Debug.Print FolderSpec
   End Sub

   Public Function ReadFile(ByVal FileName As String) As String
      Dim hFile As Long
      On Error GoTo Hell
      hFile = FreeFile
      Open FileName For Binary As #hFile
         ReadFile = Space$(LOF(hFile))
         Get #hFile, , ReadFile
      Close #hFile
   Hell:
   End Function

After you've added CDirDrill to your project, all the setup work is done in that
button click event.  There, you tell CDirDrill where to look, what to look for, and
to do so recursively.  Then you tell it to GO!  CDirDrill then fires events
everytime it changes directories and everytime it finds a matching file.  You can
react to those events.  As it happens, Shortcut/Link files are really just raw text
with a .URL extension.  They look like this, inside:

   [InternetShortcut]
   URL=http://www.microsoft.com/isapi/redir.dll?prd=ie&ar=windows
   Modified=E0814A6F9D23C701A2

Actually, these are INI files, but that's a whole deeper level. <g>  Anyway, you
want the 2nd line, starting with the 5th character.  That's all there is to it!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top