Wednesday, December 28, 2011

Room Finder - Outlook 2007 Add-in

Often, when we want to book a meeting room, we end up gathering all the rooms in the vicinity, removing those that are already booked, and then narrowing down our choices based on our preferred rooms. This sounds like quite a lot of manual effort each time we want to book a room. Fortunately, the Outlook APIs are good enough to allow us to automate most of this.

Introducing, the Room Finder - an add-in for Outlook 2007 users.

Sources of inspiration:
1. A large retailer's IT company that I used to work with had a Room Finder utility added in Outlook. This was back in 2008. I wasnt allowed access to the source code, though. So, I had to create one on my own, based on my experiences as an end-user.

2. Outlook 2010 has this feature. So, for those who haven't got a chance to use Outlook 2010 yet, this is a feature that you would be using once the upgrade happens. Using this add-in in Outlook 2007 makes you get used to this feature.

Steps to create the Room Finder add-in:
1. Using Visual Studio 2010, create an Outlook 2007 Add-in project

2. Add an Outlook Form Region.

Name it RoomFinderFormRegion instead of the default FormRegion1.
How would you like to create this form region?
Select 'Design a new form region'
What type of form region do you want to create?
Select 'Adjoining' type. Replacement is not supported for built-in forms.
What name, title and description do you want to use?
Name it 'Room Finder'
In which display modes should this form region appear?
Turn on the first checkbox and turn off the other 2. We only want this to be available in compose mode.
Which standard message classes will display this form region?
Turn on the first one checkbox for Appointment. Turn off the others, including the default one for Mail Message. We only want this for Appointments / Meeting Requests.
We don't need to specify any custom message classes either.
Click on Finish
(Optional) If you used the default name of FormRegion1 and want to rename it to RoomFinderFormRegion, rename it on the file instead of the class. This keeps the file and the class name in sync. For sake of completeness, there are some more regions where this name would need to be udpated. Find and replace all in the solution for 'FormRegion1'. You would find a string literal, some comments, a corresponding factory class and event handlers where this needs to be updated.
(Note) A pfx key would also be added automatically to the project. You don't need to worry about that.

3. Open the RoomFinderFormRegion in design mode. Add a normal (Windows Forms) Button from the toolbox. Change the Name and Text properties of this button to 'btnSuggest' and 'Su&ggest me a room...' respectively. The & is obviously just to make the g a hot-key so that one can get to it sooner by using the Alt-G key combination.
Ensure that you reduce any whitespace on the form region, except for the button. Resize the form region vertically. Resizing horizontally is not required, as the form region would occupy the entire width of the appointment item anyway.

