- Defining the Windows Service Project
- Adding Some Simple Test Code
- Registering the Service
- Creating the Test Program
- Bottom Line
Registering the Service
You can't just start the service. Even though this is an executable, you have to install the service in a particular way. The standard method is to open the command prompt and use the InstallUtil command to perform the task. This method should work with the example because it works with Windows services created using other languages. Unfortunately, it doesn't work very well, so you need to rely on a second method: typing the service name at the command prompt with the -Install switch. You also need to provide the full path to the executable (even though you're already in the executable path), which means typing something like this to install the Windows service:
C:\MyService\SimpleService.exe -Install
Yes, the installation code is that flawed.
Unfortunately, Microsoft doesn't provide code to uninstall the Windows service. Consequently, I added the code shown in Listing 2 to the SimpleServiceWinService.CPP file. This code appears immediately after the install code in the _tmain method.
Listing 2 Adding Uninstall Functionality to the Windows Service
if (_tcsicmp(argv[1], _T("-Uninstall")) == 0) { //Install this Windows Service using InstallUtil.exe String* myargs[] = System::Environment::GetCommandLineArgs(); String* args[] = new String*[myargs->Length]; args[0] = S"-u"; args[1] = (myargs[0]); AppDomain* dom = AppDomain::CreateDomain(S"execDom"); Type* type = __typeof(System::Object); String* path = type->get_Assembly()->get_Location(); StringBuilder* sb = new StringBuilder(path->Substring(0, path->LastIndexOf(S"\\"))); sb->Append(S"\\InstallUtil.exe"); dom->ExecuteAssembly(sb->ToString(), 0, args); }
The code is a simple modification of the installation code. When the Common Language Runtime (CLR) starts the application and you provide -Uninstall as the command-line parameter, argv[] actually contains two arguments: the -Uninstall switch and the application name as you type it at the command line. When you don't include the path information, neither does CLR, so the InstallUtil utility doesn't know where to find the executable; it reports an error such as this:
System.IO.FileLoadException: Unverifiable image 'SimpleService.exe' can not be run.
To uninstall a Windows service, the code must call InstallUtil using the ExecuteAssembly() method. This method accepts the name of the application (including full path) and an array containing arguments, args. The first argument in args is -u, which tells InstallUtil to remove the Windows service. The second argument is the full path to the Windows service executable, including the EXE extension. Notice how the code obtains this information using the GetCommandLineArgs() method and places it in the args array. Notice that there isn't a single line of error-checking or augmentation codewhat you type at the command line is what the code will use.
Now that the code has created a list of arguments, it has to create the call to InstallUtil. It begins by getting the path to the simple object that CLR possesses, System::Object. This path actually leads to mscorlib.dll. Therefore, the code strips off the executable information from the path it just obtained and appends InstallUtil.exe to it. The final step is to uninstall the Windows service. It's not pretty, but it works, and you don't have to figure out precisely what path information to provide.