none
Contact picture retrieved via PIA doesn't change when updated in Outlook RRS feed

  • Question

  • Outlook version: 15.0.4815.1002
    Microsoft.Office.Interop.Outlook version: 15.0.0.0
    DLL Runtime Version: v2.0.50727
    .NET Framework: 4.5.2

    It seems that, as long as the Outlook application is open on the PC, the ContactPicture.jpg attachment is cached in the COM objects. Is this a bug in PIA, or am I doing something wrong?

    Here is my code:

    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop.Outlook;
    using Application = Microsoft.Office.Interop.Outlook.Application;
    
    namespace OutlookImageTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                var app = new Application();
                var ns = app.GetNamespace("MAPI");
                var folder = ns.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
                var first = args[0].Split(' ')[0];
                var last = args[0].Split(' ')[1];
                var contact = (ContactItem) folder.Items.Find($"[FirstName] = '{first}' And [LastName] = '{last}'");
                var picture = contact.Attachments["ContactPicture.jpg"];
                picture.SaveAsFile(args[1]);
                Release(picture);
                Release(contact);
                Release(folder);
                Release(ns);
                Release(app);
            }
    
            private static void Release(object item)
            {
                Marshal.ReleaseComObject(item);
                while (Marshal.FinalReleaseComObject(item) != 0) {}
            }
        }
    }

    I would run this from the command prompt like so:

    > .\OutlookImageTest.exe "Fred Flintstone" C:\Temp\Fred1.jpg

    Here are the steps to reproduce the problem:

    1) Open Outlook and create a contact named Fred Flintstone

    2) Set Fred's contact image to Image1, save and close the contact, and don't close Outlook

    3) Run OutlookImageTest.exe, saving the image to Fred1.jpg

    4) Edit Fred. Change the image to Image2, save and close the contact, and don't close Outlook

    5) Run OutlookImageTest.exe, saving the image to Fred2.jpg

    6) Edit Fred. Change the image to Image3, save and close the contact, and don't close Outlook

    7) Run OutlookImageTest.exe, saving the image to Fred3.jpg

    8) Close Outlook. Check the task manager to ensure it has exited.

    9) Re-open Outlook

    10) Run OutlookImageTest.exe, saving the image to Fred4.jpg

    Expected result:

    Fred1.jpg should match Image1
    Fred2.jpg should match Image2
    Fred3.jpg
     should match Image3
    Fred4.jpg
    should match Image3

    Actual result:

    Fred1.jpg matches Image1
    Fred2.jpg matches Image2
    Fred3.jpg
     matches Image2 <-- WRONG!
    Fred4.jpg 
    matches Image3

    This is very repeatable. Any recommendations?

     - danBhentschel

    Tuesday, May 17, 2016 2:05 PM

All replies

  • Hello Dan,

    You shouldn't use the following line of code:

    while (Marshal.FinalReleaseComObject(item) != 0) {}
    

    Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. This is particularly important if your add-in attempts to enumerate more than 256 Outlook items in a collection that is stored on a Microsoft Exchange Server. If you do not release these objects in a timely manner, you can reach the limit imposed by Exchange on the maximum number of items opened at any one time. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object.

    Note, you don't release an instance of the Items and Attachments class:

    var contact = (ContactItem) folder.Items.Find($"[FirstName] = '{first}' And [LastName] = '{last}'");
    var picture = contact.Attachments["ContactPicture.jpg"];

    The Items property returns an instance of the Items class which should be released after.

    The Attachments property returns an instance of the Attachments class and should be released too.


    [custom.development]

    Tuesday, May 17, 2016 4:44 PM
  • Thank you very much for your reply. Your recommendations make sense, however, I tried them, and there is absolutely no change in behavior from that described in my original post.

    Here is my new code after changes:

    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop.Outlook;
    using Application = Microsoft.Office.Interop.Outlook.Application;
    
    namespace OutlookImageTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                var app = new Application();
                var ns = app.GetNamespace("MAPI");
                var folder = ns.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
                var first = args[0].Split(' ')[0];
                var last = args[0].Split(' ')[1];
                var items = folder.Items;
                var contact = (ContactItem) items.Find($"[FirstName] = '{first}' And [LastName] = '{last}'");
                var attachments = contact.Attachments;
                var picture = attachments["ContactPicture.jpg"];
                picture.SaveAsFile(args[1]);
                Marshal.ReleaseComObject(picture);
                Marshal.ReleaseComObject(attachments);
                Marshal.ReleaseComObject(contact);
                Marshal.ReleaseComObject(items);
                Marshal.ReleaseComObject(folder);
                Marshal.ReleaseComObject(ns);
                Marshal.ReleaseComObject(app);
            }
        }
    }
     - danBhentschel

    Tuesday, May 17, 2016 5:05 PM
  • You need to set the object reference to null in C# too.

    Anyway, I'd suggest connecting to the running Outlook instance instead of creating a new Application instance. You can use the Marshal.GetActiveObject method for that. Frankly speaking, Outlook is a singleton and only one instance can be run. But who knows...


    [custom.development]

    Tuesday, May 17, 2016 8:12 PM
  • Thanks again. Even more good recommendations, but still no luck. Running my modified code through the previously documented procedure still produces the exact same results as before. Any other recommendations?

    Here is my code as it stands now:

    using System;
    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop.Outlook;
    using Application = Microsoft.Office.Interop.Outlook.Application;
    
    namespace OutlookImageTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                var app = Marshal.GetActiveObject("Outlook.Application") as Application;
                if (app == null)
                {
                    Console.WriteLine("Could not attach to Outlook. Starting new instance.");
                    app = new Application();
                }
                var ns = app.GetNamespace("MAPI");
                var folder = ns.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
                var first = args[0].Split(' ')[0];
                var last = args[0].Split(' ')[1];
                var items = folder.Items;
                var contact = (ContactItem) items.Find($"[FirstName] = '{first}' And [LastName] = '{last}'");
                var attachments = contact.Attachments;
                var picture = attachments["ContactPicture.jpg"];
                picture.SaveAsFile(args[1]);
                Marshal.ReleaseComObject(picture);
                picture = null;
                Marshal.ReleaseComObject(attachments);
                attachments = null;
                Marshal.ReleaseComObject(contact);
                contact = null;
                Marshal.ReleaseComObject(items);
                items = null;
                Marshal.ReleaseComObject(folder);
                folder = null;
                Marshal.ReleaseComObject(ns);
                ns = null;
                Marshal.ReleaseComObject(app);
                app = null;
            }
        }
    }

    NOTE that at no point during my testing did the "Could not attach..." message get printed to the screen, so it looks like I was successfully attaching to the active Outlook instance.

     - danBhentschel

    Wednesday, May 18, 2016 12:39 PM
  • If you get the message then you run Outlook with different privileges. For example, Outlook is run under a regular user account while your application is run with admin privileges.

    [custom.development]

    Wednesday, May 18, 2016 2:51 PM
  • Right, or Outlook isn't running at all, which is also a valid scenario with my production application. If this is the approach I go with, I'll handle that. For now, I'd like to focus on the issue at-hand, which is why I don't see updates to the contact image. I still haven't found a solution to this issue.

     - danBhentschel

    Wednesday, May 18, 2016 2:55 PM