How to get the last modification of a contact?

Topics: Developer Forum, User Forum
Apr 15, 2007 at 8:16 AM
Hi,

Is it possible to find out the last modification (DateTime) of a contact? How can it be done?

Cheers,
Marsupi
Coordinator
Apr 19, 2007 at 6:50 PM
Yes, though not directly. It depends on what information you're looking for.
If the contact is backed by a file and you just care about the last time it was modified on disk, you can get the information from the backing file, e.g.:

DateTime lastMod = File.GetLastWriteTime(contact.Path);

If you want something more granular, you can use the contact as a property collection and query for the modification dates of each property:

    // Assuming CreationDate is valid and likely less than other modification dates.
    DateTime lastMod = contact.CreationDate;
    foreach (ContactProperty prop in contact.GetPropertyCollection())
    {
        if (prop.ModificationDate > lastMod)
        {
            lastMod = prop.ModificationDate;
        }
    }

Do either of those work for you?
Coordinator
Apr 20, 2007 at 2:19 AM
Aside from the ways that you can do this, can you elaborate on why you need it? It's possible for me to add a "LastModified" property to the contact. Do you need that value specifically? Or do you want GetHashCode() to work so it can identify when a contact has changed from some snapshot? Or something else entirely?

Thanks,
-Joe
Apr 20, 2007 at 7:27 AM
Thanks for the fast answer.

I think both ways of getting a modification date will work for my needs. Due to the fact that the solution with the filesystem is related to file time data (i see problems with diffrent filesystems coming :-)) the solution 2 may be the better way to go, isn't it? I'll try that out soon.

I need the last modification date to find out what contacts have been modified between the last synchronisation and now. This could also be done by a hash code for each contact, but i prefer the solution with the last modification. Having a property in the contact would be nice...

Thanks, Marsupi
Coordinator
Apr 21, 2007 at 4:40 AM
For synchronization solution 2 is probably even better than having a LastModified property, since it can tell you which properties have been modified since your last sync time. Depending on the fidelity of your sync, you may choose to ignore changed properties that you don't care about. Let me know how it works out for you :)

I'll look into some designs that give you something like Contact.GetPropertyColllection(DateTime modifiedSince) , though it may be just as easy to do that with LINQ. Dunno... Feel free to open an issue against me if you want more functionality than is currently provided.
Apr 22, 2007 at 7:47 AM
For now i can work with the solution 2. If you have the time a property to get the last modification time of contact would be nice, but i can work with the given solution as well.

While working with the Contacts.NET i had two other issues.
1) The CollectionChanged event didn't work some how. The event is never triggered.
2) The Contacts.NET does only work in an STA thread. Unuckily some other parts i use require an MTA thread. Is there a way to get Contacts .NET running in an MTA thread? The other solution would be a shell around the Contacts .NET including the required STA thread, but i'd prefere another way. :-)

Cheers, Marsupi
Coordinator
Apr 23, 2007 at 6:17 AM
It's a requirement of the underlying IContact COM objects that not only are they used STA, but also that they're accessed only on the thread on which they were created. I made Contact and ContactManager extend DispatcherObject to help mitigate this for (I think) most scenarios - it should enable you to call contact methods from MTA threads, but you'll still need to have an STA thread for creating them.

You should see the CollectionChanged event raised when changes to contacts are committed to disk. I use pretty standard components for it. Can you send me some sample code that you expect to work?

Thanks,
-Joe
May 12, 2007 at 10:27 AM
Hi Joe

I'm back from other projects in my windows contacts project.

I managed it to have the access on WindowsContacts in a STA therad. It took me some time, but the code change was worth it. Actually my implementation synchronizes the contacts quite well with Outlook. Your library is quite nice to work with. Thnks for that!

But there are still some points left:

1) CollectionChanged
The CollectionChanged doesn't work in my implementation. Don't know why. This is not urgent for me, but "nice to have". I'll investigate that if i find the time. I hope i can send you some code then.

2) ID
For my Application it is important to have an ID which identifies an obejct over its lifetime. I thought the Id Property is exactlky what i'm looking for. Unluckily this Id changes if you change the name of the contact. The reason for it is that the id contains a GUID and the file path. When you change the name the file changes as well and with it the Id. :-( That's useless for my needs. I thing the GUID (windowsContact.ContactIds.Default) can fit my needs now. Don't know if is possible for you to change your id.

