You are currently browsing the monthly archive for October 2008.

A little trick i’ve used for quite some time…
 
If you want to test a windows service for instance, you can’t start it in the debugger. But I want to use the debugger.
 
Then you could write something like the code below in the Program.cs.
Insert an internal Start method in the service code that just calls the OnStart() method of your service.
 
Now for the magic… i can now easily switch the code blocks (first 2 lines and last 5 lines) by removing one of the first 2 asterix (*) or reinserting one.
 

        static void Main()

        {

/**/

            var service = new AgentService();

            service.Start();

/*/

            ServiceBase[] ServicesToRun;

            ServicesToRun = new ServiceBase[]

            {

                new AgentService()

            };

            ServiceBase.Run(ServicesToRun);

/**/

        }

 

Would become

 

        static void Main()

        {

/*/

            var service = new AgentService();

            service.Start();

/*/

            ServiceBase[] ServicesToRun;

            ServicesToRun = new ServiceBase[]

            {

                new AgentService()

            };

            ServiceBase.Run(ServicesToRun);

/**/

        }

 

 

 

Advertisements

I’ve been trying and failing for quite some time at creating my configuration based on values enterend through the installer interfaces… And today I finally succeeded.

Let me walk you through the hardships and how to get it to work.

Create a console application that displays a name from the configuration file. (pretty easy so far)

class Program

{

    static void Main(string[] args)

    {

        var config = MyConsoleApplicationSection.Load();

 

        Console.Out.WriteLine(“Name = {0}”, config.Name);

 

        Console.ReadLine();

    }

}

 

class MyConsoleApplicationSection : ConfigurationSection

{

    [ConfigurationProperty(“Name”, IsRequired = true)]

    public string Name

    {

        get { return this[“Name”].ToString(); }

        set { this[“Name”] = value; }

    }

 

    public MyConsoleApplicationSection(string name)

    {

        this.Name = name;

    }

 

    public MyConsoleApplicationSection() { }

 

    public static MyConsoleApplicationSection Load()

    {

        return (MyConsoleApplicationSection)ConfigurationManager.GetSection(“MyConsoleApplicationSection”);

    }

}

And now for the harder part, I want to create an installer and in this installer I want to set the name in a textbox.

Let us start by creating a new project and select a setup and deployment project | setup project.

I’ll name mine NameSetup and click on OK.  Next we add a Project output to the application folder, and we choose our program’s active configuration. Now do a right click on the Setup Project and select View | User Interface

View | User Interface

View | User Interface

Right click Install | Start in the treeview and Add Dialog | TextBoxes (A). You’ll see that we have to move textboxes (A) up 2 positions in order to be able to build the project so lets do that.

Now we change some of the properties on the TextBoxes (A) dialog.

Properties

Properties

Ok. The click work is almost finished…

Now click on the setup project again in your solution explorer, and select the View | Custom Actions

Rigth click on the Custom Actions Node (main) | Add Custom Action …

Select the application folder and choose the output.

On properties of the custom action that is added under the install folder we need to change the CustomActionData propertie value to ” /name=[EDITA1] ” (without quotes)

The EDITA1 value refers to the first textbox on the dialog we’ve just added. You can add other values in the same manner (like this /name=[EDITA1] /type=[EDITA2] /something=[EDITA3] … ‘catch my drift’)

Now our setup project is ready. We only have to do one more thing. Add a new component to the project you want to setup, the installer class. Add following code to the installer class:

  public override void Install(IDictionary stateSaver)

  {

      base.Install(stateSaver);

 

      var assemblyPath = Context.Parameters[“assemblyPath”];

      var name = Context.Parameters[“name”];

      var configuration = new MyConsoleApplicationSection(name);

      var conf = ConfigurationManager.OpenExeConfiguration(assemblyPath);

 

      conf.Sections.Add(“MyConsoleApplicationSection”, configuration);

      conf.Save(ConfigurationSaveMode.Modified);

  }

