none
How to access Exchange Public folders programatically - C#

    Question

  • Hello,

    I need a routine that will obtain a count of items in a given Exchange Public Folder.  I'm not worried about how to write the code as much as the restrictions a C# routine would have.  Are there any?

    In other words, I already have a routine that gets the directory statistics such number of folders or files.... But when it comes to Public Folders located in Exchange, how do I identify the folder path?

    Below is a routine I'm currently using for normal folders and files:

    Thanks




    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using System.Text;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using WriteToFile;



    namespace GetFileStats
    {

        public class FileStats
        {

            public int ProcessFile(string FilePath)
            {
                FileWriter FS = new FileWriter();
                string line;
                int linecount = 0;
                try
                {
                    //Pass the file path and file name to the StreamReader constructor
                    StreamReader SR = new StreamReader(FilePath);

                    //Read the first line of text
                    line = SR.ReadLine();

                    //Continue to read until you reach end of file
                    while (line != null)
                    {
                        linecount++;
                        line = SR.ReadLine();

                    }

                    //close the file
                    SR.Close();
                }
                catch (Exception e)
                {
                    FS.WriteToFile("c:\\FileWriterErrors.txt", "Bad File");
                }
                return linecount;
            }


            public void ProcessFolder(string SourceDir, int RecursionLvl,
                                    ref int TotalNumOfLines, int HowDeepToScan)
            {
                int FilePathLen = 0;
                int CurrentOfLines = 0;
                FileWriter FW = new FileWriter();

                //ArrayList DirList = new ArrayList();

                if (RecursionLvl <= HowDeepToScan)
                {
                    // Process the list of files found in the directory.
                    string[] FileEntries = Directory.GetFiles(SourceDir);
                    foreach (string FilePath in FileEntries)
                    {
                        // do something with fileName; i.e. dirList.Add(fileName);

                        FilePathLen = FilePath.Length;
                        if (String.Compare(FilePath.Substring(FilePathLen - 3, 3), "xml") == 0) // ********* whatever file necessary identified here *************
                        {
                            CurrentOfLines = ProcessFile(FilePath);
                            TotalNumOfLines += CurrentOfLines;
                            FW.WriteToFile("C:\\ZFileCount.txt", "Total lines:  \t" + CurrentOfLines.ToString() + "  from file: \t\t" + FilePath);

                        }
                    }
                    // Recurse into subdirectories of this directory.
                    string[] SubdirEntries = Directory.GetDirectories(SourceDir);
                    foreach (string SubDir in SubdirEntries)
                        // Do not iterate through reparse points
                        if ((File.GetAttributes(SubDir) &
                             FileAttributes.ReparsePoint) !=
                                 FileAttributes.ReparsePoint)

                            ProcessFolder(SubDir, RecursionLvl + 1, ref TotalNumOfLines, HowDeepToScan);
                }
            }
        }
    }

     

    Saturday, July 12, 2008 5:19 PM

Answers

  • The best I can recommend, really, is that you follow the instructions provided in the MSDN article for adding the web references. "Web Services On The Local Machine" is not what you want to do.

     

    You will need to know the fully qualified name of your exchange server, there is no way around it. This is true for adding the web reference, but it's also true when actually writing code against EWS. Have you setup the Exchange server yourself? If you have, then you are probably the one who knows what the server name is. Otherwise, you will probably want to ask your administrator.

     

    When you use "Microsoft.Office.Outlook.Interop", you are using the Outlook Object Model, which means your application will only work on computers on which Outlook is installed. The Outlook Object Model, even if you are still using a version of Outlook prior to 2007, should work just fine against Exchange 2007.

     

    I still recommend you use EWS. Adding the web reference is very easy as long as you know your server name or the public facing URL of EWS in case you are developing from outside your company's internal network (again, ask your administrator).

     

    Friday, July 18, 2008 1:02 AM

