Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Delphi Cookbook

You're reading from   Delphi Cookbook Recipes to master Delphi for IoT integrations, cross-platform, mobile and server-side development

Arrow left icon
Product type Paperback
Published in Jul 2018
Publisher
ISBN-13 9781788621304
Length 668 pages
Edition 3rd Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Daniele Teti Daniele Teti
Author Profile Icon Daniele Teti
Daniele Teti
Daniele Spinetti Daniele Spinetti
Author Profile Icon Daniele Spinetti
Daniele Spinetti
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Delphi Basics FREE CHAPTER 2. Becoming a Delphi Language Ninja 3. Knowing Your Friends – The Delphi RTL 4. Going Cross-Platform with FireMonkey 5. The Thousand Faces of Multithreading 6. Putting Delphi on the Server 7. Linux Development 8. Riding the Mobile Revolution with FireMonkey 9. Using specific platform features 10. Delphi and IoT 11. Other Books You May Enjoy

Creating a Windows Service

Some kinds of application need to be running 24/7. Usually, they are network servers or data transfer/monitoring applications. In these cases, you probably start with a normal GUI or console application. However, when the systems start to be used in production, you are faced with a lot of problems related to Windows session termination, reboots, user rights, and other issues related to the server environment.

Getting ready

The way to go, in the previous scenario, is to develop a Windows Service.

In this recipe, we'll see how to write a good Windows Service scaffold, and this can be the skeleton for many other services. So, feel free to use this code as a template to create all the services that you will need.

How it works...

The project has been created using the default project template accessible by going to File | New | Other | Delphi Projects | Service Application; it has been integrated with a set of functionalities to make it real.

All the low-level interfacing with the Windows Service Manager is done by the TService class. In ServiceU.pas, there is the actual descendant of TService that represents the Windows Service we are implementing. Its event handlers are used to communicate with the operating system.

Usually, a service needs to respond to Windows Service Controller commands independently of what it is doing. So, we need a background thread to do the actual work, while the TService.OnExecute event should not do any real work (this is not a must, but usually is the way to go). The unit named WorkerThreadU.pas contains the thread and the main service needed to hold a reference to the instance of this thread.

The background thread starts when the service is started (the OnStart event) and stops when the service is stopped (the OnStop event). The OnExecute event waits and handles ServiceController commands but doesn't do any actual functional work. This is done using ServiceThread.ProcessRequests(false) in a while loop.

Usually, the OnExecute event handler looks like this:

procedure TSampleService.ServiceExecute(Sender: TService); 
begin 
  while not Terminated do 
  begin 
    ServiceThread.ProcessRequests(false); 
    TThread.Sleep(1000);  
  end; 
end; 

The wait of 1,000 milliseconds is not a must, but consider that the wait time should not be too high because the service needs to be responsive to the Windows Service Controller messages. It should not be too low because otherwise the thread context switch may waste resources.

The background thread writes a line in a logfile once a second. While it is in a Paused state, the service stops writing. When the service continues, the thread will restart writing the log line. In the service event handlers is the logic to implement this change of state:

procedure TSampleService.ServiceContinue(Sender: TService; 
var Continued: Boolean); begin FWorkerThread.Continue; Continued := True; end; procedure TSampleService.ServicePause(Sender: TService;
var Paused: Boolean); begin FWorkerThread.Pause; Paused := True; end;

In the thread, there is actual logic to implement the Paused state, and in this case, it is fairly simple; we pause the writing of the logfile:

Here's an extract:

    Log := TStreamWriter.Create( 
      TFileStream.Create(LogFileName, 
fmCreate or fmShareDenyWrite)); try while not Terminated do begin if not FPaused then begin Log.WriteLine('Message from thread: ' + TimeToStr(now)); end; TThread.Sleep(1000); end; finally Log.Free; end;

The FPaused Boolean instance variable can be considered thread safe for this use case.

Delphi services don't have a default description under the Windows Service Manager. If we want to give them a description, we have to write a specific key in the Windows registry. Usually, this is done in the AfterInstall event. In our service, this is the code to write in the AfterInstall event handler:

procedure TSampleService.ServiceAfterInstall( 
Sender: TService); 
var 
  Reg: TRegistry; //declared in System.Win.Registry; 
begin 
  Reg := TRegistry.Create(KEY_READ or KEY_WRITE); 
  try 
    Reg.RootKey := HKEY_LOCAL_MACHINE; 
    if Reg.OpenKey( 
      'SYSTEM\CurrentControlSet\Services\' + name,  
      False {do not create if not exists}) then 
    begin 
      Reg.WriteString('Description',  
        'My Fantastic Windows Service'); 
      Reg.CloseKey; 
    end; 
  finally 
    Reg.Free; 
  end; 
end; 

It is not necessary to delete this key in the AfterUnInstall event because Windows deletes all the keys related to the service (under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<MyServiceName>) when the service is actually uninstalled.

Let's try an installation. Build the project, open the Windows command prompt (with administrator-level privileges), and go to the folder where the project has been built. Then, run this command:

C:\<ExeProjectPath>\WindowsService.exe /install  

If everything is okay, you should see this message:

Figure 1.17: The service installation is okay

Now, you can check under the Windows Services Console, and you should find the service installed. Click on Start, wait for the confirmation, and the service should start to write to its logfile.

Play with Pause and Continue and check the file activity.

Some text editors could have a problem opening the logfile while the service is writing. I suggest that you use a Unix tail clone for Windows.

There are many free choices. Here are some links:

There's more...

Windows Services are very powerful. Using the abstractions that Delphi provides, you can also create an application that, reading a parameter on the command line, can act as a normal GUI application or as a Windows Service.

In the respective recipe folder, there is another recipe folder called 20_WindowsServiceOrGUI.

This application can be used as a normal Windows Service using the normal command-line switches used so far, but if launched with /GUI, it acts as a GUI application and can use the same application code (not TService). In our example, the GUI version uses the same worker thread as the service version. This can be very useful for debugging purposes also.

Run the application with the following command:

C:\<ExeProjectPath>\WindowsServiceOrGUI.exe /GUI  

You will get a GUI version of the service, as shown here:

Figure 1.18: The GUI version of the Windows Service

Using the TService.LogMessage method

If something happens during the execution of the service that you want to log, and you want to log in to the system logger, you can use the LogMessage method to save a message. The message can be viewed later using the Windows built-in event viewer.

You can call the LogMessage method using an appropriate logging type, like this:

LogMessage('Your message goes here for SUCCESS', 
EVENTLOG_SUCCESS, 0, 1);

If you check the event in the Event Viewer, you will find a lot of garbage text that complains about the lack of description for the event.

If you really want to use the Event Viewer to view your log message (when I can, I use a logfile and don't concern myself with the Event Viewer, but there are scenarios where the Event Viewer log is needed), you have to use the Microsoft © Message Compiler.

The Microsoft © Message Compiler is a tool able to compile a file of messages into a set of RC files. Then, these files must be compiled by a resource compiler and linked into your executable.

More information on Microsoft © Message Compiler and the steps needed to provide the description for the log event can be found at http://www.codeproject.com/Articles/4166/Using-MC-exe-message-resources-and-the-NT-event-lo.

You have been reading a chapter from
Delphi Cookbook - Third Edition
Published in: Jul 2018
Publisher:
ISBN-13: 9781788621304
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image