I've started writing another post on how to write a Windows service and realized that I want to show first something simpler: how to install the service in the Windows system, provided that you already have the binary. This is fairly simple to do right, and yet much confusion exists around it.
For example, if you use the C# service boilerplate project in VisualStudio, you actually get two binaries: one for the service itself, another one for the service install code. Yet the service install code is nothing but a pain and suffering and a waste of space. Windows already contains a tool that can install or uninstall any service for you. The tool name is sc.exe ("sc" stands for the Service Controller).
Here is an example of how I can manually install a Setup And Boot Collection service for testing:
sc create BootEventCollector binPath=c:\Temp\bevtcol.exe start=demand
It's fairly straightforward: give it the service name, the binary of the service in the binPath, and the start mode (on-demand is convenient for testing).
Note that this command would work as-is from cmd.exe but not from PowerShell. PowerShell happens to define "sc" as an alias for its own cmdlets that know how to stop and start a service but nothing more. You want the real command-line tool, so from PowerShell you would have to use the explicit "sc.exe" instead of simply "sc".
To start the service, use:
sc start BootEventCollector
or the equivalent PowerShell cmdlet Start-Service.
Well, there is actually a bit more that might need to be configured if your service uses the ETW logging and supports the WMI control calls. This part has nothing to do with the service as such, any binary that uses this features would have to have the manifests installed. But I'll show it here for completeness anyway.
To install the ETW manifest, use:
wevtutil im lib\bevtcol.man /rf:c:\Temp\bevtcol.exe /mf:c:\Temp\bevtcol.exe
The /rf and /mf specify that the strings for the interpretation of the messages would be pulled directly from the binary. Or if you've built the binary with a separate MUI file for the localizations, from the MUI file that has to be placed at a certain path relative to the binary. In my case that would be c:\Temp\en-us\bevtcol.exe.mui.
To install the WMI manifest, use:
As the final part of the installation, your service might need some configuration. If it's needed, there are two ways to do it: either place it properly into Registry or specify the extra command-line parameters in binPath when creating the service. The Registry configuration goes into HKLM\SYSTEM\CurrentControlSet\Services\YourServiceName\Parameters, such as:
reg add HKLM\SYSTEM\CurrentControlSet\Services\BootEventCollector\Parameters /v config /t REG_SZ /d c:\Temp\active.xml
Or here is an example of installing a service with the extra command-line parameters:
sc create %SVCNAME% binPath="%RUNDIR%\WrapSvc.exe -name %SVCNAME% -ownLog %RUNDIR%\W_%SVCNAME%.log -svcLog %RUNDIR%\S_%SVCNAME%.log -- c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Unrestricted %RUNDIR%\TestSvc.ps1" start=demand
This service is an interesting one, it wraps any binary and makes it run as a service, in this particular test wrapping a PowerShell script. You might be interested in the more detailed description. But for now it shows a good example that the parameters can be quite complicated. The only trouble you might encounter is if you ever need to nest the quotes.
If you want to see how a service is defined, use:
sc qc BootEventCollector
Now let me show how to take a service down when you don't need it any more:
sc stop BootEventCollector sc delete BootEventCollector wevtutil um lib\bevtcol.man mofcomp bevtcol\BootEventCollectorWmiUninstall.mof
When the service gets deleted, the Service Controller clears its Registry location. Note that the uninstallation of a MOF manifest consists of compiling another manifest that has the uninstall instructions in it.
The final side note is that the loading of the drivers is also controlled by the Service Controller in the same way as the services. I've seen the driver examples that provide their own code for loading the driver just like the C# boilerplate does for loading the service. But that's just wrong, use sc.exe instead. For example:
sc create UDFS type=filesys binPath=c:\windows\system32\drivers\udfs.sys