2010年8月

Serialization in .NET Programming

If you want to Binary or XML Serialization in .NET Programming and you are using C#, you can use following code.

using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web.Mail;

namespace npuInfoCS.Utilities
{
public class Serialization
{
public enum SerializationFormat {Xml, Binary}

public static System.Boolean SerializeToDisk(string path, object request, SerializationFormat format)
{
try
{
System.IO.MemoryStream oStream = Serialize(request, format);
if(System.IO.File.Exists(path)) System.IO.File.Delete(path);
System.IO.FileStream output = System.IO.File.Open(path, System.IO.FileMode.CreateNew);
output.Write(oStream.ToArray(),0,Convert.ToInt32(oStream.Length));
output.Close();
oStream.Close();
return true;
}
catch(Exception e)
{
return false;
}
}

public static object DeSerializeFromDisk(string path, SerializationFormat format)
{
try
{
if(System.IO.File.Exists(path))
{
System.IO.FileStream input = System.IO.File.Open(path, System.IO.FileMode.Open);
byte[] filecontents=new byte[Convert.ToInt32(input.Length)];
input.Read(filecontents, 0, Convert.ToInt32(input.Length));
input.Close();
//System.IO.File.Delete(path);
System.IO.MemoryStream inStream = new System.IO.MemoryStream(filecontents);
object newObj = DeSerialize(inStream, format);
inStream.Close();
return newObj;
}
else
{
return null;
}
}
catch(Exception e)
{
return null;
}
}

#region wrappers for binary and xml serialization
public static System.IO.MemoryStream Serialize(object request, SerializationFormat format)
{
if(format==SerializationFormat.Binary)
return SerializeBinary(request);
else
return SerializeSOAP(request);
}
public static object DeSerialize(System.IO.MemoryStream memStream, SerializationFormat format)
{
if(format==SerializationFormat.Binary)
return DeSerializeBinary(memStream);
else
return DeSerializeSOAP(memStream);
}
#endregion

#region Binary Serializers
public static System.IO.MemoryStream SerializeBinary(object request)
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, request);
return memStream;
}

public static object DeSerializeBinary(System.IO.MemoryStream memStream)
{
memStream.Position=0;
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
object newobj = deserializer.Deserialize(memStream);
memStream.Close();
return newobj;
}
#endregion

#region XML Serializers
public static System.IO.MemoryStream SerializeSOAP(object request)
{
System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, request);
return memStream;
}

public static object DeSerializeSOAP(System.IO.MemoryStream memStream)
{
object sr;
System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
memStream.Position=0;
sr = deserializer.Deserialize(memStream);
memStream.Close();
return sr;
}
#endregion

#region miscel
public static string ConvertStreamToString(System.IO.Stream theStream)
{
string theString="";
theStream.Position=0;
using (System.IO.StreamReader sr = new System.IO.StreamReader(theStream) )
{
theString = sr.ReadToEnd();
// Close and clean up the StreamReader
sr.Close();
}
return theString;
}
#endregion
}
}

Unable to get the project file from the Web server

My environment:
OS: Windows XP
Web Server: IIS 5
.NET Framework: 1.1
IDE: Visual Studio 2003

After moving or copying my code directory, when I tried to open the solution in the Visual Studio 2003, I got an error: "Unable to get the project file form the Web server" as following screenshot.

Unable to get the project file from the Web server

The reason is the Visual Studio cannot open the project file, which is http://localhost/projectName.csproj. I checked all settings of the IIS and permission of files and found nothing was wrong. After several hours struggling, I finally figured out following solution.

I deleted the all files under VSWebCache folder which is under C:\Documents and Settings\my_user_name\. The screenshot is as below. The possible reason is when I copied or moved the code folder, the previously generated runtime chache conflicts with the new code folder's location.

Clear the VSWebCache

The Open Source iPhone Apps List

Here is the open source iphone app list in alphabetical order:

1.ABC 123

Sequence memorization game.Utilizes Cocos2D.

(itunes link) (source code)

2.Artifice

Strategy game where you try to get to the other side by moving boxes out of the way.Utilizes Cocos2D.

(itunes link) (source code)

*3.Battle For Wesnoth

Fantasy themed turn based tactical RPG game available on several platforms and now the iPhone/iPad.

(itunes link) (source code)

4.Colloquy

Conversion of the most popular Mac IRC client to the iPhone.

(itunes link) (source code)

5. Countitout-

A generic counting app.

(itunes link) (source code)

6. Diceshaker-

