When you develop Windows software you come across the need for some kinds of applications need to be run for 24 hours or, in fact, continually while the computer is running. Usually, the computers in question are network servers or monitoring applications on desktop machines. In these cases, you might think about creating a console application which has a either a minimal amount of interaction or is completely silent, but there are better solutions than this.
Simple console or minimized regular GUI apps can face problems like Windows session termination, reboots and user rights. The way to solve this problem is to develop Windows services. In this tutorial, we will dive into building Windows services using Delphi. Following this, you can use it as a template for your Windows services.
Table of Contents
Why would I need to create a Windows service?
There are numerous reasons for creating long-running services such as:
- Processing CPU-intensive data.
- Queuing work items in the background.
- Performing a time-based operation on a schedule
- Unobtrusive operation of ‘helper’ or utility app functions
Background service processing usually doesn’t involve a user interface (UI), but UIs can be built around them.
What are the prerequisites for developing Windows services?
Before starting to dive into Service App development, I recommend you check out the latest version of Delphi IDE. With lots of enhancements and new features, the development process goes even smoother. Besides, you can go with the Delphi Community Edition for free and familiarize yourself with Delphi programming languages and its syntax.
How do Windows Service Applications work?
Service applications take requests from client applications, process those requests, and return information to the client applications. A Web, FTP or e-mail server is an example of a service application.
A Windows service application is a Windows application that can run without requiring a user to be logged on. Windows service applications rarely interact with the desktop at all. In this tutorial, we will be creating a Windows service application with Delphi.
When a service application runs it can be set up to use a default set of user rights either as a virtual ‘service user’ or as an actual regular user with rights to access the system. It’s important to understand that these user rights can affect the folders to which your service app has rights to access and also any ‘mapped’ network folders – where someone has created a mapping like a “Z:” drive which points to a network folder will most likely not be available. Where possible you should always use full UNC path names – or use Windows system calls or Delphi’s runtime TPath
type functions to obtain the correct locations of special folders such as the location of the %APPDATA%
folder and “My Documents” type virtual paths.
Windows service applications can provide a lot of functionality. Look at the list when you start the Services utility tool. These services can be the core of the main GUI applications that you use.
When do you need to write Windows service?
Some time ago, I needed a system monitor utility to monitor the free disk space on our file server. I wrote a utility to check every minute and then write that information to a log file. However, it required a user to be logged on, and when the user logged out, my app just closed. The solution was to recreate the app as a Windows service which would then remain running all the time the computer was powered on – even if the user wasn’t logged in.
Although RAD Studio with Delphi or C++ Builder is optimized more for typical user-facing interactive applications it is more than capable of easily creating service applications.
How to create a Windows service project in Delphi?
To create a new project in Delphi, you will need to have the Delphi development environment installed on your computer. Once you have Delphi up and running, you can begin by creating a new project.
To create a new Windows service project in RAD Studio with Delphi, take the following steps:
- Click on the File menu and select New -> Other. This will bring up the New Items dialog box.
- From the left-hand side of the dialog box, select Delphi and then choose the Windows Service project type from the Windows category.
- Click OK to create the new project.
Note the process is similar for those of you using C++ Builder.
Once the new project is created, you will need to set up the project properties and options. To do this, right-click on the project name in the “Project Manager” window and select “Options.” This will bring up the “Project Options” dialog box. In this dialog box, you can set various options for your project, such as the target platform, output directory, and compiler options. Make sure to set these options according to your project requirements before proceeding with the development of your service.
How to implement a Windows service app using Delphi?
After creating a new Windows service project in Delphi and setting up the project properties and options, the next step is to implement the service itself. This involves adding code to the main service unit, which is typically named “Unit1.pas” by default.
To begin, double-click on the “Unit1.pas
” file in the “Project Manager” window to open it in the code editor. This will display the unit’s code file, which contains the skeleton code for a Delphi service application.
To add your own code to the unit, you will need to define the service’s behavior by handling various service events. These events include the start, stop, and pause events, which are triggered when the service is started, stopped, or paused, respectively.
To handle these events, you can use Delphi’s built-in service component, the “TService
” class. This component provides various methods and properties that you can use to control the service’s behavior, such as the “Start” and “Stop” methods.
For example, you can use the “OnStart” event handler to define the code that should be executed when the service is started. To do this, you can add the following code to the “Unit1.pas” file:
1 2 3 4 5 |
procedure TService1.ServiceStart(Sender: TService; var Started: Boolean); begin // code you want to execute when the service starts // such as making a log entry end; |
Similarly, you can use the “OnStop” event handler to define the code that should be executed when the service is stopped. To do this, you can add the following code to the “Unit1.pas” file:
1 2 3 4 5 |
procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean); begin // Add your code here for things that happen when your service is stopped // this might be closing down persistent connections or writing to a log end; |
In addition to the start and stop events, you may also need to handle the pause event, which is triggered when the service is paused. To do this, you can use the “OnPause” event handler, which is similar to the “OnStart” and “OnStop” event handlers.
For example, you can add the following code to the “Unit1.pas” file to handle the pause event:
1 2 3 4 5 6 7 |
procedure TService1.ServicePause(Sender: TService; var Paused: Boolean); begin // Add your code here for things to happen when the user // chooses "pause" from the service menu or uses the service control // commands to pause your service. // This is NOT the same as the service being stopped! end; |
In each of these event handlers, you can add your own code to define the service’s behavior when the respective event is triggered. For example, you could use the “Start” and “Stop” methods of the “TService” component to start or stop a timer or a thread, or you could use the “Pause” method to pause the execution of a task.
Once you have added code to the main service unit and handled the service’s events, you can proceed with debugging and testing the service using the Delphi debugger and the Windows Services Manager.
How do we implement Windows service application functionality?
Add Vcl.SvcMgr
unit to the Uses
clause of the unit.
Change the ancestor of the main form to TService by modifying the declaration of the TForm class as follows:
1 2 |
type TService1 = class(TService) |
and here goes the full source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
unit ServiceUnit; interface uses Winapi.Windows , Winapi.Messages , System.SysUtils , System.Classes , Vcl.Graphics , Vcl.Controls , Vcl.SvcMgr , Vcl.Dialogs , BackgroundThreadUnit , System.Win.Registry; type TService1 = class(TService) procedure ServiceExecute(Sender: TService); procedure ServiceStart(Sender: TService; var Started: Boolean); procedure ServiceStop(Sender: TService; var Stopped: Boolean); procedure ServicePause(Sender: TService; var Paused: Boolean); procedure ServiceContinue(Sender: TService; var Continued: Boolean); procedure ServiceAfterInstall(Sender: TService); private FBackgroundThread: TBackgroundThread; { Private declarations } public function GetServiceController: TServiceController; override; { Public declarations } end; {$R *.dfm} var MyService: TService1; implementation procedure ServiceController(CtrlCode: DWord); stdcall; begin MyService.Controller(CtrlCode); end; procedure TService1.ServiceExecute(Sender: TService); begin while not Terminated do begin ServiceThread.ProcessRequests(false); TThread.Sleep(1000); end; end; function TService1.GetServiceController: TServiceController; begin Result := ServiceController; end; procedure TService1.ServiceContinue(Sender: TService; var Continued: Boolean); begin FBackgroundThread.Continue; Continued := True; end; procedure TService1.ServiceAfterInstall(Sender: TService); var Reg: TRegistry; begin Reg := TRegistry.Create(KEY_READ or KEY_WRITE); try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey('SYSTEMCurrentControlSetServices' + name, false) then begin Reg.WriteString('Description', 'Blogs.Embarcadero.com'); Reg.CloseKey; end; finally Reg.Free; end; end; procedure TService1.ServicePause(Sender: TService; var Paused: Boolean); begin FBackgroundThread.Pause; Paused := True; end; procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean); begin FBackgroundThread.Terminate; FBackgroundThread.WaitFor; FreeAndNil(FBackgroundThread); Stopped := True; end; procedure TService1.ServiceStart(Sender: TService; var Started: Boolean); begin FBackgroundThread := TBackgroundThread.Create(True); FBackgroundThread.Start; Started := True; end; end. |
The background thread unit here follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
unit BackgroundThreadUnit; interface uses System.Classes; type TBackgroundThread = class(TThread) private FPaused: Boolean; // FTerminated: Boolean; // FOnTerminate: TNotifyEvent; protected procedure Execute; override; public procedure Pause; procedure Continue; // procedure Terminate; // property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate; end; implementation uses System.SysUtils, System.IOUtils; procedure TBackgroundThread.Continue; begin FPaused := False; end; // process something here procedure TBackgroundThread.Execute; var LogFile: TextFile; begin try FPaused := False; AssignFile(LogFile, 'C:TempLogs.log'); Rewrite(LogFile); while not Terminated do begin if not FPaused then begin WriteLn(LogFile, 'Logs From Background Thread: ' + DateTimeToStr(Now)); end; TThread.Sleep(1000); end; finally CloseFile(LogFile); end; end; procedure TBackgroundThread.Pause; begin FPaused := True; end; end. |
Now you can build the service. In the Projects window, you can open the context menu and build the service like this:
Testing the Service using the Windows Services Manager
You can test the service using Windows Services Manager. This utility allows you to start, stop, pause and resume a service and view its status and any errors it may have encountered.
To install the service, you should follow these steps:
- Go to the folder and open in terminal (the executable file should be configured to run as an administrator privileges)
- Type the service name with “/install” command
Once the service is installed, you can start, stop, pause, or resume the service using the Windows Services Manager. To do this, open the Services app in the Administrative Tools folder of the Windows Control Panel. Find the service in the list of services, right-click on it, and select the desired action from the context menu.
After a few seconds, stop the service from the service manager and check the Logs file.
Get the full project source code here on this repository. Moreover, do not forget to check out other tutorials where you can learn how to create multi-platform native applications.
- Everything You Need to Know About Cross-Platform Development
- The Anatomy Of A Great Windows IDE
- 10 Signs You Should Invest in Cross Platform Apps
FAQ
How to Debug Windows Services?
How to Delete a Windows Service?
Open PowerShell as an administrator and type this command sc delete ServiceName
How do I silently install a Windows service app?
You can avoid the message box appearing by adding “/silent
” to the end of the command line like so: myserviceapp.exe /install /silent
Can 32bit services run on 64bit Windows?
Yes, a 32bit service can run normally on a 64bit version of Windows. Note, however, that some network system administrators may have a Windows group policy option set up which will prevent anything other than 64bit services from running. Most don’t do this, but it is possible.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
Thank you for the information I received.
I made a Windows service and it is ok.
Sometimes after a Windows update comes, I find my service uninstalled and I can’t figure out what the reason would be.
Have you encountered such a situation?
No, if you install it using the
/install
command line parameter it should stay installed. It might be that you have some antivirus or network group policy which is forcing your service to be uninstalled although that would be unusual.Hi. Setting description directly in registry is legal, but proper way is via API:
procedure TSampleSvc.ServiceAfterInstall(Sender: TService);
var
SvcMgr,Svc: SC_HANDLE;
ADescription : SERVICE_DESCRIPTION;
begin
SvcMgr := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if SvcMgr = 0 then RaiseLastOSError;
try
Svc := OpenService(SvcMgr, PChar(Sender.Name), SERVICE_ALL_ACCESS);
if Svc = 0 then RaiseLastOSError;
try
ADescription.lpDescription := ‘Whatever you want as description’;
if not ChangeServiceConfig2(Svc, SERVICE_CONFIG_DESCRIPTION, @ADescription) then
RaiseLastOSError;
finally
CloseServiceHandle(Svc);
end;
finally
CloseServiceHandle(SvcMgr);
end;
end;
FROM windows7, service with desktop form can not be supported. How to make a desktop windows form in Tservice app by DELPHI DEVELOPER?
The answer to this is more complicated than we can perhaps go into in a comment – so it might be better if I write an article about that in the future.
For now, though, here’s a brief overview.
First of all, it’s more about *why* you would need a form or visual element for your service. You’re quite right though, services should not attempt to directly show a GUI since services often do not run ‘interactively’ and there may not even be a user session logged in on which to display any GUI in the first place.
Generally, a service might need a dialog to display some kind of configuration settings. This is best done with a separate app whose only purpose is to display the settings form and then notify the service that something has changed. You can write the app as you would for any other desktop app and then come up with a mechanism to notify the service that something has changed and that the service needs to act on it. The simplest way is to create a ‘flag file’ which is just a simple file which you create when your settings app closes. The service then periodically looks to see if that flag file exists, if it does then it deletes the flag file and rereads its settings or whatever it is that the small GUI app has changed. The reason to delete the flag file is to stop it continually trying to process changed settings if nothing has changed.
There are lots of other ways to implement a conversation between a service and a user’s desktop session – for example the service might expose an API or web settings page on a particular port, or it could support protocols like MQTT. In fact there are lots of different ways, each with their own use cases and pros and cons.
This is very good and helpful information. I am using it as a shell to write my service app. How can I change the name of the service? If i change the project name, the executable is different but it stilo shows as ServiceDemoBlogdEmbarcaderp in teh windows service app.Thanks
You need to open the unit which contains the service controller. This usually looks like a form with nothing on it, and you might find it called TService1. Whatever it is called it is probably descended from TService so search for “Class(TService)” if you’re not sure where to find it.
With that form open in the form designer, open the Object Inspector (F11) – and look at the
ServiceStartName
andDisplayName
properties. That’s what you need to change.See the following image for an example: