Search Gmail message id

Gmail provides a unique message ID for each email so that a unique message may be identified across multiple folders.

Retrieval of this message ID is supported via the X-GM-MSGID attribute on the FETCH command.

The message ID is a 64-bit unsigned integer.

The X-GM-MSGID attribute may also be used in the SEARCH command to find the UID of a message given Gmail’s message ID.

// C# version

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.UseBestLogin("pat@gmail.com", "password");

    // Select 'All Mail' folder
    CommonFolders common = new CommonFolders(client.GetFolders());
    client.Select(common.AllMail);

    long newestMessageUID = imap.GetAll().Last();
    var messageId = imap.GetEnvelopeByUID(newestMessageUID).GmailMessageId;

    List<long> uids = imap.Search().Where(
        Expression.GmailMessageId(messageId));

    MessageInfo info = imap.GetMessageInfoByUID(uids.First());

    Console.WriteLine("{0} - {1}",
        info.Envelope.GmailMessageId,
        info.Envelope.Subject);

    imap.Close();
}
' VB.NET version

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.UseBestLogin("pat@gmail.com", "password")

	' Select 'All Mail' folder
	Dim common As New CommonFolders(client.GetFolders())
	client.Select(common.AllMail)

	Dim newestMessageUID As Long = imap.GetAll().Last()
	Dim messageId = imap.GetEnvelopeByUID(newestMessageUID).GmailMessageId

	Dim uids As List(Of Long) = imap.Search().Where( _
		Expression.GmailMessageId(messageId))

	Dim info As MessageInfo = imap.GetMessageInfoByUID(uids.First())

	Console.WriteLine("{0} - {1}", _
            info.Envelope.GmailMessageId, _
            info.Envelope.Subject)

	imap.Close()
End Using

You can learn more about this Gmail IMAP extension here:
http://code.google.com/apis/gmail/imap/#x-gm-msgid

Search Gmail thread id

Gmail provides a thread ID to associate groups of messages in the same manner as in the Gmail web interface.

Retrieval of this thread ID is supported via the X-GM-THRID attribute on the FETCH command.

The thread ID is a 64-bit unsigned integer.

The X-GM-THRID attribute may also be used in the SEARCH command to find the UIDs of messages in a given thread.

// C# version

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.UseBestLogin("pat@gmail.com", "password");

    // Select 'All Mail' folder
    CommonFolders common = new CommonFolders(client.GetFolders());
    client.Select(common.AllMail);

    long newestMessageUID = imap.GetAll().Last();
    var threadId = imap.GetEnvelopeByUID(newestMessageUID).GmailThreadId;

    List<long> uids = imap.Search().Where(
        Expression.GmailThreadId(threadId));

    foreach (MessageInfo info in imap.GetMessageInfoByUID(uids))
    {
        Console.WriteLine("{0} - {1}",
            info.Envelope.GmailThreadId,
            info.Envelope.Subject);
    }

    imap.Close();
}
' VB.NET version

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.UseBestLogin("pat@gmail.com", "password")

	' Select 'All Mail' folder
	Dim common As New CommonFolders(client.GetFolders())
	client.Select(common.AllMail)

	Dim newestMessageUID As Long = imap.GetAll().Last()
	Dim threadId = imap.GetEnvelopeByUID(newestMessageUID).GmailThreadId

	Dim uids As List(Of Long) = imap.Search().Where( _
		Expression.GmailThreadId(threadId))

	For Each info As MessageInfo In imap.GetMessageInfoByUID(uids)
	    Console.WriteLine("{0} - {1}", _
                info.Envelope.GmailThreadId, _
                info.Envelope.Subject)
	Next

	imap.Close()
End Using

You can learn more about this Gmail IMAP extension here:
code.google.com/apis/gmail/imap/#x-gm-thrid

Get Gmail message id

Gmail provides a unique message ID for each email so that a unique message may be identified across multiple folders.

Retrieval of this message ID is supported via the X-GM-MSGID attribute on the FETCH command.

The message ID is a 64-bit unsigned integer.

// C# version

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.UseBestLogin("pat@gmail.com", "password");

    imap.SelectInbox();

    List<long> uids = imap.GetAll();
    List<messageInfo> infos = imap.GetMessageInfoByUID(uids);

    foreach (MessageInfo info in infos)
    {
        Console.WriteLine("{0} - {1}",
            info.Envelope.GmailMessageId,
            info.Envelope.Subject);
    }

    imap.Close();
}
' VB.NET version

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.UseBestLogin("pat@gmail.com", "password")

	imap.SelectInbox()

	Dim uids As List(Of Long) = imap.GetAll()
	Dim infos As List(Of MessageInfo) = imap.GetMessageInfoByUID(uids)

	For Each info As MessageInfo In infos
	    Console.WriteLine("{0} - {1}", _
                info.Envelope.GmailMessageId, _
                info.Envelope.Subject)
	Next

	imap.Close()
End Using

You can learn more about this Gmail IMAP extension here:
http://code.google.com/apis/gmail/imap/#x-gm-msgid

Get Gmail thread id

Gmail provides a thread ID to associate groups of messages in the same manner as in the Gmail web interface.

Retrieval of this thread ID is supported via the X-GM-THRID attribute on the FETCH command.

The thread ID is a 64-bit unsigned integer.