One more thing. Because there seems to be a bug or something in the OpenExeConfiguration, we can not read the MyConsoleApplicationSection in the installer class. I haven’t found a solution to this for now, but there is a workaround. Just remove the configuration section from your app.Config entirely (configsection part and MyConsoleApplicationSection). Now when we call the save method (like on the last line above) the file is saved correctly.

 

So thats about it for this post. If you have any remarks or improvements, or this has been a great help please let me know.

See y’all next time…

Welcome back,

Today I would like to look into creating a Distribution List through the Exchange Webservice. This is not as straight forward as creating a contact, but it should work none the less. According to this book and the documentation found on the internet, it not possible to accomplish this. But there is a way around. Lets take the scenic route to create a Distribution List J

Take a look at the code in the previous post. You can see that – in the method to create the contact – a CreateItemType is instantiated. If you look at the Items property of that variable you can see that the it expects an NonEmptyArrayOfAllItemsType which again has an Items Property. In that property we can store any ItemType we want except a Distribution List, as you can see here. But if we can not insert the DistributionList in a CreateItem, then how the f* are we going to get it to the exchange server. Well we are going to create an ItemType (base class) and use that to set all the properties needed so that exchange and outlook recognizes the Item as a distribution list. Because an ItemType is the base class, it is not provided with any of the properties we need to create a distribution list. That is where the Extended Properties come in handy. Everything needs to be set through those properties.

Here are some links with more information on the properties:

And you can also still use OutlookSpy which was linked in the previous post.
Now for some code.
The first thing I’d like to do is declare the path to extended field variables and set them to point to the properties we’re going to use. I’ll name them all starting with ptef so we can get the quickly through intellisense.

private static PathToExtendedFieldType ptefDisplayName =

new PathToExtendedFieldType

