Monday, February 04, 2008

Windows Services in C#: Part 2: Adding an Installer for Your Windows Service

In Part 1 we discussed how to write a simple Windows Service that used a timer to write to to the Event Log every 5 minutes. Although we can use installutil to manually add the service to Server Explorer, this can be cumbersome when deploying our application. This is especially true when we need to deploy to multiple machines or the Windows Service is being deployed directly by our customer. To simplify this process, we'll learn how to add an installer for our solution and tell that installer to add the service for us.

This article is one in a five-part series covering the following topics:
  1. Programming a Windows Service in C#
  2. Adding an Installer for Your Windows Service
  3. Getting Your Installer to Start Your Service
  4. Some Options for Debugging Your Windows Services
  5. Adding an Uninstaller for Your Windows Service

The examples in this series are written in C#, but this should help anyone out there wanting to do this on the .NET 2.0 Framework no matter if they're using C#, VB.Net, or any other language. This is particularly true with this subject since most of the work is actually done in the Visual Studio 2005 GUI and not in the code.

Enough with the small talk, let's build this thing!

Adding an Installer for Your Windows Service
Making a Windows Service installer is about the easiest thing to do, once you know how to do it.

1. Select Add > New Project... under the File menu.
This will (as you may have suspected) add a new project to your Solution.

2. Select a Setup Project .

Under Other Project Types > Setup and Deployment, select Setup Project. At this point you should also give your project a name. For my examples my project will simply be called Setup. Click Ok when you're done.

3. Add Project Output to Application Folder.
When you select your Setup Project in the Solution Explorer you're presented with the File System Editor for that project. From here you can specify what files are added to the user's machine and where. This allows you to designate things like what files from the solution should be added to the application's directory and what desktop and start menu items should be created, if any.

To tell your installer that you want the output (executable and support files) from your Windows Service project to be installed on the hard-drive, right-click on Application Folder and select Add > Project Output...

Select your Windows Service Project from the resulting dialog (shown below; mine is SuperService) and select "Primary output" from the selection box. As you can see from the Description, this is the "DLL or EXE built by the project", but will also include the support files for that executable. Click Ok when you're done.

At this point we have our Setup project that will generate an installer that adds our project's files to a folder on our computer. However, the installer won't actually install the service... yet.

This is where the real Visual Studio 2005 magic begins. We're going to get our Setup Project to fully install our service, fully customized and configured, without touching a single line of code.

4. Add a service installer for your Windows Service project.
Open your Windows Service in design view. Right-click anywhere in the blank space and select Add Installer from the right-click menu.

A new class called ProjectInstaller.cs will be added to your project. Open that new class in Design View. You'll see two controls, serviceProcessInstaller1 and serviceInstaller1.

Click on serviceProcessInstaller1 and observe the Properties, particularly the Account property. This lets you specify which user or system account to run the service under. What you select here really depends on what your application does, but for our purposes, we'll set it to LocalSystem.

Next, select serviceInstaller1 and observe its Properties. Several of them are note-worthy:
  • Description: In the Server Explorer (Control Panel > Administrative Tools > Services) each service has an optional Description next to its name. This property allows you to assign that description. For example, I've set mine to "Writes to the Event Log."
  • DisplayName: If you set this property, the service will be displayed in the Server Explorer as this name instead of the service's real name (dictated by the ServiceName property.)
  • ServiceName: The name of the service. It will appear under this name when windows records events for this service (such as when it errors.) This of course does not include the events we're programmatically logging to the Event Log as detailed in Part 1.
  • StartType: Here, you have three options:
    • Automatic: Start the service when Windows starts. For some reason this does not mean the service will automatically start as soon as it's installed. That's why we'll handle how to start your service after install in our next article.
    • Manual: The user (or another program) must manually start the service.
    • Disabled:Well... Disabled.