All replies

  • Hello,

     

    Exchange Public Folders are not file system folders. You cannot access them using file system specific routines; they don't have a physical path.

     

    To access Exchange Public Folders, I recommend you use Exchange Web Services. You can retrieve the Public Folder hierarchy using the FindFolder method and access items in the Public Folders using the FindItem method.

     

    To use Exchange Web Services in your C# application, you will probably want to use the "Add Web Reference" feature of Visual Studio 2005 (or "Add Service Reference" feature of Visual Studio 2008) to generate proxy classes that handle the communication with Exchange Web Services. This article describes how to use the "Add Web Reference" feature in VS 2005.

     

    Saturday, July 12, 2008 6:40 PM
  • Note that Exchange Web Services are only available in Exchange 2007. If your are using a previous version of Exchange, I recommend you take a look at WebDAV.

     

    Saturday, July 12, 2008 6:49 PM
  •  

    Appreciate the help.  I took a look at the code for the FindFolder operation.  You wouldn't happen to know how the C# calls the code provided in their example?  I don't understand why if the Language Filter for that web site is set to C# the code provided ends up in  xml:

     

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns: soap="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <soap:Body>
        <FindFolder Traversal="Shallow" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
          <FolderShape>
            <t:BaseShape>Default</t:BaseShape>
          </FolderShape>
          <ParentFolderIds>
            <t: DistinguishedFolderId Id="inbox"/>
          </ParentFolderIds>
        </FindFolder>
      </soap:Body>
    </soap:Envelope>

     

    Could you drop a few lines of code to demonstrate how my C# application would use the code above?  Another thing I wonder about is the Exchange server access approach.  I guess the code above assumes you have already gained access to the server ... I'm not that far along yet.

     

    Thanks

     

     

    Sunday, July 13, 2008 1:12 PM
  • As I indicated in my previous answer, you will want to use the "Add Web Reference" feature of Visual Studio to generate proxy classes.

     

    Here is an example of calling the FindFolder method using the proxy classes. There is quite a bit of information and samples available on MSDN and the web in general.

    Monday, July 14, 2008 10:28 PM
  •  

    David,

     

    Do appreciate the help.  I added the Web Reference.

     

    It just seems odd to me that for the example you provided (a good one)  the code makes no mention of gaining access to the server.

     

    I need help on the theory of this stuff !  What exactly do you mean by proxy classes?  Does the use of a 'proxy' somehow over-ride the necessity for code which makes a request for access to the server?

     

    When I use Outlook I can clearly see the Public Folders available through the directory.  I can see these because I have logged on to Exchange.  How then could any application see these folders without a login?

     

    Although there is a lot of code at the link you pointed me to, can you narrow down my search if you happend to know what I need here?

     

    Once again, are you saying the 'FindFolder' example you pointed me to in your last post is all sufficient??  If so, I don't get it.

     

    Thanks

     

     

    Tuesday, July 15, 2008 7:39 PM
  •  

    I was wrong about having successfully added the Web Reference.  Take a look at this and please tell me what you think.

     

    using WHAT ???

     

    [SerializableAttribute]
    [XmlTypeAttribute(Namespace = "http://schemas.microsoft.com/exchange/services/2006/messages")]
    public class FindFolderType : BaseRequestType


    {   / *********  DOES THIS BRACKET GO HERE ?? *********  /

     

        static void FindFolder(ExchangeServiceBinding esb)
        {
            // Create the request and specify the traversal type.
            FindFolderType findFolderRequest = new FindFolderType();
            findFolderRequest.Traversal = FolderQueryTraversalType.Deep;

            // Define the properties to be returned in the response.
            FolderResponseShapeType responseShape = new FolderResponseShapeType();
            responseShape.BaseShape = DefaultShapeNamesType.Default;
            findFolderRequest.FolderShape = responseShape;

            // Identify which folders to search.
            DistinguishedFolderIdType[] folderIDArray = new DistinguishedFolderIdType[1];
            folderIDArray[0] = new DistinguishedFolderIdType();
            folderIDArray[0].Id = DistinguishedFolderIdNameType.inbox;

            // Add the folders to search to the request.
            findFolderRequest.ParentFolderIds = folderIDArray;

            // Restriction based on the folder display name.
            RestrictionType restriction = new RestrictionType();
            PathToUnindexedFieldType fldrRestriction = new PathToUnindexedFieldType();
            fldrRestriction.FieldURI = UnindexedFieldURIType.folderDisplayName;
            // Identify the folder name to restrict on.
            ContainsExpressionType contains = new ContainsExpressionType();
            contains.ContainmentMode = ContainmentModeType.Substring;
            contains.ContainmentModeSpecified = true;
            contains.ContainmentComparison = ContainmentComparisonType.IgnoreCase;
            contains.ContainmentComparisonSpecified = true;
            contains.Item = fldrRestriction;
            contains.Constant = new ConstantValueType();
            contains.Constant.Value = "SentOnlyToMe";
            restriction.Item = contains;
            findFolderRequest.Restriction = restriction;

            // Define the paging scheme for the result set.
            FractionalPageViewType fpvt = new FractionalPageViewType();
            fpvt.MaxEntriesReturned = 1;
            fpvt.MaxEntriesReturnedSpecified = true;
            fpvt.Numerator = 1;
            fpvt.Denominator = 4;
            findFolderRequest.Item = fpvt;

            try
            {
                // Send the request and get the response.
                FindFolderResponseType findFolderResponse = esb.FindFolder(findFolderRequest);

                // Get the response messages.
                ResponseMessageType[] rmta = findFolderResponse.ResponseMessages.Items;

                foreach (ResponseMessageType rmt in rmta)
                {
                    FindFolderResponseMessageType ffrmt = (rmt as FindFolderResponseMessageType);

                    FindFolderParentType ffpt = ffrmt.RootFolder;
                    BaseFolderType[] folders = ffpt.Folders;

                    foreach (BaseFolderType folder in folders)
                    {
                        // Check folder type
                        if (folder is CalendarFolderType)
                        {
                            CalendarFolderType fldr = (folder as CalendarFolderType);
                            // TODO: Handle calendar folder
                        }
                        else
                        {
                            // TODO: Handle folders, search folders, tasks folders,
                            // and contacts folder
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }     //  END OF FindFolder()


    }   // END OF CLASS

    Tuesday, July 15, 2008 8:06 PM
  • Sorry, I need to take another look at the subject you provided:

     

    Creating a Proxy Reference by Using Visual Studio 2005

     

    Tuesday, July 15, 2008 8:20 PM
  • Jon,

     

    You do need to log into the Exchange server. Here is the base of the code you need to write to do what you want. I think that adding a web reference is already well covered on MSDN, so thie below will assume that you have do it. Let me know if you need additional information.

     

    Code Snippet

    // First, you need an instance of the ExchangeServiceBinding class.

    // All the methods available on EWS are exposed on this class.

    ExchangeServiceBinding serviceBinding = new ExchangeServiceBinding();

     

    // Then indicate that you want to use the Exchange SP1 version of EWS

    serviceBinding.RequestServerVersionValue = new RequestServerVersion();

    serviceBinding.RequestServerVersionValue.Version =

    ExchangeVersionType.Exchange2007_SP1;

     

    // Then, you need to set the URL of the service binding. Typically,

    // the URL is https://<your server>/EWS/Exchange.asmx

    serviceBinding.Url = "https://<your server>/EWS/Exchange.asmx";

     

    // Then you need credentials to authenticate against (log into) EWS.

    serviceBinding.Credentials = new NetworkCredential("username", "password", "domain");

     

    // Then you can call the FindFolder method. Let's retrieve the list of

    // all the Public Folders available under the Public Folder's root.

    // Create the request. You need to call serviceBinding.FindFolder, and that

    // method takes a parameter of type FindFolderType, so we need an instance of

    // that type.

    FindFolderType request = new FindFolderType();

     

    // Set the "FolderShape" property. This indicates what properties you want returned

    // on the folders that are found by FindFolder. Here we are requesting all the

    // available properties.

    request.FolderShape = new FolderResponseShapeType();

    request.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties;

     

    // Set the ParentFolderIds property. This indicates the parent folders under which

    // you want to search for folders. Here, the Public Folders' root.

    DistinguishedFolderIdType publicFoldersRootId = new DistinguishedFolderIdType();

    publicFoldersRootId.Id = DistinguishedFolderIdNameType.publicfoldersroot;

    request.ParentFolderIds = new BaseFolderIdType[] { publicFoldersRootId };

     

    // Then set the Traversal property. This indicates whether you want to retrieve

    // only the folders directly under a parent folder (Shallow) or the entire

    // hierarchy (Deep).

    request.Traversal = FolderQueryTraversalType.Shallow;

     

    // Then call the FindFolder method and store its result.

    FindFolderResponseType response = serviceBinding.FindFolder(request);

     

    // The response contains an array of response messages. There is one response message

    // per parent folder passed in the request. Here, we passed only one parent folder,

    // so there is only one response message.

    FindFolderResponseMessageType responseMessage =

    response.ResponseMessages.Items[0] as FindFolderResponseMessageType;

     

    // Look at the ResponseClass property of the response message. It tells you

    // whether the call succeeded or failed.

    if (responseMessage.ResponseClass == ResponseClassType.Success)

    {

    // If the call succeeded, dumpt the names of the folders that were found to

    // the console.

    foreach (FolderType folder in responseMessage.RootFolder.Folders)

    {

    Console.WriteLine(folder.DisplayName);

    }

    }

     

    Tuesday, July 15, 2008 11:01 PM
  • First off - thanks for the continued effort and all the comments to the code!!

     

    As I try to implement this, unfortunately I get stuck on step '2' of adding the web reference:

     

    http://msdn.microsoft.com/en-us/library/bb408520(EXCHG.80).aspx

     

    2)  Type the URL of the wsdl file that describes the location of the Client Access server that hosts Exchange Web Services; for example, https://myClientAccessServer.myDomain.com/EWS/Services.wsdl, where myClientAccessServer.myDomain.com is the fully qualified domain name (FQDN) of the Client Access server.

     

    What happens for me is that I get the same dialogue shown in the example and I am forced to choose this option because the other two don't produce anything:

     

    Web Services On the Local Machine

     

    When I click this link to perform analysis on my machine the one result that seems to work for me is:

     

    ReportingServices  http://localhost/ReportServer/ReportingServices.wsdl

     

    I then select this option to as my URL for the Client Access server but I get this error:

     

    localhost/ReportServer - /

     

    In short, my current application is using 'Microsoft.Office.Interop.Outlook' to do all mail transactions.  However, the new feature that I'm trying to add will support operations with Exchange 2007 and that is good.  Why?  Because we have recently migrated from 2003 to 2007.

     

    As the new developer here  -  I'm not quite sure how the application survives doing current email notification since apparently we have made the switch to 2007.  Do you happen to know whether 'Microsoft.Office.Interop.Outlook' classes supports 2007?

     

    Apparantly, this is the ticket:

     

    Microsoft.Office.Interop.Outlook.OlDefaultFolders MyFolder = new OlDefaultFolders();

     

    This compiles in my project but I have the feeling I should plug on through with your suggestion to use Web Services.

     

    When it comes to the code you provided, you wrote:

     

    // All the methods available on EWS are exposed on this class.

     

    ExchangeServiceBinding serviceBinding = new ExchangeServiceBinding();

     

    Getting this class recognized by my application is still a mystery.

     

    Thanks! 

     

     

    Thursday, July 17, 2008 9:16 PM
  • The best I can recommend, really, is that you follow the instructions provided in the MSDN article for adding the web references. "Web Services On The Local Machine" is not what you want to do.

     

    You will need to know the fully qualified name of your exchange server, there is no way around it. This is true for adding the web reference, but it's also true when actually writing code against EWS. Have you setup the Exchange server yourself? If you have, then you are probably the one who knows what the server name is. Otherwise, you will probably want to ask your administrator.

     

    When you use "Microsoft.Office.Outlook.Interop", you are using the Outlook Object Model, which means your application will only work on computers on which Outlook is installed. The Outlook Object Model, even if you are still using a version of Outlook prior to 2007, should work just fine against Exchange 2007.

     

    I still recommend you use EWS. Adding the web reference is very easy as long as you know your server name or the public facing URL of EWS in case you are developing from outside your company's internal network (again, ask your administrator).

     

    Friday, July 18, 2008 1:02 AM