Category
Some of my blog entries are just to remind myself how to do things later--and on several occasions they've been useful in that regard. I hope they are also useful to others that may encounter similar struggles. This is another one of those articles which I will likely refer back to at some point.
I've been doing more and more with .NET and using Oxygene makes the transition from Delphi a little more familiar. One of the things I've been working on lately is a set of plug-ins to a Delphi application that provide additional functionality. And with Hydra, I can write those plug-ins in either Delphi or Oxygene. Recently, I wanted one of my plug-ins to talk to a service on a remote server. The first step, of course, is to write the service for the remote server and learn how to get it installed.
Because Microsoft Visual Studio Community 2013 has such a large developer base, there are many resources to help you accomplish this (that's the beauty of working in .NET rather than Delphi). But just a few nuances here and there for someone that doesn't do this very often slowed me down a little. Finding just the right web reference or book chapter takes time, so once I find that, I tend to want to write it down.
New Project
First of all, Visual Studio makes it pretty simple to get started: just select the "Windows Service" new project template.
This gives you a project to start with that includes Main.pas, the main section of the code you'll be working with, Program.pas, which you can likely ignore but is necessary, and ProjectInstaller.pas.
Add Installer
The first thing to do is right-click on Main and View Designer. Look in the Properties window and click on Add Installer, a small link at the bottom. This adds a couple of components to the ProjectInstaller and allows you to easily name and describe your service.
Service Properties
Now in the ProjectInstaller designer view, click on the serviceInstaller component and set the DisplayName and Description properties. These will show up in the Services control panel after it's installed. Here's also where you can set the StartType (Manual, Automatic, or Disabled) and other properties affecting how the service behaves with Windows. If you then view the code for ProjectInstaller.Designer.pas, you'll see the properties you set listed in the InitializeComponent method.
Before you leave the ProjectInstaller, you need to check one more thing. Look at the properties for the other component in designer mode, serviceProcessInstaller. An important property is Account. By default, it's set to User, but in most cases it can be either LocalService or LocalSystem. I'll leave it to the reader to research the differences and choose what is most appropriate for your situation. For this simple test, setting it to LocalService is easiest.
Event Logging
Since a service does not typically interact with users but runs silently in the background, it's a good idea to provide some way to check to make sure it is running. There is an automatic way of doing this and all it takes is one line in the constructor of Main.pas: AutoLog := true;
. This will add to the System Event Log when the service is started, stopped, paused, or resumed. You can add your own custom logging by using the EventLog component on by rolling your own.
Now even though this service doesn't do anything useful, it's got the basics to be compiled, run, installed, and started with logging to see it actually happen. So compile the project and move to the final step.
Install the Service
When a service is ready to install, use the command-line utility InstallUtil. This utility comes with the .NET framework. Normally, there is no path to the .NET framework tools, but both Visual Studio and Oxygene provide command shells you can launch that provide the path. However, you need to know which version of the .NET framework you used to build your project because the shells are specific to the version of the framework and if you run the wrong one, you'll get errors. Oxygene comes with one for .NET 2.0 but the default framework for new projects (as of this writing) is .NET 4.0. I have copied the batch file that is installed with Oxygene (C:\Program Files (x86)\RemObjects Software\Oxygene\bin\OxygenePrompt.bat
) and separated them so I now have one for each of the two framework versions. (By the way, to tell which version your project is using, view the Application tab of the project's properties and you'll see it under Target Framework.) Once you have the correct command shell ready, be sure to Run as Administrator because standard users cannot install/uninstall Windows services.
With the command shell running as administrator, navigate to the bin\Release folder for your service and type InstallUtil MyServiceName.exe
(where "MyServiceName" is the name of your project). Open the Services control panel (or refresh if it was already open) and you should see your new service. You can start or stop it just like other services (although if you've followed this example, it doesn't yet do anything once started).
Start/Stop, Check the Log
Now that the service is built and installed, start the service then go to the Windows Event Viewer. You should see events in both the Application and System sections of the Windows Logs.
Now, it's time to go back to Main.pas and add some useful code when it starts!
Installing the service with InstallUtil may raise questions about what to do on a system that is not your development system. But remember, InstallUtil comes with the .NET framework, so any computer that can run your service would have that. The trick is to add the framework's path to the environment so you can call it from your service folder. Here is a batch file (for .NET 4.0) as an example you can use:
@echo off
@set PATH=%PATH%;C:\Windows\Microsoft.NET\Framework\v4.0.30319
title Command Prompt with .NET 4.0
C:\WINDOWS\system32\cmd.exe
There are, of course, other ways of installing a service written with .NET technologies. Here are some references: