Recent

Author Topic: Detecting user logon and logoff in a Windows service (Lazarus daemon)  (Read 3801 times)

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
I have a Windows service created in Lazarus that can access a remote MySQL database and read and update records there. The service was not all my own work but was put together years ago with the help of forum members. I'm now looking for a way to detect user logon and logoff by handling SERVICE_CONTROL_SESSIONCHANGE. One piece of advice on line says that this isn't difficult in a service, but only gives code snippets in C++, of which I have very little understanding.   

I've found a thread here from 6 years ago which makes it look as if this is difficult and refers to directly hooking into FPC source code. Obviously FPC will have changed a lot since then and I wonder if there's now a simpler way to do this...?

I'd be grateful for any hints or code snippets (with advice as to where they should go in the service).
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

GetMem

  • Hero Member
  • *****
  • Posts: 3757

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Maybe this thread can help: https://forum.lazarus.freepascal.org/index.php/topic,24826.0.html

Thanks. That's the thread I was referring to. Does what was said in 2014 still apply?
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

ASBzone

  • Sr. Member
  • ****
  • Posts: 476
  • Automation leads to relaxation...
    • Free BrainWaveCC Console Utilities
Maybe this thread can help: https://forum.lazarus.freepascal.org/index.php/topic,24826.0.html

Thanks. That's the thread I was referring to. Does what was said in 2014 still apply?

Are you trying to capture or track when the service is logged on or off?  Or when specific users logon or logoff of Windows in general?
-ASB: https://www.BrainWaveCC.com

Lazarus v2.0.11 r63516 / FPC v3.2.1-r46879 (via FpcUpDeluxe) -- Windows 64-bit install w/32-bit cross-compile
Primary System: Windows 10 Pro x64, Version 2004 (Build 19041.508)
Other Systems: Windows 10 Pro x64, Version 2004 or greater

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Maybe this thread can help: https://forum.lazarus.freepascal.org/index.php/topic,24826.0.html

Thanks. That's the thread I was referring to. Does what was said in 2014 still apply?

Are you trying to capture or track when the service is logged on or off?  Or when specific users logon or logoff of Windows in general?

It's user logons and logoffs I'm interested in, because the service will be set to automatic, so it starts at bootup and only closes at system shutdown. I think, if I can detect when the logon happens, getting the user name should be straightforward with a call to the Windows API.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Has anyone any hints on how to do this, and where in my existing service the code should go? What I want to do is record each user session in my database (already created and linked), so I need to detect each user logon and logoff - session end or system shutdown.

It seems as if this would be a frequent requirement in a Windows service and I'm disappointed that it's not easier or better-documented.

Thanks.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

fred

  • Full Member
  • ***
  • Posts: 189
I don't know much of it but in a windows network there is a policy for running a task at logon/logoff.
You can also start a scheduled task on user logon and system  events.
Started with OmegaSoft Pascal on OS-9/68k , now Lazarus 2.0.8 / FPC 3.0.4 on Windows 7

PascalDragon

  • Hero Member
  • *****
  • Posts: 2248
  • Compiler Developer
I have not tested it, but in essence you need to add a class that inherits from TDaemonController where you override the virtual Controller method (this method will be called like the LPHANDLER_FUNCTION_EX).

Then you need to descend from TDaemonApplication and override CreateDaemonController where you return your TDaemonController descendant.

Finally set AppClass to your TDaemonApplication descendant first thing in your main project before any call to Application. The daemon framework should now use your classes and you can happily intercept the SERVICE_CONTROL_SESSIONCHANGE message.

Again: I have not tested this, but from what I can see it should work.

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
I have not tested it, but in essence you need to add a class that inherits from TDaemonController where you override the virtual Controller method (this method will be called like the LPHANDLER_FUNCTION_EX).

Then you need to descend from TDaemonApplication and override CreateDaemonController where you return your TDaemonController descendant.

Finally set AppClass to your TDaemonApplication descendant first thing in your main project before any call to Application. The daemon framework should now use your classes and you can happily intercept the SERVICE_CONTROL_SESSIONCHANGE message.

Again: I have not tested this, but from what I can see it should work.

Thanks for the hints - will give it a try.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #9 on: August 07, 2020, 11:25:05 am »
On further examination I find I can't fill all the required detail: in brief, I just don't understand enough about services and objects to do it. As this task must be a frequent requirement in services, I'm surprised that it's so difficult. (The main code in the linked thread is in daemonapp.pp and it contains no mention of SERVICE_CONTROL_SESSIONCHANGE).