5. Connect that service installer to your Setup Project.
Once you're done playing with your properties, you have to let the Setup Project know you want to use this Project Installer during the installation process. Save your property changes, then click on your Setup Project in the Solution Explorer. You may have noticed that when the Setup Project was selected, the buttons across the top of the Solution Explorer changed. The second-to-last button (to which my cursor is pointing in the image below) is for the Custom Actions Editor.

Click on that fella and the Custom Actions Editor will open. Right-click on Install and select Add Custom Action... from the right-click menu.

From the resulting dialog double-click on Application Folder, then "Primary output from [Your service] (Active)". It should be easy to find since it will probably be the only option. Click Ok when you're done.

You can then rename the thing if you want, or keep the default label.

6. Build and install.
All you have to do now is build your project and test the installer. However, by default the Setup Project is set to not build. This is because the MSI file can take a little while to compile and you probably wouldn't want to have to wait for it every time you built your solution.

When you're ready to test your installer, go to Build > Configuration Manager and check the Build checkbox next to your Setup Project. Then when you build it will compile as well.

To actually run the installer, you don't have to go dig up the MSI file on your hard-drive and run it. All you need to do is go to Project > Install.

I realize being able to install your service but not uninstall it is kinda lame. The "uninstall" portion of this series isn't completed yet, so I'll clue you in on a little secret: It's really really easy. Remember back in step 5 where we added the Custom Install Action? Well, do the exact same thing but for the Uninstall folder, instead. Yeah, that's all there is to it. You don't even need to make anther ProjectInstaller.cs file, or anything.

In the next segment I'll show you how you can get your installer to start the service immediately after installing. Why MS didn't just make this a property option on the installer, I don't know. Perhaps they just wanted to give me the opportunity to write this article!

Continue to Part 3: Getting Your Installer to Start Your Service



At 2/11/08, 9:52 AM, Blogger Mel Grubb said...

I'm trying to create the setup project in the way you describe, and I know I've done this successfully on past projects, but for some reason it just doesn't seem to be working this time. Under "Detected Dependencies", I see "Microsoft .NET Framework", but if I double-click it, there's an exclamation mark next to it under the "Launch Conditions" group, and every build fails. Is there something broken with building setups for v3.5 apps?

At 2/11/08, 10:00 AM, Blogger Grinn said...

The exclamation point is normal. I'm not sure about the answer to your 3.5 question, but I doubt it matters and if it did it probably wouldn't affect your build. What error does it give you when your build fails?

At 2/11/08, 5:29 PM, Blogger Mel Grubb said...

It just says "build failed", but none of the projects have any errors. It's only when the setup project is part of the mix. Very strange, and I'm not doing anything "unusual" with the setup project, either. Just exactly the steps you outlined in the post.

At 2/12/08, 8:30 AM, Blogger Grinn said...

Mel: Send us a message through our contact form locate at and I'll respond from my email address so you can zip up and send me your code. Then I'll take a look at it and see if I can figure out what the problem is.

At 5/7/09, 10:45 AM, Blogger ruim said...

Great article Grinn. Very well explained. Had no problems following.
One problem I'm experiencing:
I don't know the details of how the installer (per your instructions) installs the service, but it appears to be calling the same APIs as installutil.exe. How can I specify the installer to use the 64-bit version of the .NET Framework instead of the 32-bit version?

Here's why I'm asking:
Before I read your article, I was installing my service using installutil.exe. I kept getting a BadImageFormatException every time I tried installing "installutil 'servicename.exe'". It took me a while to figure this out. Passing the info along so hopefully others won't struggle with it too long. Turned out the Visual Studio 2008 Command Prompt PATH was pointing to the 32-bit version of the .NET Framework instead of the 64-bit version every time I ran installutil.exe. I'm building on Windows Server 2008 x64. After I switched to the 64-bit version, %windir%\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe, I was able to install my service.

I'm experiencing the same problem with the installer. Got any suggestions that can help me out?


At 5/14/09, 8:01 AM, Blogger ritesh said...

This post is marvellous.
It is so easy to follow.
Thanks a million Mr.Grinn


Post a Comment

Links to this post:

Create a Link

<< Blog Home