Dice rolling simulator designed for role-playing game enthusiasts.

(itunes link) (source code)

7. Doom Classic-

Classic 3D first person shooter.

(itunes link) (source code) (build instructions)

8. Ecological Footprint

Calculate, display, and record your ecological footprint.

(itunes link) (source code)

9. Fosdem-

Calendar app for the Fosdem open source conference.

(itunes link) (source code)

10. Freshbooks

Open Source iPhone app that enables usage of Freshbooks web invoicing software from your iPhone.

(itunes link) (source code)

11. Gorillas

Classic Worms/iShoot turn based shooter type game converted to iPhone from basic. Utilizes Cocos2D.

(itunes link) (source code). 1249ouFh83XA

12. Go Go Lotto

Open source iPhone app for lotto ticket generation.

(itunes link) (source code)

13. iStrobe

Turns the iPhone 4 flash into a highly configurable strobe light.

(itunes link) (source code)

14. Last.fm

Software that enables usage of the Last.fm platform for personal radio stations.

(itunes link) (source code)

15. Mobilesynth-

A monophonic synthesizer designed for live performance.

(itunes link) (source code)

16. Molecules

Allows you to view 3D models of molecules and manipulate them through touch.

(itunes link) (source code)

17. Mover

Allows you to transfer stuff from one iPhone to another by “flicking” it to the other device.

(itunes link) (source code)

18. Natsulion-

A basic twitter client converted from mac.

(itunes link) (source code)

19. NevoChess

A Xiangqi game.

(itunes link) (source code)

20. NowPlaying

Allows you to check local theater listings, and check rotten tomato ratings.

(itunes link) (source code)

21. Packlog

Backpack journal client.

(itunes link) (source code)

22. PlainNote

Simple Open Source Notepad.

(itunes link) (source code)

23. PocketFlix

Find movies, and manage your Netflix information.

(itunes link) (source code)

24. Puff Puff

Beautiful underwater 2D game utilizing Cocos2D and Openfeint featuring unique physics based gameplay.

(itunes link) (source code)

25. reMail

E-mail client featuring ultra-fast search. Removed from app store, but source made available.

(source code)

26. RobotFindsKitten

Port of a very silly “classic” ASCII game.

(itunes link) (source code)

27. Sci-15 HPCalc

Calculator app based on classic scientific HP-Calculator.

(itunes link) (source code)

28. SpaceBubble

Space game featuring core graphics, and accelerometer usage.

(itunes link) (source code)

29. Star3Map

Augmented reality star and planet charting application.

(itunes link) (source code)

30. Task Coach

Personal to-do list and task manager.

(itunes link) (source code)

31. Tubestatus

London train schedule tracker.

(itunes link) (source code)

32. Tux Rider

iPhone port of the extremely popular, and beautiful 3D Tux Racer game.

(itunes link) (source code)

33. Tweejump

Platform jumping game inspired by Icy Tower. Utilizes Cocos2D.

(itunes link) (source code)

34. Tweetee

Enhanced version of the Natsulion Twitter Client.

(itunes link) (source code)

35. Tweetero

Basic twitter client with image uploading.

(itunes link) (source code)

36. Twitterfon

Super-fast intuitive twitter client.

(itunes link) (source code)

37. ViralFire

Unique game where you are a dodging blood cell.

(itunes link) (source code)

38. Wikihow

A reader app for the popular how to wiki site.

(itunes link) (source code)

39. Wolfenstein 3D Classic Platinum – 

If you haven’t heard of Wolfenstein post below so we can say a prayer for you.

(itunes link) (source code)

40. WordPress

Client for managing WordPress blogs.

Also has an iPad version. (itunes link) (source code)

41. Xpilot

Simple massively multiplayer arcade shooter from the early days of the internet

. (itunes link) (source code)

42. YourRights

Pocket database containing a summary of your legal rights.

(itunes link) (source code)

43. ZBar

A barcode reader open source iphone app.

(itunes link) (source code)

Check them out if you are working on something similar or think they might have a feature you could use there’s no reason to re-invent the wheel.  Always make sure you understand the licenses as many of these open source iPhone apps are GPL and require that you open source your app.

IIS: Creating Multiple Web Sites within IIS on (2000 and XP Pro)

In the server versions of Windows, you can have multiple versions of web sites installed into IIS and running simultaneously. You can install and run more than one web site within IIS in the professional version of Windows XP or Windows 2000. You just have to pull a trick to modify the IIS metabase to that it is aware of the additional sites. The user interface does not support creating more than one web site. Also, you still will not be able to have more than one site running at the same time.