I know it's a long shot but would appreciate any further details from someone who has successfully done this in FreePascal or Delphi.
« Last Edit: August 07, 2020, 02:56:39 pm by TyneBridges »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

PascalDragon

  • Hero Member
  • *****
  • Posts: 2248
  • Compiler Developer
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #10 on: August 08, 2020, 01:23:25 pm »
I've now tried this myself. While my solution was correct per se, it wasn't enough. Thus I have now implemented this in FPC 3.3.1, to make it easier to utilize this. You can either use FPC trunk or you can backport the changes (see at the bottom).

Now if you create a new Daemon application (I assume you use the Lazarus package for that) you need to go to your daemon's definition in the Daemon Mapper and inside WinBindings.AcceptedCodes enable wccSessionChange. In your daemon you then add an event handler for the new OnControlCodeEvent (not OnControlCode!). The parameters of this event handler will be like those for LPHANDLER_FUNCTION_EX (minus the lpContext parameter).

For example (the constants used here are in JwaWinSvc and JwaWinUser):

Code: Pascal  [Select][+][-]
  1. procedure TDaemon1.DataModuleControlCodeEvent(Sender: TCustomDaemon; ACode,
  2.   AEventType: DWord; AEventData: Pointer; var Handled: Boolean);
  3. var
  4.   evtype, sessionid: String;
  5. begin
  6.   if ACode = SERVICE_CONTROL_SESSIONCHANGE then begin
  7.     case aEventType of
  8.       WTS_CONSOLE_CONNECT:
  9.         evtype := 'Console Connect';
  10.       WTS_CONSOLE_DISCONNECT:
  11.         evtype := 'Console Disconnect';
  12.       WTS_REMOTE_CONNECT:
  13.         evtype := 'Remote Connect';
  14.       WTS_REMOTE_DISCONNECT:
  15.         evtype := 'Remote Disconnect';
  16.       WTS_SESSION_LOGON:
  17.         evtype := 'Session Logon';
  18.       WTS_SESSION_LOGOFF:
  19.         evtype := 'Session Logoff';
  20.       WTS_SESSION_LOCK:
  21.         evtype := 'Session Lock';
  22.       WTS_SESSION_UNLOCK:
  23.         evtype := 'Session Unlock';
  24.       WTS_SESSION_REMOTE_CONTROL:
  25.         evtype := 'Session Remote Control';
  26.       otherwise
  27.         evtype := Format('Unknown event type %d', [aEventType]);
  28.     end;
  29.  
  30.     if Assigned(aEventData) then
  31.       sessionid := Format('%d', [PWTSSESSION_NOTIFICATION(aEventData)^.dwSessionId])
  32.     else
  33.       sessionid := 'none';
  34.  
  35.     Logger.Info('Session Change of type ''%s'' with session %s', [evtype, sessionid]);
  36.   end;
  37. end;

What you do with the received session ID is up to you...



If you want to backport the changes to FPC 3.2.0:

You need the changes of revisions 46326 and 46327. Apply these to your 3.2.0 sources (those that are provided by Lazarus for example).

