Recent

Author Topic: How to intercept windows messages in a service ?  (Read 14220 times)

izd

  • New Member
  • *
  • Posts: 12
How to intercept windows messages in a service ?
« on: June 09, 2014, 07:08:18 pm »
Hi,

Porting a parental program to a service, I searched the web without finding any information about intercepting systems messages ( especially open/close session & shutdown ) in TCustomDaemon and so.

Thanks in advance !

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: How to intercept windows messages in a service ?
« Reply #1 on: June 09, 2014, 09:34:39 pm »
I'm not sure to understand fully your request.

Services usually don't have any windows: so they usually don't use windows messages (I'm not even sure they can receive all the messages usually sent to a desktop application).

Quote from: izd
... open/close session...

Do you mean "users sessions" ? I guess it's probably not easy to know when a user is logging or closing his session, as services are now completely separated from the desktop running environment.

Or do you mean "service start/stop" ? Registered services receive automatically this kind of events.


Quote from: izd
... shutdown...

You may eventually have a look to this msdn information page:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms685149%28v=vs.85%29.aspx


** EDIT **  And in daemonapp.pp
Code: [Select]
  TDaemon = Class(TCustomDaemon)
...
  Published
...
    Property OnShutDown : TDaemonEvent Read FOnShutDown Write FOnShutDown;
...
« Last Edit: June 09, 2014, 09:52:54 pm by ChrisF »

izd

  • New Member
  • *
  • Posts: 12
Re: How to intercept windows messages in a service ?
« Reply #2 on: June 10, 2014, 10:12:27 am »
Thanks ChrisF,

You understood well, I want the service able to intercept windows messages such opening & closing user session.
You confirm what I supposed, no way with standards events because no window handler.
So, how to do it ?
Creating an event handler ? Hook ? Something else ?

izd

  • New Member
  • *
  • Posts: 12
Re: How to intercept windows messages in a service ?
« Reply #3 on: June 10, 2014, 12:48:41 pm »
As you advice me, I've tried to play with RegisterServiceCtrlHandlerEx() with no success...

Did Anyone knows how to manipulate this function ?

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: How to intercept windows messages in a service ?
« Reply #4 on: June 10, 2014, 06:55:16 pm »
It's not as simple.

The Controller member of the TCustomDaemon class already uses the RegisterServiceCtrlHandlerEx API; and do need to handle the incoming events.

I've tried to find a simple way to redirect it or to use derived classes, but couldn't succeed 'till now.

izd

  • New Member
  • *
  • Posts: 12
Re: How to intercept windows messages in a service ?
« Reply #5 on: June 10, 2014, 09:54:07 pm »
Thanks you ChrisF for yours efforts,

There is something I don't catch, NT Service are not supposed to handle systems messages ?!  :o
Why is it so complicated ?
Is due to Lazarus/Fpc ? Is it the same with Delphi or another language ?

Anyway, RegisterServiceCtrlHandlerEx can't be call several times ?...
...So, what about Hook ? ( Hook that I manage so good as Handler  :-[).

izd

  • New Member
  • *
  • Posts: 12
Re: How to intercept windows messages in a service ?
« Reply #6 on: June 11, 2014, 09:09:30 am »
I've found the declaration of RegisterServiceCtrlHandlerEx in daemonapp.inc, I see the problem...

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: How to intercept windows messages in a service ?
« Reply #7 on: June 11, 2014, 12:21:53 pm »
I don't know what you really mean by "systems messages" but services don't uses messages as for the standard windows applications (in windows gui mode, I mean).

Anyway, the only working solution I've found until now is to patch directly the concerned fpc source code (which is of course not really recommended for maintenance purposes).

I've tried to redirect the callback function to my own one:
Code: [Select]
...
  T:=RegisterServiceCtrlHandlerEx(MYDAEMON_NAME,@MyServiceControlHandlerEntry,Controller);
...

function MyServiceControlHandlerEntry(dwControl,dwEventType: DWord; lpEventData,lpContext : Pointer) : DWord; StdCall;
const
  SErrNoControlContext = 'Not handling Control message without control context: (%d %d %d).';
begin
    If (Nil<>lpContext) then
      begin
        CallLog('MyServiceController: '+Format('ControlCode - EventType : %d - %d',[dwControl,dwEventType]));
        TDaemonController(lpContext).Controller(dwControl,dwEventType,lpEventData)
      end
    else
      Application.Log(etError,Format(SerrNoControlContext,[dwControl,dwEventType,lpEventData]));
   result:=NO_ERROR;
end;


This is OK. But I can't modify the reportstatus for it after that, in order to add the SERVICE_ACCEPT_SESSIONCHANGE option using the SetServiceStatus API.

So, either I'm doing it incorrectly (witch is quite possible as Windows seems to be "sensible" to the parameters in the SERVICE_STATUS structure); or it's impossible to modify twice the reportstatus for a given service when it's started (the first call being done into the fpc source code).


Attached a demonstration test with the patched fpc source code, as indicated above. I've just copied the source code from the fpc 2.6.4 version and put it directly into the test project directory (for demonstration purposes only). Only daemonapp.inc has been modified (see comment "Patch 001 ...").

First of all, modify the log filename and path inside the CallLog function in MyDaemon.lpr (see CLOG_FILENAME) according to your own need. I'm not using the application event log any more, because it's off when a shutdown is in process (it took me a while to figure that).

The name of this test service is "Test daemon" (as indicated in D.DisplayName): search for this name in the service manager to interact with it (i.e. start, stop, pause, continue) after you've installed it (i.e. mydaeamon -i).

All the main actions are logged, even the shutdown and the session change calls. The last ones are processed trough the standard HandleCustomCode function (with code=$0B). But you get only the main code here, not the EventType ones (like WTS_SESSION_LOGON and WTS_SESSION_LOGOFF, for instance).

izd

  • New Member
  • *
  • Posts: 12
Re: How to intercept windows messages in a service ?
« Reply #8 on: June 12, 2014, 09:52:38 pm »
WWaaaOoo !

That is a great job ! Thanks You !!  :)

I will need some times to understand everything but now, I have a way !

Un grand merci !

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: How to intercept windows messages in a service ?
« Reply #9 on: June 13, 2014, 09:06:37 pm »
You thanks are rather unmerited, as I've done only very minimal changes for this test: remember that daemonapp.pp and daemonapp.inc are just coming from the "real" source code of FPC; not mine.

Anyway, if you intend one day to introduce a modification request to include the support for the session change detection in FPC (I don't know if it has change to be accepted), I suggest you to process differently that I've done for this test. I've forced the SERVICE_ACCEPT_SESSIONCHANGE option to be always present; which is of course quite unnecessary for most of the "common" usage of daemons (or windows service).

So, attached the source code (daemon10-withpossiblepatch.zip) for a possible patch which is most respectful of the logic and the structures of the current FPC source code (changes are also minimal).

In daemonapp.pp: add a new option
Code: [Select]
...
  // Patch (doSessionChange added in list TDaemonOption)
  TDaemonOption = (doAllowStop,doAllowPause,doInteractive,doSessionChange);
...

In daemonapp.inc (windows version): new option treatment
Code: [Select]
...
    // Patch (doSessionChange processed)
    if doSessionChange in FDAemon.Definition.Options then
      Result := Result or SERVICE_ACCEPT_SESSIONCHANGE;
...

And finally in MyDaemon.lpr (my own test source code):
Code: [Select]
...
  D.Options:=D.Options+[doSessionChange];   // SessionChange added in ReportStatus
...

lazguy

  • Jr. Member
  • **
  • Posts: 78
Re: How to intercept windows messages in a service ?
« Reply #10 on: November 29, 2017, 05:39:27 pm »
Hello,

Why I'm having this error on the log file created by this example, what it means and how to solve it ?


14:24:03: TestDaemon: Daemon HandleCustomCode = 14


Thank you very much in advance !


I don't know what you really mean by "systems messages" but services don't uses messages as for the standard windows applications (in windows gui mode, I mean).

Anyway, the only working solution I've found until now is to patch directly the concerned fpc source code (which is of course not really recommended for maintenance purposes).

I've tried to redirect the callback function to my own one:
Code: [Select]
...
  T:=RegisterServiceCtrlHandlerEx(MYDAEMON_NAME,@MyServiceControlHandlerEntry,Controller);
...

function MyServiceControlHandlerEntry(dwControl,dwEventType: DWord; lpEventData,lpContext : Pointer) : DWord; StdCall;
const
  SErrNoControlContext = 'Not handling Control message without control context: (%d %d %d).';
begin
    If (Nil<>lpContext) then
      begin
        CallLog('MyServiceController: '+Format('ControlCode - EventType : %d - %d',[dwControl,dwEventType]));
        TDaemonController(lpContext).Controller(dwControl,dwEventType,lpEventData)
      end
    else
      Application.Log(etError,Format(SerrNoControlContext,[dwControl,dwEventType,lpEventData]));
   result:=NO_ERROR;
end;


This is OK. But I can't modify the reportstatus for it after that, in order to add the SERVICE_ACCEPT_SESSIONCHANGE option using the SetServiceStatus API.

So, either I'm doing it incorrectly (witch is quite possible as Windows seems to be "sensible" to the parameters in the SERVICE_STATUS structure); or it's impossible to modify twice the reportstatus for a given service when it's started (the first call being done into the fpc source code).


Attached a demonstration test with the patched fpc source code, as indicated above. I've just copied the source code from the fpc 2.6.4 version and put it directly into the test project directory (for demonstration purposes only). Only daemonapp.inc has been modified (see comment "Patch 001 ...").

First of all, modify the log filename and path inside the CallLog function in MyDaemon.lpr (see CLOG_FILENAME) according to your own need. I'm not using the application event log any more, because it's off when a shutdown is in process (it took me a while to figure that).

The name of this test service is "Test daemon" (as indicated in D.DisplayName): search for this name in the service manager to interact with it (i.e. start, stop, pause, continue) after you've installed it (i.e. mydaeamon -i).

All the main actions are logged, even the shutdown and the session change calls. The last ones are processed trough the standard HandleCustomCode function (with code=$0B). But you get only the main code here, not the EventType ones (like WTS_SESSION_LOGON and WTS_SESSION_LOGOFF, for instance).

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to intercept windows messages in a service ?
« Reply #11 on: November 29, 2017, 07:15:40 pm »
Why I'm having this error on the log file created by this example, what it means and how to solve it ?

14:24:03: TestDaemon: Daemon HandleCustomCode = 14

Service control code 14 is SERVICE_CONTROL_SESSIONCHANGE (per the MSDN documentation).
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to intercept windows messages in a service ?
« Reply #12 on: November 29, 2017, 07:20:52 pm »
This is OK. But I can't modify the reportstatus for it after that, in order to add the SERVICE_ACCEPT_SESSIONCHANGE option using the SetServiceStatus API.

So, either I'm doing it incorrectly (witch is quite possible as Windows seems to be "sensible" to the parameters in the SERVICE_STATUS structure); or it's impossible to modify twice the reportstatus for a given service when it's started (the first call being done into the fpc source code).

Of course it is possible to call SetServiceStatus() more than once in a service's lifetime.  You can call it as often as you want.  So yes, it is quite likely that you are simply using SetServiceStatus() incorrectly.

All the main actions are logged, even the shutdown and the session change calls. The last ones are processed trough the standard HandleCustomCode function (with code=$0B).

Service control code $0B is SERVICE_CONTROL_DEVICEEVENT.

But you get only the main code here, not the EventType ones (like WTS_SESSION_LOGON and WTS_SESSION_LOGOFF, for instance).

You have to successfully enable SERVICE_CONTROL_SESSIONCHANGE via SetServiceStatus() in order to get those events.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

lazguy

  • Jr. Member
  • **
  • Posts: 78
Re: How to intercept windows messages in a service ?
« Reply #13 on: November 30, 2017, 12:49:52 pm »
Thank you, Remy Lebeau

I am studying all this answers. Until now I am a little bit lost.

I would like to log users logon, logoff and lock. Do you have any tips for me to achieve this ? 


A doubt: If an user is logged on and shutdown Windows without log off prior to shutdown. The log off event will be fired automatically ?


If I have some more doubts for sure I will return here :-)
   
best regards


Why I'm having this error on the log file created by this example, what it means and how to solve it ?

14:24:03: TestDaemon: Daemon HandleCustomCode = 14

Service control code 14 is SERVICE_CONTROL_SESSIONCHANGE (per the MSDN documentation).

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to intercept windows messages in a service ?
« Reply #14 on: November 30, 2017, 08:51:11 pm »
I would like to log users logon, logoff and lock. Do you have any tips for me to achieve this ? 

Handling SERVICE_CONTROL_SESSIONCHANGE events is the way to do that in a service.

A doubt: If an user is logged on and shutdown Windows without log off prior to shutdown. The log off event will be fired automatically ?

I don't know, as my services never need to handle SERVICE_CONTROL_SESSIONCHANGE.  However, a service can register to receive SERVICE_CONTROL_PRESHUTDOWN and SERVICE_CONTROL_SHUTDOWN events during Windows shutdown.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018