locked
vb.net with reference to Outlook: create contacts and groups RRS feed

  • Question

  • All,

      I created a vb.net application that reads a data file (SQL table) and creates contacts and groups from this data. If I run this application and point the Contact folder to my local Outlook folders, the application runs fine. It creates about 550 contact and 10 groups. My problem is when I go to run the application against a public folder that am the "owner" of with full permissions it will run fine until it hits items 250 and then I get a COM error.

    If anyone with more experience, can give me any insight as to why it works against my local Contact folder and not completely against the Public Folder, I'd appreciate it.

    Here is the code segment that I am running from my vb.net console app:

    Dim ObjAPP As outlook.Application

    Dim objSystem_TaskFolder As outlook.MAPIFolder

    Dim objMoveFolder As outlook.MAPIFolder

    Dim objNewContact As outlook.ContactItem

    Dim objNewDistList As outlook.DistListItem

    Dim objNs As outlook.NameSpace

    Dim myRecipients As outlook.Recipients

    Dim myTempItem As outlook.MailItem

     

    ObjAPP = CreateObject("Outlook.Application")

    objNs = ObjAPP.GetNamespace("MAPI")

    objSystem_TaskFolder = objNs.GetDefaultFolder(outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders).Folders("Agency Contacts").Folders("Test - ACR")

     

    objNewDistList = objSystem_TaskFolder.Items.Add(outlook.OlItemType.olDistributionListItem)

    myTempItem = ObjAPP.CreateItem(outlook.OlItemType.olMailItem)

    myRecipients = myTempItem.Recipients

     

     

    For i = 0 To ds.Tables(0).Rows.Count - 1

        objNewContact = objSystem_TaskFolder.Items.Add

        objNewContact.FullName = “fullname”

        objNewContact.Email1DisplayName = “display name”

        objNewContact.Email1Address = “something@somthing.com”

     

        objNewContact.Save()

        objNewContact = Nothing

    Next

     

    The error message I receive is as follows:

    at system.threading.threadhelper.threadstart()</StackTrace><ExceptionString>System.Runtime.InteropServices.COMException (0x80004005): The operation failed.

    at Microsoft.office.interop.outlook._ContactItem.Save()

     

     


    Thomas
    Thursday, June 30, 2011 8:12 PM