To create the second web site:

  • Create a command prompt window.
  • Type "cd \Inetpub\Adminscripts" and press enter.
  • Find out what the highest numbered site you currently have is. You can do that by typing

adsutil.vbs ENUM /P W3SVC

If you have never done this process before, the highest numbered site should be 1.

  • Add one to the highest numbered site. Then run this command:

adsutil.vbs COPY W3SVC/1 W3SVC/x

Where x is replaced by the new numbered site. For example if you run the enum command and the highest numbered site is 4, then type this:

adsutil.vbs COPY W3SVC/1 W3SVC/5

  • Run the IIS Manager. You should find a new site has been created. It will be an exact copy of site #1, so you will need to change a few things. Go into the properties for the web site. Change the site name, and the virtual directory for the root of the web site to point to another location on the hard disk. Usually, you will create a new subdirectory under Inetpub and point the virtual directory at that.
  • To use the site, you must first stop the currently running site, then start the new site.

To delete a site, first stop it if it is running (bad things happen if you don't!). Then type:

adsutil.vbs DELETE W3SVC/x

Where x is the number of the site you want to delete.

You can find the number for an existing web site in IIS Manager by enabling logging and examining the logging properties. The web site number is at the bottom of the dialog.

Sending email from ASP.NET MVC through MVC and MSMQ Part 2

Original Post: http://dotnetslackers.com/articles/aspnet/Sending-email-from-ASP-NET-MVC-through-MVC-and-MSMQ-Part2.aspx

By: Andrew Siemer

In this article you will learn how to send emails using ASP.NET MVC and MSMQ.

Contents

  1. Introduction
  2. Setting up a private queue
    1. MSMQ in Windows 7
    2. MSMQ in Windows 2008
  3. Creating an MSMQ Service
  4. Update your web application to use MSMQ instead of SMTP
  5. Draining the queue
    1. A quick refactor of the queue guard code
  6. Sending the emails
    1. Relocating the SerializeMessage method
  7. Creating an installable Windows Service to drain the queue
    1. A quick refactor of our console applciation
  8. Finishing up the Windows Service
    1. Adding a Timer
    2. Making the service installable
  9. Installing your website email queue processing service
  10. Summary

Sending email from ASP.NET MVC through MVC and MSMQ Part Series

Introduction

In the previous article we discussed some of the downfalls of sending email directly from a web page and worker process that served that request. The biggest issue that we discussed was the fact that the user that initiated an email to be sent would be stuck waiting for the code to connect to the mail server and deliver the message. But we also discussed that for important emails we would also need a logging component to keep track of all the customer communications that we send. We then built out an example ASP.NET MVC application that sends email in a direct from the page fashion. We also built out a logging component so that all the messages that we intended to send would be serialized and stored in a logging database using LINQ to SQL.

In this article we will now refactor the previously created code base (make sure you grab a copy of the first articles application if you want to work along with this article) to take advantage of MSMQ. We will modify our EmailService so that rather than speaking directly with System.Net.Mail and SMTP we will instead utilize System.Messaging and MSMQ. Once our web application is converted to send its email communications to MSMQ we will then need to create a queue processing application. We will start by creating this in the form of a console application that clears the queue and sends email via our existing EmailService. Once the console application is working we will convert it into a more reliable Windows Service.

Setting up a private queue

To get started we will first create a new queue. There are many options when creating a queue. Take a look at "Reliable Messaging with MSMQ and .NET" for a good in depth discussion of MSMQ concepts. We are going to create a simple non-transactional private queue for this example. You need to first make sure that you have MSMQ installed on your machine.

I will quickly describe how to install MSMQ in Windows 7 and Windows 2008. You can use MSMQ on any of today's current versions of Windows (just google for some MSMQ installation help if you don't know what to do).

MSMQ in Windows 7

Go to Control Panel\Programs and select "Turn Windows features on or off". In the Windows Features window that pops up, scroll down to Microsoft Message Queue (MSMQ) Server and select that box. Just leave the default selection as that will get us through this demonstration. Then click OK.

Then navigate to the Computer Management console. You should be able to expand Services and Applications and see a Message Queuing entry. Expand that entry and right click on Private Queues. Select new private queue. In the New Private Queue window that opens enter WebsiteEmails. This will make the path to your private queue "{ComputerName}\private$\WebsiteEmails".

MSMQ in Windows 2008

