Looping through Folders

  • Thread starter Thread starter David C. Holley
  • Start date Start date
D

David C. Holley

How do I loop through my Outlook folders using code? I cannot for the
life of me figure it out.
 
Try out the code below. Paste it into a new Module in the Outlook VBA editor
and set a breakpoint on the "Erase strPaths" line. Execute the Run
procedure, and watch how the strPaths() array gets populated before it gets
cleared.

Option Explicit
Dim i As Integer
Dim strPaths() As String
Dim objNS As Outlook.NameSpace
Dim objFolder As Outlook.MAPIFolder
Dim objFolders As Outlook.Folders
Dim objFolders2 As Outlook.Folders

Sub Run()
Set objNS = Application.GetNamespace("MAPI")
Set objFolders = objNS.Folders
RecurseFolders objFolders

Erase strPaths
Set objNS = Nothing
Set objFolder = Nothing
Set objFolders = Nothing
Set objFolders2 = Nothing
End Sub

Sub RecurseFolders(objTheseFolders As Outlook.Folders)
For Each objFolder In objTheseFolders
Debug.Print objFolder.Name
ReDim Preserve strPaths(i + 1)
strPaths(i) = objFolder.FolderPath
Set objFolders2 = objFolder.Folders
i = i + 1
RecurseFolders objFolders2
Next
End Sub
 
Now is it just me or does is it just plain *STUPID* that MS didn't give
us an easy way to do this?
 
I'm sorry, but how is this NOT easy? You can't architect an object model
based on a hierarchical data structure any better than using a Parent ->
Children / one-to-many model. Microsoft has provided the toolset; it is up
to programmers to implement it.

Futhermore, looping through a collection of information that may itself
contain sub-collections is a very common occurrence in modern object oriented
programming.

If you are having difficulties obtaining a reference to a specific folder, I
agree that walking through a hierarchy to find the parent of the desired
folder in order to retrieve that folder can get complex. In this case, if
you know the unique EntryID value for that folder, you can call the
NameSpace.GetFolderFromID method.