You then need to do a slight adjustment in packages/fcl-extra/src/win/daemonapp.inc: in TDaemonController.ReportStatus.GetAcceptedCodes you need to disable the handling of wccTimeChange, wccTriggerEvent and wccUserModeReboot (this way you don't need to add revision 46325 as well which might lead to the need to compile more than necessary).

Once you've done the changes you go to the packages/fcl-extra/src directory and execute the following:

Code: [Select]
fpc -FUpath\to\units -Fiwin daemonapp.pp
The path\to\units is the path where the compiler FPC 3.2.0 units are located in case of Lazarus that should be fpc\3.2.0\units\x86_64-win64\fcl-extra (or i386-win32 if you use the 32-bit variant).

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #11 on: August 09, 2020, 04:21:05 pm »
I've now tried this myself. While my solution was correct per se, it wasn't enough. Thus I have now implemented this in FPC 3.3.1, to make it easier to utilize this. You can either use FPC trunk or you can backport the changes (see at the bottom).
...

That's great, thanks! This looks like just what I need. I'm currently using Lazarus 2.0.8 with FPC 3.0.4, so I will upgrade and try it out. I'm really grateful for your help.

John
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

TyneBridges

  • Full Member
  • ***
  • Posts: 127
    • Personal blog
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #12 on: August 10, 2020, 04:28:01 pm »
PascalDragon wrote:
Quote
If you want to backport the changes to FPC 3.2.0:

You need the changes of revisions 46326 and 46327. Apply these to your 3.2.0 sources (those that are provided by Lazarus for example).

As these are both modifications of the same files, have you any tips or pointers for how to get just the updated bits of each .pp and .inc file? Using 3.3.1 looked simpler but I couldn't work out how to get and install it.

I'm also confused about the nature of the revisions - if the two sets are needed, that suggests the changes are not cumulative: can I assume the later version always takes priority?

Thanks.
« Last Edit: August 10, 2020, 05:28:54 pm by TyneBridges »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer
Tried .Net Core and was perpetually in knots, so am back here to try again with Windows clients

ASBzone

  • Sr. Member
  • ****
  • Posts: 476
  • Automation leads to relaxation...
    • Free BrainWaveCC Console Utilities
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #13 on: August 10, 2020, 09:33:51 pm »
I'm also confused about the nature of the revisions - if the two sets are needed, that suggests the changes are not cumulative: can I assume the later version always takes priority?

Version 3.3.1 is "trunk" or "development".   It is where all the new features go to percolate and blossom (to mix metaphors terribly).

Version 3.2.0 is current "stable" version.    So if you want to be otherwise on the stable release version, but have the advantage of this specific change, you'll have to pull it from the development version and back port it to stable version.

At some point when we get to FPC 3.4 (or even 4.0), it is hoped that all these new features will prove stable enough to end up in the new stable edition at that time (in the future)
-ASB: https://www.BrainWaveCC.com

Lazarus v2.0.11 r63516 / FPC v3.2.1-r46879 (via FpcUpDeluxe) -- Windows 64-bit install w/32-bit cross-compile
Primary System: Windows 10 Pro x64, Version 2004 (Build 19041.508)
Other Systems: Windows 10 Pro x64, Version 2004 or greater

PascalDragon

  • Hero Member
  • *****
  • Posts: 2248
  • Compiler Developer
Re: Detecting user logon and logoff in a Windows service (Lazarus daemon)
« Reply #14 on: August 11, 2020, 10:09:03 am »
PascalDragon wrote:
Quote
If you want to backport the changes to FPC 3.2.0:

You need the changes of revisions 46326 and 46327. Apply these to your 3.2.0 sources (those that are provided by Lazarus for example).

As these are both modifications of the same files, have you any tips or pointers for how to get just the updated bits of each .pp and .inc file? Using 3.3.1 looked simpler but I couldn't work out how to get and install it.

I'm also confused about the nature of the revisions - if the two sets are needed, that suggests the changes are not cumulative: can I assume the later version always takes priority?

Changes build upon each other. In this specific case they're independent, but in general apply them from oldest to newest.

I have attached the patches that correspond to the revisions. To apply them you go to fpc\3.2.0\source inside your Lazarus directory (in a command line or PowerShell terminal). Maybe do a backup of the packages\fcl-extra\src directory to be safe. Then you execute the following commands (the patch utility is shipped together with the FPC distribution that Lazarus provides, if you don't have fpc\3.2.0\bin\x86_64-win64 (or i386-win32 for 32-bit) in your PATH then you need to use the full path to that):

Code: [Select]
patch -Np 1 -i path\to\fcl-extra-46326.patch
patch -Np 1 -i path\to\fcl-extra-46327.patch

The output should look like this:

Code: [Select]
PS D:\lazarus\2.0.10\fpc\3.2.0\source> patch -Np 1 -i X:\fcl-extra-46326.patch
patching file `packages/fcl-extra/src/daemonapp.pp'
patching file `packages/fcl-extra/src/win/daemonapp.inc'
PS D:\lazarus\2.0.10\fpc\3.2.0\source> patch -Np 1 -i X:\fcl-extra-46327.patch
patching file `packages/fcl-extra/src/daemonapp.pp'
patching file `packages/fcl-extra/src/unix/daemonapp.inc'
patching file `packages/fcl-extra/src/win/daemonapp.inc'

After that you go to the packages\fcl-extra directory and execute the following command (the same remark regarding PATH as for patch applies for fpc):

Code: [Select]
fpc -FU..\..\..\units\x86_64-win64\fcl-extra -Fiwin daemonapp.pp
(Note: if you use a 32-bit Lazarus then you need to use i386-win32 instead of x86_64-win64)

After that you rebuild Lazarus and everything should be set.

Edit: forgot to attach the patches... :-[

 

TinyPortal © 2005-2018