3) E-Mail Lables
This point isn't a point concerning the library. So there is probably nothing you can do on it. It's more a point for WindowsContacts itself. When i synchronize contact databases with Windows Contacts (or Outlook) i've the problem that this database doesn't differ between Home and Office E-Mailaddress. This makes it difficult to synchronize them. Don't know why Microsoft doesn't have E-Mail's which belong to a location (home or office).

But once more. You are doing a great job.

Cheers, Marsupi
May 12, 2007 at 2:50 PM
Hi again,

While doing some more tests another thing came up. When i set a Birthday (i guess Anniversary as well) with a DateTime (e.g. 25.07.1972 00:00:00) the date showed in the contact view wil be the 24.07.1972. I assume that ther is some Time Calculation (TimeZones???) going on. If i fake in a Timeshift of 2hous (which is exctliy my current shift to UTC) it eams to work. Can you check this for me? I thought that the time component of DateTime should be ignored in this case because it's a Date (without any time component).

Cheers, Marsupi
Coordinator
May 12, 2007 at 5:09 PM
Thanks for the feedback. Glad to hear that this is working for you :) I'll try to address your points.

post1 1) If the contact files under the ContactManager's root folder are changing then it should be working. The one thing I can think of that might make a difference is maybe messages aren't being pumped? Can you try calling System.Windows.Forms.Application.DoEvents() and see if the notifications get raised?

post1 2) The Id property has to stay unique for any contact in the address book. As you've noticed, it's a runtime Id that's composed basically of "/GUID:\"<contact.ContactIds.Default>\" /PATH:\"<contact.Path>\"". Since the ContactId collection is stored in the contact, this id needs to be resilient against users copy-pasting the contact files (e.g. using a contact as a template). There are heuristics the ContactManager has for finding the contact if the ID doesn't match exactly. This allows clients to merge contacts, and so long as the ContactIdCollection is also merged then references to the old contact will resolve to the new one.
So that's the background. I think for your case using ContactIds.Default is probably the right thing (and it's always guaranteed to be present), but you'll need to be aware that the user might have duplicated the contact or another program may have merged it with something else so the Id you're expecting is no longer "Default". Something that might help if you run into it, you can turn the guid into the above string format ("/PATH" part isn't required) and ContactId will find the contact for you if it still exists.

post1 3) The preview pane for Contacts in the Shell should respect labels on e-mail addresses. Part of the hope of this project is that someone will write a better (fuller fidelity) UI than Windows Contacts and register it as the handler for the .Contacts; the store is a lot more flexible than the UI. It doesn't really help for Outlook sync, but anything that's multi-valued you can label on the Windows Contacts side.

post2) Sounds like a bug. I'll look into it and get back to you. The contact view you're seeing it in is Windows Contacts, yea?

Cheers,
-Joe
May 12, 2007 at 10:49 PM
Very fast respond, thanks.

Post1 1) Yout guess was a 100Point shot. If i call the MessagePump manually the trigger works. I use the Contacts.NET dll from a STA thread which noramlly executes "Wait" and waits for trigger events. I thought "Wait" does some MeassagePump?

Post1 2) I'l use the ContactIds.Default. It works quite good with it. Thank you for the hint with the /GUID notation. This make life much easier for me.

Post2) The wrong date can be seen in the standard contact viewer.
Coordinator
May 12, 2007 at 11:33 PM

Marsupi wrote:
Post1 1) Yout guess was a 100Point shot. If i call the MessagePump manually the trigger works. I use the Contacts.NET dll from a STA thread which noramlly executes "Wait" and waits for trigger events. I thought "Wait" does some MeassagePump?


Not sure which Wait you're using or what native call it's wrapping. In the native world CoWaitForMultipleHandles behaves differently if you're in MTA or STA. When in STA it pumps at a really low priority. I've seen many many messages get queued up when the expectation was that it would handle them in the background. It may be reasonable to call DoEvents to clear the pump whenever your thread gets triggered, or add a timer to your trigger.
Coordinator
May 18, 2007 at 1:11 AM

Marsupi wrote:

Post2) The wrong date can be seen in the standard contact viewer.


I didn't realize DateTimeKind existed. I use DateTime.ToFileTime before writing to the XML and it's assuming that if the kind was unspecified that it's local rather than UTC. I should probably flip that assumption in this API, but I should also be explicit and careful when reading back the properties. I'll open a bug about it.

Thanks for reporting it!