How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017
This is the second part of the Tray Notify series which illustrates how a Windows client app can communicate with a Window Service.
The sample application illustrates monitoring file change events from the Windows service and sending out file change notications to the task bar tray applications using the WCF service.
2. Windows Service Primer
A Windows Service is a special type of application that is managed by the OS. Unlike a normal application where a user starts it by clicking a Start menu item, from explorer or by typing in the application name in a console window, a service application is managed by the Windows Service Control Manager (SCM). In other words, to start and stop services a user does this using the Services control panel applet (found under Adminstrator tools section of the control panel).
2.2. Windows Service Properties
Other than being managed by the SCM, Windows services have other unique properties. For one thing they can be set to run under specified accounts (as compared to a normal application which typically runs under the account of the user that started it).
Another property is a Windows Service can be set to auto-restart in case of a catastrophic failure. A normal application that crashes would need to be restarted by the user whereas a windows service can be set to be restarted by the SCM.
The final interesting property of Windows services is they run independent of any logged on user and run in a different desktop from the user(s). A Windows Service runs in Session 0 desktop only. This gives added security and prevents a Windows Service from directly interacting with a user's desktop (which from Vista on runs in Session 1 and so on).
In pre-Vista, the OS ran Windows services in Session 0, and also ran the first logged on user in Session 0 as well, given a service the opportunity to choose the "Interact with the desktop" option. Back in the day (aka pre-Vista) users could choose this option and have a Windows Service display dialogs, forms, etc. directly on the user's desktop.
Although Window Services were not intended to interact with the UI, in some cases it was convenient for a service to be able to do so. Unfortunately, allowing a service to interact with the desktop was also a security issue, so in Vista, Win7 and later, Microsoft not longer supports this option.
2.3. Debugging Windows Services
One difficulty with working with Windows services is the issue of how to debug them. Remember a Windows Service runs in a different desktop from the developer coding it. So running a Windows Service under the SCM and trying to step through the code in a debugger isn't going to work.
For this reason, a bit of abstraction is used when coding up the service code. Rather than putting all the code inside the ServiceBase derived class, an 'implementation' class is used to perform all the work and the ServiceBase derived class just delegates the work to the implementation class. This facilitates debugging because to debug, the code in the implementation class is run (instead of running the ServiceBase derived code). More on this later.
3. Build out the Windows Service (Host) Project
The Windows Service project is the Windows Service application that hosts the WCF Service. The service gets registered with SCM (Windows Service Control Manager) to operate as a Windows Service. Windows services are useful because they can be set to auto-start on machine startup and don't require any logged on users to execute.
Windows Services require several standard methods: OnStart, OnStop, OnShutdown and a few optional methods. When the Windows Service is registered, the SCM handles the startup/shutdown and management of the service. This is a bit different than a regular application.
A Windows service is used because it is 'common' with any logged on user. If multiple 'my documents' folders of different users are being monitored, it makes sense to perform the monitoring from a single process independent of any user - a Windows Service fits this bill nicely.
The Windows Service buildout will include adding a service installer so the project can be registered as a Window service and adding code that will host the WCF service.
3.1. Remove the default Service1 class from the project.
Open the CG.TrayNotify solution built in Part II and right click on the Service1.cs to remove the default service from the CG.TrayNotify.Host.Service project.
3.2. Create a TrayNotifyHostService class
This is the class that gets registered and executed by the Windows Service Control Manager (SCM). In order to function as a Windows Service, all Windows Service projects must contain at least one class derived from ServiceBase.
Follow these steps to add the TrayNotifyHostService class:
- Right click on the "Solution 'CG.TrayNotifyHostService" node in the Solution Explorer
- Click "Add\Class..."
- Under "Categories:", expand the "Visual C# Items" node and select "General"
- Under "Templates:", select "Windows Service"
- Enter "TrayNotifyHostService.cs" for the class name
- Press "OK"
3.3. Add an Installer to the TrayNotifyHostService class
The installer allows the service to be registered using the InstallUtil.exe utility which will be outlined later.
To add the installer:
- Double click on the TrayNotifyHostService to open it in the service designer
- In the service designer, right click and choose "Add Installer"
- A ProjectInstaller.cs file will be added to the project.
3.4. Configure the Project Installer
The installer contains the settings used when starting up the Windows Service. These settings contain the Startup account, startup mode, name, description and so on. After the service has been registered these settings appear under the service in the Services control panel applet.
Double click on the ProjectInstaller.cs file which will cause the installer to open in the designer window. You'll notice that there are two icons: serviceProcessInstaller1 and serviceInstaller1. Open the properties window in the solution explorer and pin it.
3.4.1. Rename the serviceProcessInstaller1 class
The serviceProcessInstaller1 contains startup account settings. Rename this to TrayNotifyHostServiceAccount in the Name field of the properties window.
3.4.2. Configure the TrayNotifyHostServiceAccount
In the properties window, set the Account field to LocalSystem.
3.4.3. Rename the serviceInstaller1 class
The serviceInstaller1 contains the service display name, description and service dependencies, and startup type. Rename this class to TrayNotifyHostServiceInstaller.
3.4.4. Configure the TrayNotifyHostServiceInstaller
Fill out the following fields:
- Description: Hosts the Tray Notify WCF Service
- DisplayName: CG Tray Notify Host
- StartType: Automatic
Note: The ServicesDependsOn string array isn't used in this sample. However, this is useful if the service depends on other services to run. For example, if a service depended on SQL Server or Message Queuing, then MSSQLSERVER and MSMQ would be entered in this list. This would tell the SCM to ensure that the MSSQLSERVER and MSMQ services were started before this service.
About the Author
Arjay Hawco is an application developer/architect and Microsoft MVP who works with the latest WxF .Net technologies. He is also cofounder of Iridyn, Inc, a software consulting firm.