Products:

- SvCom
- Shell+
- PerfUtils

Download:

- SvCom
- Shell+
- PerfUtils

Ordering:

- Order now
- Resellers

Support:

- Contact directly


Embarcadero Technology Partner

 
    Service with GUI frontend implementation  

The code below shows usage of two components: TsvWtsSession and TsvLaunchFrontEnd.

Let's start with basic actions: create a new service application and add service module to it. The TComService was used as a parent because we plan to implement COM-based interprocess communication between our service and its GUI.

The next step is to add all necessary components to it:

service module with LaunchFrontEnd and WtsSessions components

Now we need to set up LaunchFronEnd properties. It can be done with Object Inspector but it will be more flexible to set up it at runtime:

 

procedure TGuiService.ComServiceCreate(Sender: TObject);
var S: String;
begin
    S:=ExtractFilePath(ParamStr(0));
    SetLength(S,Length(S)-1);
    S:=ExtractFilePath(S)+'011-GuiClient\GuiClient.exe';
    svLaunchFrontEnd.ApplicationName:=S;
end;

This code means that we expect to find the GuiClient.exe at path

../011-GuiClient/GuiClient.exe

Now we can launch the GUI at any time we need. For example we can do it immediately when service starts:

 

procedure TGuiService.ComServiceStart(Sender: TNtService;
  var DoAction: Boolean);
var i: Integer;
    Session: TsvWtsSessionInfo;
begin
    svWtsSessions.UpdateSessions;

    svWtsSessions.Lock;
    try
        for i:=0 to svWtsSessions.SessionCount-1 do
        begin
            Session:=svWtsSessions.Sessions[i];
            if (Session.State <> WTSActive) then continue;
            svLaunchFrontEnd.Launch(Session.SessionId);
        end;
    finally
        svWtsSessions.Unlock;
    end;
end;

A few notes about this code:

  •  First of all we call UpdateSessions to refresh list of current sessions and their states.
  • We use Lock to get access to list of sessions. When session list is not necessary we release it with Unlock call. Sure it is placed into "finally" section.
  • We ignore inactive sessions
  • If the session is active then we launch GUI with simple call. The only parameter indicates in what sessions the GUI should be launched. The TsvLaunchFrontEnd component does the job.

It may be useful to receive notification when the process is launched. It is easy to implement, just add OnProcessLaunch handler:

 

procedure TGuiService.svLaunchFrontEndProcessLaunch(Sender: TObject;
  Process: TsvLaunchedProcess);
begin
    EventLog.LogMessage('It is really launched!');
end;

Another notification is fired when the process terminates:

 

procedure TGuiService.svLaunchFrontEndProcessTerminate(Sender: TObject;
  Process: TsvLaunchedProcess);
var Session: TsvWtsSessionInfo;
begin
    svWtsSessions.UpdateSessions;

    svWtsSessions.Lock;
    try
        Session:=svWtsSessions.SessionsById[Process.SessionId];

        if not Assigned(Session) or (Session.State <> WTSActive) then
        begin
            Process.Free; // We should cleanup LaunchedProcess 
                          // entries if we do not need it more
            exit;
        end;
    finally
        svWtsSessions.Unlock;
    end;

    svLaunchFrontEnd.Launch(Process.SessionId);
end;

Important thing is that the TsvLaunchFrontEnd component maintains the list of launched processes. The list can be accessed using LaunchedProcess[index], count of items can be get through LaunchedProcessCount. Items of the list are TsvLaunchedProcess. They are linked to the list, freeing of item removes it the from list too.

As you can see in the snippet above we check that GUI was closed by user and re-launch it again. Try to run the example and you will see that GUI always persists even it was terminated with task manger.

Another point where the process can be launched is the new user logon:

 

procedure TGuiService.svWtsSessionsSessionStateChanged(Sender: TObject;
  Session: TsvWtsSessionInfo);
var i: Integer;
    LP: TsvLaunchedProcess;
begin
    EventLog.LogMessage(Format('Session state changed : SessionId = %d'+
    '  State = %d SID=%s',
    [Session.SessionId, Integer(Session.State), svWtsSessions.LogonSid]));

    // Check whether the user is active
    // Exit if it is not
    if Session.State <> WTSActive then exit;

    // Look up for already launched process
    for i := svLaunchFrontEnd.LaunchedProcessCount-1 downto 0 do
    begin
        LP:=svLaunchFrontEnd.LaunchedProcess[i];
        if not LP.IsAlive then
        begin
            LP.Free; // We should cleanup LaunchedProcess 
                     //entries for terminated processes
            continue;
        end;
        if LP.SessionId = Session.SessionId then exit; // Yes, it still runs
    end;

    EventLog.LogMessage('Launching...');
    svLaunchFrontEnd.Launch(Session.SessionId);
    EventLog.LogMessage('Launched');
end;

This code does a lot of checks:

  • It ignores inactive sessions
  • It walks through the list of launched processes. If GUI is alive then it exists
  • In other case it launches the GUI
  • It logs all events into system eventlog.

Finally add the timer handler to update sessions state regularly:

 

procedure TGuiService.svTimerTimer(Sender: TObject);
begin
    svWtsSessions.UpdateSessions;
end;

The code is finished and you can test it. Start the service, the GUI will appear. Try to close it and it will restart again. Try to logon another user and it will receive its own GUI. You can logon locally or using RDP, in any case you will see the GUI.

Return to examples index for more sample services.

<< . index . 1 . 2 . 3 . 4 . >>
SvCom links:

- Overview
- Download
- Purchase
- FAQ
- Tutorial
- What's new
- For registered users
- Support

Copyright:

© 1998-2003. Alexey Dynnikov
© 2003-2016. ALDYN Software
© Design: Adonis i-cafe.