Delphi Developing Windows Services in Windows 11: Best Practices and Tools

When you develop Windows software, you come across certain kinds of applications that need to run 24 hours a day, or, in fact, run continuously while the computer is running. Typically, these computers are web servers or monitoring applications on desktops. In these cases, you might consider creating a console application that either has a minimal amount of interaction, or is completely silent, but there are better solutions than that.

Simple console or minimized normal GUI applications can face issues like Windows session termination, restart and user permissions. The solution to this problem is to develop a windows service. In this tutorial, we'll take a deep dive into building Windows services with Delphi. After this, you can use it as a template for your windows service.

Table of contents

Why do I need to create a windows service?

What are the prerequisites for developing a Windows service?

How do Windows service applications work?

When do you need to write a Windows service?

How to create a windows service project in Delphi?

How to implement a Windows service application with Delphi?

How do we implement Windows service application functionality?

Test the service using Windows Service Manager

Debug service application


Why do I need to create a windows service?

There are many reasons for creating long-running services, such as:

  • Process CPU-intensive data.
  • Work items queued in the background.
  • Perform time-based operations on timetables
  • Unobtrusive manipulation of "helper" or utility functions

Background service processing usually does not involve a user interface (UI), but a UI can be built around them .

What are the prerequisites for developing a Windows service?

Before diving into service application development, I suggest you take a look at the latest version of the Delphi IDE. With a plethora of enhancements and new features, the development process goes even smoother. In addition, you can use the Delphi Community Edition for free to get familiar with the Delphi programming language and its syntax.

How do Windows service applications work?

Service applications receive requests from client applications, process those requests, and return information to the client applications. A Web, FTP or email 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. In this tutorial, we will create a Windows service application with Delphi.

When a service program is running, it can be set to use a set of default user rights, either as a virtual "service user", or as an actual ordinary user with access to the system. Importantly, these user permissions affect the folders your service application has access to, as well as any "mapped" network folders -- if someone creates a mapping, such as a "Z" pointing to a network folder: "The drive is most likely unusable. You should always use full UNC path names where possible -- or use Windows system calls or Delphi's runtime TPath type functions to get the correct location of special folders, such as the location of the %APPDATA% folder and the " My Documents" type virtual path.

A Windows service application can provide many functions. Look at the list when you start the Services utility. These services can be the core of the main GUI application you use.

When do you need to write a Windows service?

Some time ago, I needed a system monitoring tool to monitor the free disk space on our file server. I wrote a tool that checks every minute and writes this information to a log file. However, it requires a user to be logged in, and when the user logs out, my app closes. The solution is to recreate the program as a Windows service, and then keep running when the computer is turned on, even if the user is not logged on.

While RAD Studio is optimized for typical user-facing interactive applications with Delphi or C++ Builder, it is more capable of easily creating service applications.

How to create a windows service project in Delphi?

To create a new project in Delphi, you need to have the Delphi development environment installed on your computer. Once you have Delphi installed and running, you can start creating a new project.

To create a new Windows service project with Delphi in RAD Studio, take the following steps:

  1. Click the File menu and select New -> Other. This will bring up the New Project dialog.
  2. On the left side of the dialog, select Delphi, then select the Windows Service project type from the Windows category.
  3. Click "OK" to create a new project.

 Note that the process is similar for those using C++ Builder.

Once the new project is created, you will need to set 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. In this dialog, you can set various options for your project, such as target platform, output directory, and compiler options. Before continuing to develop your service, make sure to set these options according to your project requirements.

How to implement a Windows service application with Delphi?

After creating a new Windows Service project in Delphi and setting 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 usually defaulted to "Unit1.pas". To get started, double-click 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 the Delphi service application. To add your own code in the unit, you need to define the behavior of the service by handling various service events. These events include start, stop, and pause events, which are fired when the service starts, stops, or pauses, respectively. In order 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 behavior of the service, such as "start" and "stop" methods. For example, you can use an "OnStart" event handler to define code that should be executed when the service starts. To do this, you can add the following code to the "Unit1.pas" file:





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;

In addition to start and stop events, you may also need to handle pause events, which are fired 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:

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;

Inside each event handler, you can add your own code to define the behavior of the service when the respective event is fired. For example, you can use the "Start" and "Stop" methods of the "TService" component to start or stop a timer or a thread, or you can use the "Pause" method to pause the execution of a task.

Once you've added code to the main service unit and handled the service's events, you can continue debugging and testing the service using the Delphi debugger and Windows Service Manager.

How do we implement Windows service application functionality?

Add the Vcl.SvcMgr unit in the Unit's Uses clause .

Change the ancestor of the main form to TService by modifying the declaration of the TForm class , as follows

type
  TService1 = class(TService)

Here's the full source code:

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 code here is as follows:

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 create the service. In the project window, you can open the context menu and create the service like this:

Delphi builds Windows service

Test the service using Windows Service Manager

You can use Windows Service Manager to test the service. This tool allows you to start, stop, pause and resume a service, and view its status and any errors that may be encountered.

To install the service, you should follow these steps:

  • Go to the folder, open it in terminal (the executable should be configured to run with administrator privileges)
  • Enter the service name with the "/install" command

Go into that folder and open in terminal

Install Windows Service Using Terminal

Once the service is installed, you can use the Windows Service Manager to start, stop, pause or resume the service. To do this, open the Services application in the Windows Control Panel's Administrative Tools folder. Find the service in the list of services, right-click it, and select the desired action from the context menu.

Here you can start, stop and pause the service

After a few seconds, stop the service from the service manager and check the log files.

appendix:

Debug service application

You can debug a service application by attaching to the service application process while the service application is already running (that is, starting the service and then attaching to the debugger). To attach to the service application process, choose  Run > Attach To Process and select the service application in the dialog that appears.

In some cases, this method may fail due to insufficient permissions. If this happens, you can use the service control manager to enable your service to work with a debugger:

To debug:

  1. First create a key named Image File Execution Options in the following registry location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
  2. Create a subkey with the same name as your service (for example, MYSERV.EXE). On this subkey, add a value of type REG_SZ named Debugger. Use the full path to bds.exe as the string value.
  3. In the Services control panel applet, select your service, click Start and check Allow service to interact with desktop.

For Windows NT:

On Windows NT systems, you can use another method to debug service applications. However, this approach can be tricky as it requires very short time intervals:

  1. First, start the application in the debugger. Wait a few seconds until it finishes loading.
  2. Quickly start the service from the control panel or the command line: start MyServ

You have to start the service quickly (within 15-30 seconds after the application starts), because the application will terminate if the service is not started.

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.

Guess you like

Origin blog.csdn.net/sensor_WU/article/details/131252304