{

PropertyTag = “0x3001”,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefDistributionListName =

new PathToExtendedFieldType

{

PropertyId = 0x8053,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefFileUnder =

new PathToExtendedFieldType

{

PropertyId = 0x8005,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefMembers =

new PathToExtendedFieldType

{

PropertyId = 0x8055,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.BinaryArray

};

private static PathToExtendedFieldType ptefOneOffMembers =

new PathToExtendedFieldType

{

PropertyId = 0x8054,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.BinaryArray

};

Let’s take a look at these PathToExtendedFieldTypes:
  • ptefDisplayName: The name of the Distribution List
  • ptefDistributionListName: The name of the Distribution List
  • ptefFileUnder: Must be the same as the name of the Distribution List
  • ptefMembers:
    • The members extended property: This i a list of EntryIds of the objects corresponding to the members of the personal distribution list. Members of the personal distribution list can be other distribution lists, electronic addresses contained in a contact, global address list users or distribution lists, or one-off e-mail addresses. The format of each EntryId MUST be either a one-off EntryId or a Wrapped Entry Id. When setting this property, be sure to ensure its total size is less than 15,000 bytes. Since this property is a BinaryArray type it expects a string Array of Base64 strings. More info can be found in the Protocol Specifications on the MSDN site.
  • ptefOneOffMembers:
    • The property specifies the list of one-off EntryIds corresponding to the members of the personal distribution list. These one-off EntryIds encapsulate display names and e-mail addresses of the personal distribution list members. For each entry in this property, there should be an entry in the Members property. The size of this property must also be less than 15,000 bytes. Since this property is a BinaryArray type it expects a string Array of Base64 strings.
How the member entry id and the one off member entry id strings are built can also be found in the protocol specifications.
I’ll show you how I created the Member Entry Id:
First off you’ll need the ItemType of the contact we created in the previous post. To do that we can use the FindItemMethod or simply return the ItemType in the previous posts CreateContact method. So let us refactor that method a little bit:

private static ContactItemType CreateContact(string pGivenName, string pSurname, Gender pGender, string pTitle)

{

ContactItemType retval = null;

ContactItemType contact = new ContactItemType();

contact.GivenName = pGivenName;

contact.Surname = pSurname;

contact.DisplayName = contact.Subject = pGivenName + ” “ + pSurname;

contact.EmailAddresses = new EmailAddressDictionaryEntryType[1];

EmailAddressDictionaryEntryType email1 = new EmailAddressDictionaryEntryType();

email1.Key = EmailAddressKeyType.EmailAddress1;

email1.Value = “testUser@example.com”;

contact.EmailAddresses[0] = email1;

contact.ExtendedProperty = new ExtendedPropertyType[2];

ExtendedPropertyType gender = new ExtendedPropertyType();

gender.ExtendedFieldURI = new PathToExtendedFieldType();

gender.ExtendedFieldURI.PropertyTag = “0x3a4d”;

gender.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.Short;

gender.Item = ((int)pGender).ToString();

ExtendedPropertyType title = new ExtendedPropertyType();

title.ExtendedFieldURI = new PathToExtendedFieldType();

title.ExtendedFieldURI.PropertyTag = “0x3a45”;

title.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.String;

title.Item = pTitle;

contact.ExtendedProperty[0] = gender;

contact.ExtendedProperty[1] = title;

CreateItemType createItem = new CreateItemType();

createItem.Items = new NonEmptyArrayOfAllItemsType { Items = new[] { contact } };

DistinguishedFolderIdType folder = new DistinguishedFolderIdType();

folder.Id = DistinguishedFolderIdNameType.contacts;

TargetFolderIdType targetFolder = new TargetFolderIdType();

targetFolder.Item = folder;

createItem.SavedItemFolderId = targetFolder;

var response = esb.CreateItem(createItem);

//There is only one item in the createItem so the line below should work in this case

if (response.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success)

{

ItemInfoResponseMessageType rmt = (ItemInfoResponseMessageType)response.ResponseMessages.Items[0];

retval = (ContactItemType)rmt.Items.Items[0];

}

return retval;

}

To create a Distribution List with members you will need to find the EntryId when all we have is the ItemId and we want it in a Hex format. To achieve this I want to use the ConvertId Method on the ExchangeServiceBinding. The convertId Method accepts a ConvertIdType as a parameter. The ConvertIdType properties that are used here are the sourceIds and the destinationFormat. They kinda speak for themselves.
Lets take a look at some code again.

private static string CreateMemberEntryId(ItemType item)

{

byte[] retval = new byte[0];

string WrappedEntryIDPrefix = “00000000C091ADD3519DCF11A4A900AA0047FAA4C3”;

ConvertIdType convertReq = new ConvertIdType();

convertReq.DestinationFormat = IdFormatType.HexEntryId;

convertReq.SourceIds = new[]

{

new AlternateIdType()

{

Format = IdFormatType.EntryId,

Id = item.ItemId.Id

}

};

try

{

ConvertIdResponseType response = esb.ConvertId(convertReq);

ArrayOfResponseMessagesType aormt = response.ResponseMessages;

ResponseMessageType[] rmta = aormt.Items;

foreach (ConvertIdResponseMessageType resp in rmta)

{

if (resp.ResponseClass == ResponseClassType.Success)

{

ConvertIdResponseMessageType cirmt = resp;

AlternateIdType myId = (cirmt.AlternateId as AlternateIdType);

if (myId != null)

{

//Strip the last 48 bytes (96 chars) – this is actually the

folderId.

//Strip the first 7 bytes.

//then remove 1 byte right after the actual entryId (40 bytes)

//then the ChangeKey follows (in the rest of the string).

StringBuilder sb = new StringBuilder();

sb.Append(WrappedEntryIDPrefix);

sb.Append(“00000000”);

sb.Append(myId.Id.Substring(14, 84));

sb.Append(myId.Id.Substring(100, myId.Id.Length – 196));

retval = sb.ToString().ToByteArray();

}

}

else if (resp.ResponseClass == ResponseClassType.Error)

{

//TODO error Logging

}

else

{

//TODO warning logging

}

}

}

catch (Exception e)

{

//TODO error logging

}

return Convert.ToBase64String(retval);

}

You can see in previous code block that there is some comment:
Strip the last 48 bytes (96 chars) – this is actually the folderId.

Strip the first 7 bytes.

then remove 1 byte right after the actual entryId (40 bytes)

then the ChangeKey follows (in the rest of the string).

Don’t pay to much attention to this for the moment. If you want more information about this check out [MS-OXOCNTC]: Contact Object Protocol Specification.

The WrappedEntryIDPrefix is a fixed string except the last 2 characters. What it stands for you see in the protocol specification but in the MS-OXOMSG pdf.

They represent a bit settings which specifies what kind of email address the entry id is going to be. So if you change that you could be talking about another distribution list for instance.

We are going to use this MemberEntryId and Members extended property of a distribution list. As said before we’ll need the OneOffMembers entry’s also so we can put them in the same position as the MemberEntryId. The OneOffMembers entry is a bit less complicated.

private static string CreateOneOffMemberEntryId(ContactItemType contact)

{

var retval = new List<byte>();

var flags = Encoding.Unicode.GetBytes(“”);

var version = Encoding.Unicode.GetBytes(“”);

var pad = Encoding.Unicode.GetBytes(“”);

var muid = new byte[]

{

0x81, 0x2b, 0x1f, 0xa4, 0xbe,

0xa3, 0x10, 0x19, 0x9d, 0x6e,

0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02

};

var wFlags = new byte[] {0x01, 0x90};

var first = Encoding.Unicode.GetBytes(contact.Subject + “(“ + contact.EmailAddresses[0].Value + “)”);

var middle = Encoding.Unicode.GetBytes(“UNKNOWN”);

var last = Encoding.Unicode.GetBytes(contact.EmailAddresses[0].Value);

retval.AddRange(flags);

retval.AddRange(muid);

retval.AddRange(version);

retval.AddRange(wFlags);

retval.AddRange(first);

retval.AddRange(pad);

retval.AddRange(middle);

retval.AddRange(pad);

retval.AddRange(last);

retval.AddRange(pad);

return Convert.ToBase64String(retval.ToArray());

}

Here we only need some actual strings from the contact to create the One Off Members entry. All the values except for wFlags, first, middle and last have fixed values. The wFlags should be changed to 0x8001 if you want to specify a distribution list. So it should read like this:

var wFlags = new byte[] {0x01, 0x80};

var first = Encoding.Unicode.GetBytes(distributionlist.Subject);

var middle = Encoding.Unicode.GetBytes(“MAPIPDL”);

var last = Encoding.Unicode.GetBytes(“Unknown”);

Now to actually create a distribution list with members we’ll need to create an Item that has all the correct Extended Properties:

private static void CreateDistributionList(string name, string description, ContactItemType contact)

{

ItemType list = new ItemType();

list.ItemClass = “IPM.DistList”;

list.Subject = name;

list.Body = new BodyType { BodyType1 = BodyTypeType.Text, Value = description };

List<string> members = new List<string>();

List<string> oneOffMembers = new List<string>();

members.Add(CreateMemberEntryId(contact));

members.Add(CreateOneOffMemberEntryId(contact));

List<ExtendedPropertyType> eProps = new List<ExtendedPropertyType>();

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefDisplayName,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefDistributionListName,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefFileUnder,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefMembers,

Item = new NonEmptyArrayOfPropertyValuesType

{

Items = members.ToArray()

}

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefOneOffMembers,

Item = new NonEmptyArrayOfPropertyValuesType

{

Items = oneOffMembers.ToArray()

}

});

list.ExtendedProperty = eProps.ToArray();

}

I hope this was helpfull. Feel free to post comments and / or questions. I won’t bite

As you probably could see in the code this is not ready to ship…

There is alot more exception handling to be done and so forth, but in order to keep this document a bit more to the point I’ve skipped those parts.