Answers

  • All,

    I was able to resolve my problem using both the Marshall.ReleaseComObject and the GC.Collect method (garbage collection) within my for each loop. This seems to have freed up the memory that the "objects" were taking up.

    Here is a better explanation by Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007

    When accessing an Exchange connection you are generally limited to a maximum of 255 RPC channels, the setting is set in the registry of the server machine. Each Outlook object (NameSpace, Application, MailItem, etc. takes up 1 RPC channel.
      
    When you set the item to Nothing you are releasing it but normally the garbage collector won't run until some time after you exit the scope of the objects when the procedure ends. That means your RPC channels are not being freed and you eventually get an exception, usually an out of memory exception.
      
    The solution usually is to add a call to Marshal.ReleaseComObject() on the mail item in the loop to fully release it. It may also be necessary to call GC.Collect() and possibly GC.WaitForPendingFinalizers() every so often in the loop, perhaps every 200 loop passes or so.
      
    The other most common causes for cases where objects aren't or can't be released are:
      
    1. ForEach loops that create internal objects that cannot be released, use for loops with objects that can be released
    2. Use of compound dot operators instead of assigning an individual object that can be released.
    3. Declaring objects inside loops so each pass creates a new object instead of reusing one declared outside the loop.\
    Thanks to everyone for helping me get through my problem.
    Thomas

    Thomas
    Tuesday, July 5, 2011 11:04 PM

All replies

  • Hi Thomas,

    Welcome to the MSDN Forum.

    There is a limit in the public folder. And you can also try the outlook forum: http://social.msdn.microsoft.com/Forums/en-US/outlookdev/threads

    Best regards,


    Mike Feng [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    Monday, July 4, 2011 7:17 AM
    Moderator
  • Mike,

      Thanks for replying. When you say there is a "Limit in the Public Forum" do you mean that there is a limit to how many items (objects) can be created in the Public "Folder"?

    I found this link http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-vb/15235/InvalidCastException-with-VB-NET-2003-using-Outlook-2003 that references the same problem I am currently experiencing. It may have somthing to do with memory leaks. Tomorrow I'll run some test using the info from this link above.

    I'll also check out your link to the Outlook forums.

     

     

    Thanks,

    Thomas


    Thomas
    Monday, July 4, 2011 1:07 PM
  • Hi Thomas,

    Yes, that is my meaning, here is the KB: You receive a "This distribution list has reached the maximum size for your network e-mail server" error message when you try to add many items to a contact distribution list in Outlook:

    It shows the cause:

    This is a limitation of distribution lists that are created and stored in a Microsoft Exchange Server mailbox store or in a personal folders (.pst) file. There is no definite limit to the number of contacts that you can add to a distribution list. The exact limit is based on the total file size of the contacts.

    If you use only the very basic information for contacts, your distribution list limit may be as many as 125 contacts to 130 contacts. For example, the information for the contacts in a list of this size may include only the name and the e-mail address for each contact. You can create very large distribution lists that contain over a 1,000 contacts. However, these very large lists may not function as expected in Outlook.

    I hope this will be helpful.

    Best regards,


    Mike Feng [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, July 5, 2011 4:32 AM
    Moderator
  • All,

    I was able to resolve my problem using both the Marshall.ReleaseComObject and the GC.Collect method (garbage collection) within my for each loop. This seems to have freed up the memory that the "objects" were taking up.

    Here is a better explanation by Ken Slovak
    MVP - Outlook
    http://www.slovaktech.com
    Author: Professional Programming Outlook 2007

    When accessing an Exchange connection you are generally limited to a maximum of 255 RPC channels, the setting is set in the registry of the server machine. Each Outlook object (NameSpace, Application, MailItem, etc. takes up 1 RPC channel.
      
    When you set the item to Nothing you are releasing it but normally the garbage collector won't run until some time after you exit the scope of the objects when the procedure ends. That means your RPC channels are not being freed and you eventually get an exception, usually an out of memory exception.
      
    The solution usually is to add a call to Marshal.ReleaseComObject() on the mail item in the loop to fully release it. It may also be necessary to call GC.Collect() and possibly GC.WaitForPendingFinalizers() every so often in the loop, perhaps every 200 loop passes or so.
      
    The other most common causes for cases where objects aren't or can't be released are:
      
    1. ForEach loops that create internal objects that cannot be released, use for loops with objects that can be released
    2. Use of compound dot operators instead of assigning an individual object that can be released.
    3. Declaring objects inside loops so each pass creates a new object instead of reusing one declared outside the loop.\
    Thanks to everyone for helping me get through my problem.
    Thomas

    Thomas
    Tuesday, July 5, 2011 11:04 PM
  • Hi Thomas,

    Thank you for sharing your solution here.

    Best regards,


    Mike Feng [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, July 6, 2011 1:31 AM
    Moderator
  • New problem related to "resolving" recipient names from a recipient collection when adding contacts to a distribution List (Group)

    Running into a problem where my Recipients collection cannot resolve all the recipient names when adding them to a DistributionList.

    I have a SQL table that has a list of names, about 600 in all. Each name (person) already exist as a Contact in a folder under \Public Folders\Agency Contacts\Test – ACR.

    The code below is part of a vb.net console app that first creates all the contacts. Then for each set of names that are assigned to a specific group (distribution list), I then build each group.

    The problem I am having is that not all the names are resolving when I add then to the Distribution List (Group). The first thing I do is loop through the record set of all contact names for a specific group and ADD each name to the Recipients collection (myRecipients.add(“name”)). After all the names are added to the Recipient collection, I then create the distribution List Item, assign a name, and then add the recipients using objNewDistList.AddMembers(myRecipients) followed by            objNewDistList.Save().

    For some reason this does not appear to work for all contacts. I can’t figure out what is different between contacts that would cause this problem. I listed the code that builds/creates my contacts at the end of this post.

      

    Each contact has data in the following fields: Fullname, Company, Job Title, File As, E-mail, and Display as.

     

    Public Sub BuildContacts(ByVal mGroupID As Integer)

     

            'variables used for Outlook

            '---------------------------------------------------

            Dim ObjAPP As outlook.Application

            Dim objSystem_TaskFolder As outlook.MAPIFolder

            Dim objNewDistList As outlook.DistListItem

            Dim objNs As outlook.NameSpace

            Dim myRecipients As outlook.Recipients

            Dim myTempItem As outlook.MailItem

            '--------------------------------------------------

     

            'initialize variables

            '-----------------------------------------

            ObjAPP = CreateObject("Outlook.Application")

            objNs = ObjAPP.GetNamespace("MAPI")

    objSystem_TaskFolder =    objNs.GetDefaultFolder(outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders).Folders("Agency Contacts").Folders("Test - ACR")

            myTempItem = ObjAPP.CreateItem(outlook.OlItemType.olMailItem)

            myRecipients = myTempItem.Recipients

     

            '-----------------------------------------

     

            Dim connectionstring As String

    connectionstring = "Data Source=phlb-navtest01;Initial Catalog=AgencyEmailCenter;Integrated Security=True"

     

            Dim cnn As New SqlConnection

            Dim command As SqlCommand

            Dim adapter As New SqlDataAdapter

            Dim sql2 As String

            Dim ds As New DataSet

            Dim mGroupName As String

     

            'this select will list all the Contact and their associated group

            'in group id order

            '-----------------------------------------------------------------

            sql2 = "SELECT dbo.AgencyProgramRecords.Email_Address, dbo.AgencyProgramRecords.Record_Contact, dbo.Groups.Group_Name, dbo.Groups.Group_ID " & _

                   "FROm dbo.Groups INNER JOIN dbo.Contact_Groups ON dbo.Groups.Group_ID = dbo.Contact_Groups.Group_ID " & _

                   "INNER JOIN dbo.AgencyProgramRecords ON dbo.Contact_Groups.AgencyProgram_ID = dbo.AgencyProgramRecords.AgencyProgram_ID " & _

                   "where dbo.Groups.Group_ID = " & mGroupID & " " & _

                   "ORDER BY dbo.Groups.Group_Name"

            '----------------------------------------------------------------

            'use the connection function created above

            'this opens a connection to the appropriate database

            '--------------------------------------------------

            cnn.ConnectionString = connectionstring

            cnn.Open()

            Command = New SqlCommand(sql2, cnn)

            adapter.SelectCommand = command

            adapter.Fill(ds, "GroupsContacts")

     

     

            For i = 0 To ds.Tables(0).Rows.Count - 1

                mGroupName = “Name of Group”

                myRecipients.Add(“Name of Contact coming from dataset field 2”)

            Next

     

            If ds.Tables(0).Rows.Count > 0 Then

      objNewDistList = objSystem_TaskFolder.Items.Add(outlook.OlItemType.olDistributionListItem)

                objNewDistList.DLName = mGroupName & " - RB"

                objNewDistList.AddMembers(myRecipients)

                objNewDistList.Save()

                Marshal.ReleaseComObject(objNewDistList)

            End If

     

            'remove the dataset

            '------------------

            ds.Tables.Remove("GroupsContacts")

     

            Marshal.ReleaseComObject(myRecipients)

            Marshal.ReleaseComObject(myTempItem)

     

            GC.Collect()

            GC.WaitForFullGCApproach()

     

            cnn.Close()

     

        End Sub

    End Module

     

    Logic that build Contacts:

    objNewContact = objSystem_TaskFolder.Items.Add

                objNewContact.FullName = Trim(ds.Tables(0).Rows(i).Item(0))

                objNewContact.Email1DisplayName = Trim(ds.Tables(0).Rows(i).Item(0)) & _

                                                  " (" & Trim(ds.Tables(0).Rows(i).Item(1)) & ")"

                objNewContact.Email1Address = Trim(ds.Tables(0).Rows(i).Item(1))

                objNewContact.CompanyName = Trim(ds.Tables(0).Rows(i).Item(2))

                objNewContact.JobTitle = Trim(ds.Tables(0).Rows(i).Item(3))

                objNewContact.BusinessAddress = "Rule Based"

     

                objNewContact.Save()

                objNewContact.Move(objMoveFolder)

     

                Marshal.ReleaseComObject(objMoveFolder)

                Marshal.ReleaseComObject(objNewContact)

                Marshal.ReleaseComObject(objSystem_TaskFolder)

                objSystem_TaskFolder = objNs.GetDefaultFolder(outlook.OlDefaultFolders.olFolderContacts)

                objMoveFolder = objNs.GetDefaultFolder(outlook.OlDefaultFolders.olPublicFoldersAllPublicFolders).Folders("Agency Contacts").Folders("Test - ACR")

                GC.Collect()

                GC.WaitForFullGCApproach()

     

    If anyone has any ideas on what might be causing my problem, I'd love to hear what you have to say. At this point, I'm not sure what to look for, especially since all the contact cards/records in the Exchange  Public folder look the same.

    Thank you,

    Thomas


    Thomas
    Tuesday, July 12, 2011 5:28 PM
  • All,

    After spending some time writing various values to the console trying to debug this problem, it appears that the data, in this case the email address on the contact record is bad for a number of contacts. If I skip the Recipient.Add(...) for any email address that doesn't appear to be a valid email address (in structure, meaning it has certain symbols and doesn't contain strange characters), then everything works well.

    So, at this point, I need to go back to when the contact records are being created and NOT take for granted that the email address coming from the database table is in a valid email format.

    Problem resolved.

    Thank you,

    Thomas


    Thomas
    Tuesday, July 12, 2011 6:52 PM