In the Server Manager window, navigate to the Features node. Then click on Add Features. In the Add Features Wizard scroll down to Message Queuing. Then click through the wizard buttons to complete the installation.

Back in the Features node of Server Manger you should now have a Message Queue node. Inside of there you will see a Private Queues node. Right click on Private Queues and select new private queue. Give your queue the same name as above WebsiteEmails. This will make the path to your private queue "{ComputerName}\private$\WebsiteEmails".

Creating an MSMQ Service

Now that we have a queue to dump our EmailMessages into, let's write some code that will allow us to do just that. Start by creating a QueueService class in your Services folder. Then we will create a method named QueueMessage that will take in an EmailMessage object.

Listing 1: QueueService.cs

public class QueueService
{
public void QueueMessage(EmailMessage message)
{
}
}


Now let's add a reference to System.Messaging to our BusinessLayer project. Then we will add the code that we need to connect to our private queue and insert a new message.

Listing 2: QueueService.cs

public class QueueService
{
public void QueueMessage(EmailMessage message)
{
Message msg = new Message();
msg.Body = message;
msg.Recoverable = true;
msg.Formatter = new BinaryMessageFormatter();
string queuePath = @".\private$\WebsiteEmails";
MessageQueue msgQ;
//if this queue doesn't exist we will create it
if(!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
msgQ = new MessageQueue(queuePath);
msgQ.Formatter = new BinaryMessageFormatter();
msgQ.Send(msg);
}
}


Update your web application to use MSMQ instead of SMTP

All that we need to do to get our web site to use MSMQ instead of SMTP is to comment out (or delete) the call to our EmailService and replace it with a call to our QueueService.

Listing 3: HomeController.cs

[HttpPost]
public ActionResult Index(string id)
{
EmailMessage em = new EmailMessage();
em.Subject = "test message";
em.Message = "howdy from asp.net mvc";
em.From = "asiemer@andrewsiemer.com";
em.To = "asiemer@andrewsiemer.com";
//new EmailService().SendMessage(em,
// "{email address}",
// "{password}",
// "smtp.gmail.com",
// 587,
// true,
// new LoggingRepository());
new QueueService().QueueMessage(em);
return View();
}


You should now be able to build and run your application. Clicking the Send Email button should now store messages in your new private queue. More importantly, the time it takes from when you click the button until the page returns should be considerably faster!

After clicking the button a few times you should be able to open up your MSMQ management window and see some messages in the queue.

Also, if you double click one of those messages to open it up, you should be able to see all sorts of data about the message. And you can see the original email that was sent!
Draining the queue

Now that we have our web site converted to send emails to a queue, we need to create an application that can read from the queue, deserialize our messages, and send out the emails as intended. We will start building this application as a console app. Once that is working as expected we can convert it to a Windows Service.

Add a new Console Application to your solution named WebsiteEmailProcessor. Then add two references to that project. The first reference is to the BusinessLayer project. The second reference is to the System.Messaging namespace.

Next we need to add a way for our application to read from the queue. This process is fairly straightforward. We will need the Main method in our console application to get us started. But we are also going to need an event handler to keep processing messages as they come into the queue.

Listing 4: Program.cs

class Program
{
private static MessageQueue msgQ = null;
private static object lockObject = new object();

static void Main(string[] args)
{
string queuePath = @".\private$\WebsiteEmails";
msgQ = new MessageQueue(queuePath);
msgQ.Formatter = new BinaryMessageFormatter();
msgQ.MessageReadPropertyFilter.SetAll();
msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(msgQ_ReceiveCompleted);
msgQ.BeginReceive();
while (Console.ReadKey().Key != ConsoleKey.Q)
{
// Press q to exit.
}
}
static void msgQ_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
lock (lockObject)
{
// The message is plain text.
EmailMessage msg = (EmailMessage)e.Message.Body;
Console.WriteLine("Message received: " + msg.Message);
}
// Listen for the next message.
msgQ.BeginReceive();
}
}


With what we have so far you can now run both the web application and the console application. When you click the Send Email button on the web application you should see a message go through your queue and through the processor. Give it a try!

A quick refactor of the queue guard code

You may have noticed that our console application is currently making an assumption that a queue exists. This is lazy! Let's refactor some code in our QueueService to check that the queue exists and make the queue in the case that it doesn't.

Listing 5: QueueService.cs

public static void InsureQueueExists(string queuePath)
{
//if this queue doesn't exist we will create it
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);
}


And then we can replace this check in our QueueService.QueueMessage method.