Otherwise, if you know the complete path to the folder, you can use this
code sample courtesy of MicroEye (http://www.microeye.com):

'******************************************************************************
'Custom procedure: OpenMAPIFolder(ByVal strPath)
'Purpose: Return a MAPIFolder from Path argument
'Argument: String representation of folder path
'Usage:
'Set objFldr=OpenMAPIFolder("\Public Folders\All Public Folders")
'Returns: MAPIFolder objec
'******************************************************************************
Function OpenMAPIFolder(ByVal strPath) As MAPIFolder
Dim objFldr As MAPIFolder
Dim strDir As String
Dim strName As String
Dim i As Integer
On Error Resume Next
If Left(strPath, Len("\")) = "\" Then
strPath = Mid(strPath, Len("\") + 1)
Else
Set objFldr = golApp.ActiveExplorer.CurrentFolder
End If
While strPath <> ""
i = InStr(strPath, "\")
If i Then
strDir = Left(strPath, i - 1)
strPath = Mid(strPath, i + Len("\"))
Else
strDir = strPath
strPath = ""
End If
If objFldr Is Nothing Then
Set objFldr = golApp.GetNamespace("MAPI").Folders(strDir)
On Error GoTo 0
Else
Set objFldr = objFldr.Folders(strDir)
End If
Wend
Set OpenMAPIFolder = objFldr
End Function
 
I'm thinking more in terms of the Access Object Model where seems a bit
easier to grab stuff. I am about 3/4 through solving the problem with my
own code and it is easier than I thought. My thinking though was related
to the fact that seemed to take multiple levels to get to the folders.
Plus I realized today that this is actually the first time I've had to
do this. All of my other experience with Outlook has been relatively simple.
 
And here is the initial code...

The final will probably take two parameters - one to designate the
namespace (where to look) and the other the specific folder name (what
to look for). In retrospect, I think that my initial problem had to do
with not realizing that there's a difference between a MAPI folder and
the folders underneath it.

Sub listOutlookFolders()

Dim appOutlook As Outlook.Application
Dim nms As Outlook.NameSpace
Dim mapiParentFolder As Outlook.MAPIFolder
Dim targetFolders As Outlook.Folders
Dim i

Set appOutlook = CreateObject("Outlook.Application")
Set nms = appOutlook.GetNamespace("MAPI")

For i = 1 To nms.Folders.Count
Set mapiParentFolder = nms.Folders(i)
Set targetFolders = mapiParentFolder.Folders
For j = 1 To targetFolders.Count
Debug.Print targetFolders(j).Name, targetFolders(j).Parent
Next j
Next i

Set targetFolders = Nothing
Set mapiParentFolder = Nothing
Set nms = Nothing
Set appOutlook = Nothing

End Sub
 
I'm thinking that I just flat missed something initially because

nms.Folders(1).Folders(1).Name

is now doing what I was trying earlier. At any rate here is the initial
code...

The final will probably take two parameters - one to designate the
namespace (where to look) and the other the specific folder name (what
to look for). The idea is to be able to be able point my Access resident
SUBS to a specific folder thus making them more generic.

Sub listOutlookFolders()

Dim appOutlook As Outlook.Application
Dim nms As Outlook.NameSpace
Dim mapiParentFolder As Outlook.MAPIFolder
Dim targetFolders As Outlook.Folders
Dim i
Dim j

Set appOutlook = CreateObject("Outlook.Application")
Set nms = appOutlook.GetNamespace("MAPI")
Stop
For i = 1 To nms.Folders.Count
Set mapiParentFolder = nms.Folders(i)
Set targetFolders = mapiParentFolder.Folders
Debug.Print mapiParentFolder.Name
For j = 1 To targetFolders.Count
Debug.Print targetFolders(j).Name
Next j
Next i

Set targetFolders = Nothing
Set mapiParentFolder = Nothing
Set nms = Nothing
Set appOutlook = Nothing

End Sub
 
What exactly is your intent anyway? Loop n levels deep, or all the way
through? Either way, you have to design the code so that it is recursive - a
function calling itself, like my initial example.
 
To able to find a specific folder (regardless of n-level) for use by
couple of SUBS that create/delete AppointmentItems created from Access.
Currently, the SUBs add/delete AppointmentItems in the default Calendar
folder. Since the SUBS are currently dependent upon a specific folder,
the SUBS aren't as flexible as I would like. While they work for me, as
a single-user and developer, I would like to provide the users with the
ability to have the AppointmentItems placed in alternate calendar folders.
 
[top posting corrected]
To able to find a specific folder (regardless of n-level) for use by
couple of SUBS that create/delete AppointmentItems created from Access.
Currently, the SUBs add/delete AppointmentItems in the default Calendar
folder. Since the SUBS are currently dependent upon a specific folder,
the SUBS aren't as flexible as I would like. While they work for me, as
a single-user and developer, I would like to provide the users with the
ability to have the AppointmentItems placed in alternate calendar folders.

It seems to me that Eric's "RecurseFolders()" in his initial response
provides the necessary starting point. Have you tried it?
 
I didn't have any subfolders setup when I ran it nor did I have multiple
*.pst files open. I'm going back to my originall opinion that it
shouldn't be this difficult to itierate through ALL folders (include
subfolders). I realized that I was wanting this to be as easy as looping
through the Nodes collection of a TreeView. I shouldn't have to use
multiple SUBs (and a recursive one at that) to get the list.

Michael said:
[top posting corrected]
To able to find a specific folder (regardless of n-level) for use by
couple of SUBS that create/delete AppointmentItems created from Access.
Currently, the SUBs add/delete AppointmentItems in the default Calendar
folder. Since the SUBS are currently dependent upon a specific folder,
the SUBS aren't as flexible as I would like. While they work for me, as
a single-user and developer, I would like to provide the users with the
ability to have the AppointmentItems placed in alternate calendar folders.


It seems to me that Eric's "RecurseFolders()" in his initial response
provides the necessary starting point. Have you tried it?
 
I didn't have any subfolders setup when I ran it nor did I have multiple
*.pst files open. I'm going back to my originall opinion that it
shouldn't be this difficult to itierate through ALL folders (include
subfolders). I realized that I was wanting this to be as easy as looping
through the Nodes collection of a TreeView. I shouldn't have to use
multiple SUBs to get the list. If its a FOLDER it should be in the
FOLDERS collection without having to go another layer deeper.

Here's the code that I wrote somewhat inspired by the original...
Sub listOutlookFolders()

'Need to figure out a way to loop subfolders if FOLDERS.COUNT <> 0
'Will probably need to load up an array if multiple folders of the same
name exist
'but will probably just search that folder as well

Dim appOutlook As Outlook.Application
Dim nms As Outlook.NameSpace
Dim mapiParentFolder As Outlook.MAPIFolder
Dim targetFolders As Outlook.Folders
Dim i
Dim j

Set appOutlook = CreateObject("Outlook.Application")
Set nms = appOutlook.GetNamespace("MAPI")
Stop
For i = 1 To nms.Folders.Count
Debug.Print
"-------------------------------------------------------------"
Set mapiParentFolder = nms.Folders(i)
Set targetFolders = mapiParentFolder.Folders
Debug.Print mapiParentFolder.Name
For j = 1 To targetFolders.Count
Call listSubFolders(targetFolders(j))
Next j
Debug.Print
"-------------------------------------------------------------"
Next i

Set targetFolders = Nothing
Set mapiParentFolder = Nothing
Set nms = Nothing
Set appOutlook = Nothing

End Sub
Sub listSubFolders(parentFolder As Object)

Dim i As Integer
Debug.Print parentFolder.Name, parentFolder.Folders.Count
If parentFolder.Folders.Count <> 0 Then
For i = 1 To parentFolder.Folders.Count
Call listSubFolders(parentFolder.Folders(i))
Next i
End If

End Sub


Michael said:
[top posting corrected]
To able to find a specific folder (regardless of n-level) for use by
couple of SUBS that create/delete AppointmentItems created from Access.
Currently, the SUBs add/delete AppointmentItems in the default Calendar
folder. Since the SUBS are currently dependent upon a specific folder,
the SUBS aren't as flexible as I would like. While they work for me, as
a single-user and developer, I would like to provide the users with the
ability to have the AppointmentItems placed in alternate calendar folders.


It seems to me that Eric's "RecurseFolders()" in his initial response
provides the necessary starting point. Have you tried it?
 
I see your point - you want to get folder a folder by name, regardless of its
location. But this approach is flawed because of the underlying data model
of MAPI - folders and items can exist with duplicate names. From a database
perspective, you would like to return a record regardless of which table it
is stored in. But guess what - you can't do it without looping through all
of the tables and building recordsets for each. And from the MAPI
perspective, Folders are essentially tables. Even with ADO you cannot search
for a table without doing something like searching records in system tables
if you use SQL Server (AFAIK). Anyway, I think you get my point.

But judging from your requirements, you simply want to create/delete
Appointments somewhere other than the default Calendar - but you don't know
*where*, correct? If so, then use the NameSpace.PickFolder method to
present the user with a dialog to choose the location, then pass the returned
MAPIFolder object to your procedure. Otherwise, if you do know where the
folder is, then use the OpenMAPIFolder function I provided earlier and pass
it the full path of the folder. If you only know a fraction of the path,
then you know how many levels deep you can start with looping code before you
must call a recursive style method to loop down the tree until the end or
until the folder you are searching for is located.

--
Eric Legault (Outlook MVP, MCDBA, old school WOSA MCSD, B.A.)
Try Picture Attachments Wizard for Outlook:
http://www.collaborativeinnovations.ca
Blog: http://blogs.officezealot.com/legault/


David C. Holley said:
I didn't have any subfolders setup when I ran it nor did I have multiple
*.pst files open. I'm going back to my originall opinion that it
shouldn't be this difficult to itierate through ALL folders (include
subfolders). I realized that I was wanting this to be as easy as looping
through the Nodes collection of a TreeView. I shouldn't have to use
multiple SUBs to get the list. If its a FOLDER it should be in the
FOLDERS collection without having to go another layer deeper.

Here's the code that I wrote somewhat inspired by the original...
Sub listOutlookFolders()

'Need to figure out a way to loop subfolders if FOLDERS.COUNT <> 0
'Will probably need to load up an array if multiple folders of the same
name exist
'but will probably just search that folder as well

Dim appOutlook As Outlook.Application
Dim nms As Outlook.NameSpace
Dim mapiParentFolder As Outlook.MAPIFolder
Dim targetFolders As Outlook.Folders
Dim i
Dim j

Set appOutlook = CreateObject("Outlook.Application")
Set nms = appOutlook.GetNamespace("MAPI")
Stop
For i = 1 To nms.Folders.Count
Debug.Print
"-------------------------------------------------------------"
Set mapiParentFolder = nms.Folders(i)
Set targetFolders = mapiParentFolder.Folders
Debug.Print mapiParentFolder.Name
For j = 1 To targetFolders.Count
Call listSubFolders(targetFolders(j))
Next j
Debug.Print
"-------------------------------------------------------------"
Next i

Set targetFolders = Nothing
Set mapiParentFolder = Nothing
Set nms = Nothing
Set appOutlook = Nothing

End Sub
Sub listSubFolders(parentFolder As Object)

Dim i As Integer
Debug.Print parentFolder.Name, parentFolder.Folders.Count
If parentFolder.Folders.Count <> 0 Then
For i = 1 To parentFolder.Folders.Count
Call listSubFolders(parentFolder.Folders(i))
Next i
End If

End Sub


Michael said:
[top posting corrected]
Eric Legault [MVP - Outlook] wrote:

What exactly is your intent anyway? Loop n levels deep, or all the way
through? Either way, you have to design the code so that it is recursive - a
function calling itself, like my initial example.


To able to find a specific folder (regardless of n-level) for use by
couple of SUBS that create/delete AppointmentItems created from Access.
Currently, the SUBs add/delete AppointmentItems in the default Calendar
folder. Since the SUBS are currently dependent upon a specific folder,
the SUBS aren't as flexible as I would like. While they work for me, as
a single-user and developer, I would like to provide the users with the
ability to have the AppointmentItems placed in alternate calendar folders.


It seems to me that Eric's "RecurseFolders()" in his initial response
provides the necessary starting point. Have you tried it?
 
Yeah that's pretty much it. I had already factored in the issue with
multiple folders. The idea was to go through the folders and if the
folder name matched the only supplied (and probably has the correct
DefaultItemType(property name?) and .ItemCount > 0) then check the
folder. Since my last post, I'm now thinking about storing the target
folder EntryId in a table that I use for system/default values. From
there I'd use the .GetFolderByEntryId to grab it, if the method fails
then I'd use the .PickFolder method to have the user find the folder and
update the system/default table accordingly. The DB example doesnt'
really apply unless you have multiple tables storing the exact data
(breaking 1 of the basic rules of having a relational DB). At any rate
it has been a nice learing experience.
 
Back
Top