// C# version

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.UseBestLogin("pat@gmail.com", "password");

    imap.SelectInbox();

    List<long> uids = imap.GetAll();
    List<messageInfo> infos = imap.GetMessageInfoByUID(uids);

    foreach (MessageInfo info in infos)
    {
        Console.WriteLine("{0} - {1}",
            info.Envelope.GmailThreadId,
            info.Envelope.Subject);
    }

    imap.Close();
}
' VB.NET version

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.UseBestLogin("pat@gmail.com", "password")

	imap.SelectInbox()

	Dim uids As List(Of Long) = imap.GetAll()
	Dim infos As List(Of MessageInfo) = imap.GetMessageInfoByUID(uids)

	For Each info As MessageInfo In infos
	    Console.WriteLine("{0} - {1}", _
                info.Envelope.GmailThreadId, _
                info.Envelope.Subject)
	Next

	imap.Close()
End Using

You can learn more about this Gmail IMAP extension here:
code.google.com/apis/gmail/imap/#x-gm-thrid

INotifyPropertyChanged with custom targets

Wouldn’t it be nice to have a simple attribute instead of backing field madness in Silverlight/WPF? Just like this:

public class MainViewModel : ViewModelBase
{
    [NotifyPropertyChanged]
    public string Title { get; set; }
}

You can use PostSharp for that, you should at least use lambda expressions instead of strings.

Here’s how to do it without 3rd party software:

1.
In your project declare base view model:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged
        = delegate { };

    protected void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged(
            this,
            new PropertyChangedEventArgs(propertyName));
    }
};

2.
Declare the attribute

It will be used to mark properties that should raise the PropertyChanged event.

[AttributeUsage(AttributeTargets.Property)]
public class NotifyPropertyChangedAttribute : Attribute
{
}

3.
Create new MSBuildTasks library project (it can be in different solution)

Add references to:

  • Mono.Cecil.dll
  • Mono.Cecil.Pdb.dll (this needed so Cecil can updated pdb file, which is need for debugging the modified assembly)
  • MSBuild assemblies

4.
Create new MSBuild task

It will inject PropertyChanged event invocation on properties marked with NotifyPropertyChanged attribute.

using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace MSBuildTasks
{
    public class NotifyPropertyChangedTask : Task
    {
        public override bool Execute()
        {
            InjectMsil();
            return true;
        }

        private void InjectMsil()
        {
            var assemblyDefinition = AssemblyDefinition.ReadAssembly(
                AssemblyPath,
                new ReaderParameters { ReadSymbols = true });

            var module = assemblyDefinition.MainModule;

            foreach (var type in module.Types)
            {
                foreach (var prop in type.Properties)
                {
                    foreach (var attribute in prop.CustomAttributes)
                    {
                        string fullName = attribute.Constructor.DeclaringType.FullName;
                        if (fullName.Contains("NotifyPropertyChanged"))
                        {
                            InjectMsilInner(module, type, prop);
                        }
                    }
                }
            }
            assemblyDefinition.Write(
                this.AssemblyPath,
                new WriterParameters { WriteSymbols = true });
        }

        private static void InjectMsilInner(
            ModuleDefinition module,
            TypeDefinition type,
            PropertyDefinition prop)
        {
            var msilWorker = prop.SetMethod.Body.GetILProcessor();
            var ldarg0 = msilWorker.Create(OpCodes.Ldarg_0);

            MethodDefinition raisePropertyChangedMethod =
                FindRaisePropertyChangedMethod(type);
            if (raisePropertyChangedMethod == null)
                throw new Exception(
                    "RaisePropertyChanged method was not found in type "
                    + type.FullName);

            var raisePropertyChanged = module.Import(
                raisePropertyChangedMethod);
            var propertyName = msilWorker.Create(
                OpCodes.Ldstr,
                prop.Name);
            var callRaisePropertyChanged = msilWorker.Create(
                OpCodes.Callvirt,
                raisePropertyChanged);

            msilWorker.InsertBefore(
                prop.SetMethod.Body.Instructions[
                    prop.SetMethod.Body.Instructions.Count - 1],
                ldarg0);

            msilWorker.InsertAfter(ldarg0, propertyName);
            msilWorker.InsertAfter(
                propertyName,
                callRaisePropertyChanged);
        }

        private static MethodDefinition FindRaisePropertyChangedMethod(
            TypeDefinition type)
        {
            foreach (var method in type.Methods)
            {
                if (method.Name == "RaisePropertyChanged"
                    &&  method.Parameters.Count == 1
                    &&  method.Parameters[0].ParameterType.FullName
                        == "System.String")
                {
                    return method;
                }
            }
            if (type.BaseType.FullName == "System.Object")
                return null;
            return FindRaisePropertyChangedMethod(
                type.BaseType.Resolve());
        }

        [Required]
        public string AssemblyPath { get; set; }
    }
}

5.
Compile the task assembly,

and copy it to “$(SolutionDir)/../lib/MSBuild/MSBuildTasks.dll” folder along with Mono.Cecil.dll and Mono.Cecil.Pdb.dll assemblies.

4.
Finally modify your Silverlight/WPF project (.csproj file):

<project>
  ...
  <usingTask TaskName="MSBuildTasks.NotifyPropertyChangedTask" AssemblyFile="$(SolutionDir)..libMSBuildMSBuildTasks.dll" />
  <target Name="AfterCompile">
    <msbuildTasks.NotifyPropertyChangedTask AssemblyPath="$(ProjectDir)obj$(Configuration)$(TargetFileName)" />
  </target>
</project>

Voila! Enjoy!

Here’s the source code of the MSBuildTasks project:
MSBuildTasks