Listing 6: QueueService.cs

MessageQueue msgQ;
InsureQueueExists(queuePath);
msgQ = new MessageQueue(queuePath);


Now we are ready to update our console app so that it makes this check as well.

Listing 7: Program.cs

class Program
{
private static MessageQueue msgQ = null;
private static object lockObject = new object();
static void Main(string[] args)
{
string queuePath = @".\private$\WebsiteEmails";
QueueService.InsureQueueExists(queuePath);
msgQ = new MessageQueue(queuePath);
}
}


Sending the emails

Now that the core guts of this thing are working we can now integrate our EmailService back into the picture. We will add this to our msgQ_RecieveCompleted event handler.

Listing 8: Program.cs

static void msgQ_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
lock (lockObject)
{
// The message is plain text.
EmailMessage msg = (EmailMessage)e.Message.Body;
new EmailService().SendMessage(msg,
"{username}",
"{password}",
"{host}",
587,
true,
new LoggingRepository());
Console.WriteLine("Message received: " + msg.Message);
}
// Listen for the next message.
msgQ.BeginReceive();
}


Now when we run the web application and the console application we can click the SendEmail button, see the messages get queued up in the MSMQ manager, see the console application process the queue, see in the database that our log is being added too, and see in our email client that the emails are getting sent too!
Relocating the SerializeMessage method

This looks pretty good so far. But it no longer makes sense to keep our SerializeMessage method in the EmailService. Let's move it to its own class now and then we can come back to update the QueueService. We need to create a new Serializer class. Then if you are using ReShaper you can simply put your mouse over the SerializeMessage method name and press [ctrl][shift][r] to refactor the method. Then you can choose Move to another Type from the list that pops up. If you don't have ReShaper then you can copy/paste! Don't forget to update your EmailService to use the relocated Serializer.SerializeMessage method (ReSharper does this for you!).

Listing 9: Serializer.cs

namespace AndrewSiemer.BusinessLayer.Services
{
public class Serializer
{
public static string SerializeMessage(EmailMessage message)
{
StringWriter outStream = new StringWriter();
XmlSerializer s = new XmlSerializer(typeof(EmailMessage));
s.Serialize(outStream, message);
return outStream.ToString();
}
}
}


Creating an installable Windows Service to drain the queue

To complete our distributed application we need to take the console application and beef it up a bit. While we could technically leave our queue processor in the console application and just leave the console application running all day, this is not a very "enterprise" way of doing things! For that reason we will create one last project. This time we will create a Windows Service project that can be installed on a server and left to run forever after.

Let's start by adding a new Windows Service project called WebsiteEmailService to our solution. Then set the ServiceName (in the properties window) to something you will recognize in your Services MMC. I am calling my service WebsiteEmailQueueProcessor.
A quick refactor of our console applciation

Then we need to do a quick refactor of our console application. We need to pull the code that we wrote there up into our BusinessLayer project. We will do this by adding a QueueProcessor class to our Services folder. Inside that class we will add all the code from our Project.cs file so that it can be referenced by both the console application and the service.

Listing 10: QueueProcessor.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Text;
using AndrewSiemer.BusinessLayer.DataAccess;
using AndrewSiemer.BusinessLayer.Domain;
namespace AndrewSiemer.BusinessLayer.Services
{
public class QueueProcessor
{
private static MessageQueue msgQ = null;
private static object lockObject = new object();
public static void StartProcessing()
{
string queuePath = @".\private$\WebsiteEmails";
QueueService.InsureQueueExists(queuePath);
msgQ = new MessageQueue(queuePath);
msgQ.Formatter = new BinaryMessageFormatter();
msgQ.MessageReadPropertyFilter.SetAll();
msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(msgQ_ReceiveCompleted);
msgQ.BeginReceive();
while (Console.ReadKey().Key != ConsoleKey.Q)
{
// Press q to exit.
Thread.Sleep(0);
}
}
static void msgQ_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
lock (lockObject)
{
// The message is plain text.
EmailMessage msg = (EmailMessage)e.Message.Body;
new EmailService().SendMessage(msg,
"{username}",
"{password}",
"{host}",
587,
true,
new LoggingRepository());
Console.WriteLine("Message received: " + msg.Message);
}
// Listen for the next message.
msgQ.BeginReceive();
}
public static void StopProcessing()
{
msgQ.Close();
}
}
}


From there we can update our Program class in the console application to use this code which will make it much smaller and easier to read!

Listing 11: Program.cs