4. Add a button click event handler. Include the following code in the event-handler.
            try
            {
                var item = (Outlook.AppointmentItem)this.OutlookItem;
                var mapi = Globals.ThisAddIn.Application.GetNamespace("MAPI");
                mapi.Logon();
                // get appointment time info
                DateTime startOn = item.Start;
                DateTime endOn = item.End;
                //TODO: sort rooms based on floor, user preferences & people count
                string identifiedRoom = string.Empty;
                foreach (var room in rooms)
                {
                    //get meeting room's calendar properties
                    var resource = mapi.CreateRecipient(room);
                    bool isFree = resource.AddressEntry.GetFreeBusy(startOn, endOn);
                    if (isFree)
                    {
                        identifiedRoom = room;
                        break;
                    }
                }
                mapi.Logoff();
                if (!string.IsNullOrEmpty(identifiedRoom))
                {
                    item.Resources = identifiedRoom;
                    item.Location = identifiedRoom;
                }
                else
                {
                    MessageBox.Show("No rooms found for this time period! Please consider scheduling the appointment at another time.");
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(string.Format("Error: {0}", ex.ToString(), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation));
            }
This code won't compile yet. Complete the following steps for that.

5. Add the following class-level member variable to the RoomFinderFormRegion class.
        private string[] rooms = new string[] {
            "Conf Room 1",
            "Conf Room 2",
        };
The array needs to contain all the rooms that we are intersted in. For each room, the string can either be the display name, alias or the full SMTP e-mail address of the meeting room.

6. Add a using statement for the MessageBox.
You can see a few more that were automatically added to the RoomFinderFormRegion class. Use Remove and Sort to remove the redundant ones. You will find that these are the only ones we need for now:
using System;
using System.Windows.Forms;
using Outlook = Microsoft.Office.Interop.Outlook;

7. If you attempt to use the in-built AddressEntry.GetFreeBusy method, it doesn't feel a lot DotNetty. It returns a string, with each character being a free/busy indicator for a certain time interval, the default being 30 minutes. The appointment, on the other hand, already has the start and end datetime values specified. So, what we actual need is for the AddressEntry object to give us a method that would tell us if the resource (meeting room) is available during that time. Since the AddressEntry object doesnt have a method of that kind, we use extensions. And for that, we need to put that in a separate static class, as shown here.
    public static class AppointmentHelper
    {
        private const int DEFAULT_INTERVAL = 30;
        public static bool GetFreeBusy(this AddressEntry addressEntry, DateTime start, DateTime end)
        {
            string freeBusyInfo = addressEntry.GetFreeBusy(start.Date, DEFAULT_INTERVAL, false);
            int position = (int) start.TimeOfDay.TotalMinutes / DEFAULT_INTERVAL;
            TimeSpan ts = (end - start);
            int blocks = (int) ts.TotalMinutes / DEFAULT_INTERVAL;
            return freeBusyInfo.Substring(position, blocks).All(c => c.Equals('0'));
        }
    }

8. Build the code. Ensure that the Outlook client has been closed. You can now run/debug the code. When you create a new appointment, you can now see the new region at the bottom part of the item window.
Click on the button in this Room Finder region to invoke the code that looks for a room which is free during the time selected in the appointment. If no rooms could be identified, a corresponding message is shown. Once a room is identified from the order in which it is present in the array, the search is called off, and the room is added as a resource to the appointment item.

9. Notice that I have left a TODO comment in the code above, which states that another feature could be built into this to sort the rooms based on location/floor, user preferences & people count. For instance, you wouldn't want to book a room which is good enough for 28 people if only 3 of you are going to use it. All this logic would execute to give a sorted list of the rooms array mentioned earlier. The rest of the code remains the same.

10. It is typical to expect the list of rooms to be configured externally instead of being hard-coded as an array of strings.
Add an application configuration file to the project. Include a key named Rooms and a value having a semi-colon delimited string of conf rooms. This is what it would look like.
<configuration>
  <appSettings>
    <add key="Rooms" value="Conf Room 1; Conf Room 2"/>
  </appSettings>
</configuration>

Add a reference to System.Configuration, and add a using statement for the same namespace. Add the following code at the top of the button click event hander, making sure this is still within the try block.
                //TODO: provide a way to edit and store this from Outlook
                string roomsDelimited = ConfigurationManager.AppSettings["Rooms"];
                rooms = roomsDelimited.Split(';');
You can also remove the array initializer since it is now being done only on the button click. The array definition can remain there.
private string[] rooms;
Again, I have left a TODO comment here since managing rooms from Outlook (another button in the add-in, plus storing this data in the mailbox) would be a better approach than having to manage it thru the config file.

11. Note that once you start the project, it will publish this add-in to Outlook. Any changes that you make to the add-in reflect in Outlook during the next launch. Even if you are launching Outlook separately after that, these changes are reflected.
To remove a certain add-in locally, you can use Tools → Trust Center → Add-ins.
Click on Go, select the add-in you don't want anymore (ignore the checkboxes - those are only for enable/disable) and click on Remove.

12. To deploy/distribute & for others to install
Use the Publish wizard. This also creates the setup file.
I suggest publishing to a website so that you can put in updates to the same place.
The default behavior is for the add-in to check for updates every 7 days.

References:
http://msdn.microsoft.com/en-us/library/bb410039(v=office.12).aspx
http://www.eggheadcafe.com/microsoft/Outlook-Program-Forms/35554170/how-to-read-single-instance-of-recursive-meetings-in-outlook.aspx
http://www.tech-recipes.com/rx/1959/outlook_2007_disabling_enabling_add_ins/
http://www.codeproject.com/KB/office/Outlook_Add-in.aspx
http://msdn.microsoft.com/en-us/library/bb147704(v=office.12).aspx
http://msdn.microsoft.com/en-us/library/ms526723(v=exchg.10).aspx
http://support.microsoft.com/kb/310259

Tuesday, December 27, 2011

OWASP Top 10 for .NET Developers

http://www.troyhunt.com/2011/12/free-ebook-owasp-top-10-for-net.html

This article includes a link to a free eBook​ that includes content from all the related blog posts.

Excerpts from the author:
I knew – and many of us know – that unsalted hashes are vulnerable to a rainbow attack but I’d never actually executed one of these attacks myself. So I did. Same again on sniffing packets; knowing that lack of transport protection leaves network traffic vulnerable is one thing, sitting in the car outside McDonald’s and actually capturing wifi traffic and hijacking the session (my own, that is!) is another thing altogether.

My analysis:
Completed just the first topic so far. It's nice to see a step-by-step approach that shows the vulnerabilities being exploited, and ways to reduce those risks.

Monday, December 12, 2011

Ctrl-Alt-Del on remote desktop

Needed to change my password on a different domain, into which I remote login.
Ctrl-Alt-Del equivalent is Ctrl-Alt-End
source: https://www.google.com/#q=ctrl%20alt%20del%20remote%20desktop

More remote desktop keyboard shortcuts are listed here:

Remote desktop connection navigation

  • CTRL+ALT+END (Open the Microsoft Windows NT Security dialog box)
  • ALT+PAGE UP (Switch between programs from left to right)
  • ALT+PAGE DOWN (Switch between programs from right to left)
  • ALT+INSERT (Cycle through the programs in most recently used order)
  • ALT+HOME (Display the Start menu)
  • CTRL+ALT+BREAK (Switch the client computer between a window and a full screen)
  • ALT+DELETE (Display the Windows menu)
  • CTRL+ALT+Minus sign (-) (Place a snapshot of the entire client window area on the Terminal server clipboard and provide the same functionality as pressing ALT+PRINT SCREEN on a local computer.)
  • CTRL+ALT+Plus sign (+) (Place a snapshot of the active window in the client on the Terminal server clipboard and provide the same functionality as pressing PRINT SCREEN on a local computer.)
source: http://support.microsoft.com/kb/301583

Sunday, December 11, 2011

Thursday, December 8, 2011

FlowDocument issues

We created this service that would fire prints on a network printer. When tested for performance, the code showed signs of memory leaks. This was strange, because I was expecting the .Net's garbage collector to cleanup unused objects. Perhaps there were some objects that were left to be disposed.

So, as part of our analysis, using WinDebugger, a memory dump of the process was collected. Using DebugDiag, this dump was analyzed. Multiple objects of type FlowDoument (used in CreateXpsDoc) were found blocking the finalizer queue. Googling for this type led us to some similar problems with this type being discussed in tech forums. It seems FlowDocument uses System.Windows.Threading.Dispatcher to free all resources

Reference: http://stackoverflow.com/questions/952985/flowdocument-memory-issue-in-c-sharp

Solution:
Execute these lines of code between printing and deleting the xps file.
var dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
dispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.SystemIdle);
System.Windows.Threading.Dispatcher.Run();

Friday, December 2, 2011

Ascii Character as Symbols

I wanted to create a spreadsheet like this in Excel.



I figured I could use - and > for the right arrow to make it ->. But, what about the down arrow? Should I use a | and a V? That didn't feel good. So, I googled for "down arrow ascii" and it brought me to this page.
http://chexed.com/ComputerTips/asciicodes.php

This tells me I could use Alt-26 for the right arrow and Alt-25 for the down arrow. The results are for you to see. Nice. :)

Note: Use the number pad on the keyboard for the digits.