class Program
{
static void Main(string[] args)
{
QueueProcessor.StartProcessing();
}
}


We also need to move the while loop out of the QueueProcessor as that implementation will only work when ran from the Console Application. It will give us errors if ran from the Windows Service. For that reason we will relocate that looping logic to our console app like this.

Listing 12: Program.cs

class Program
{
static void Main(string[] args)
{
QueueProcessor.StartProcessing();
while (Console.ReadKey().Key != ConsoleKey.Q)
{
// Press q to exit.
Thread.Sleep(0);
}
}
}


Finishing up the Windows Service

Now that our code is accessible for both the Windows Service and the Console Application we can complete the service. In the design surface for your service click the "click here to switch to code view" link.

Adding a Timer

One of the first things that we need to do when building up our service is to add a Timer to keep track of how often our service does its job. Every time the Timer's interval completes it will fire an event that we will capture to perform our queue processing. To do this we first need to declare a private Timer instance.

Listing 13: Service1.cs

public partial class WebsiteEmailQueueProcessor : ServiceBase
{
Timer timer = new Timer();
}


Then we need to initialize our timer in the OnStart method.

Listing 14: Service1.cs

protected override void OnStart(string[] args)
{
timer.Interval = 1000;
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.AutoReset = true;
timer.Enabled = true;
timer.Start();
}


Next, we need to create our even handler which will actually kick off the processor.

Listing 15: Service1.cs

void timer_Elapsed(object sender, ElapsedEventArgs e)
{
QueueProcessor.StartProcessing();
}


Lastly, we need to add some code to the OnStop method of our service that will stop the timer and free the resources in our QueueProcessor.

Listing 16: Service1.cs

protected override void OnStop()
{
QueueProcessor.StopProcessing();
timer.AutoReset = false;
timer.Enabled = false;
}


Logging makes building a service much easier!

I didn't want to bog down our code examples with logging code. But in the code download for this article you will find that I have added some error handling and logging code. This will allow you to look into what is going on with the service when you attempt to install it, run it, etc. which will ultimately help you track down problems in your service. My logging implementation just appends text to a text file in the root of your C drive. You may want to update this to run from somewhere else!

Making the service installable

Now that we have everything ready for testing we have a few more steps to perform to get our service installed and running. To make an installer for our service you need to go the design surface of your service. Then right click in the grey area and select add installer. This will add two components to your design surface, a service process installer, and a service installer. You might want to rename these to something more descriptive to your project. I prefixed both of them with WebsiteEmailQueueProcessor.

In the service installer you will want to give your service a description and name that means something to you when looking at the service in the Services MMC. I named my service "Website Email Queue Processor" and gave it a description of "Website email queue processing service".

Then in the process installer you will want to change the Account that your service will run under by default to "LocalSystem". This should give your service enough rights to run after the basic install process (described shortly).

The last and most important step of all this is to navigate to the properties window for your windows service project. In the application tab make sure that you set the Startup Object to the Program class in your service project.

Now you can build the entire solution (cross your fingers). Once everything builds ok we can get this puppy installed and running.

Installing your website email queue processing service

To get your service installed you need to open the Visual Studio command prompt in administrator mode. Then navigate to the directory that contains your service executable. This is most likely in the bin/debug folder of your service project. Then you can run installutil and the executable name to install your service.

Run these commands in the Visual Studio command prompt.

Listing 17: Visual Studio Command Prompt in Administrator Mode

cd /d {drive letter where your code is}:
cd "{full path to project}\bin\debug\"
installutil {name of executable}.exe

Microsoft (R) .NET Framework Installation utility Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

Running a transacted installation.

.........

The Commit phase completed successfully.
The transacted install has completed.


If you want to remove a service, you can type following command in the Visual Studio command prompt:

installutil /u {name of executable}.exe

If you find that you are having issues getting your service installed, you can always check the folder that contains your executable. After running the installutil you will have a couple of log files that will give you the details of your installation. Check there for more clues and google for the answer!

With any luck everything should be installed and at this point. You will then need to go to the Services MMC and start your service. You should now be able to run your web application and click the magical Send Email button. And after a few seconds that email should arrive in your inbox!

Summary

In our previous article we discussed why sending email directly from a website upon a user's request might be a bad idea. We then discussed some of the options for getting around these various forms of evil. We then created a demo application that sent email directly from the website. In this article we refactored our initial code base so that it would use a distributed method of sending email using an MSMQ private queue and a Windows Service to drain that queue and do the heavy lifting of sending email.