Recent

Author Topic: Run application from a Windows service  (Read 5692 times)

arodriguezfarfan

  • New member
  • *
  • Posts: 7
Run application from a Windows service
« on: December 01, 2021, 04:19:24 pm »
I'm trying to create a Windows service that after a certain load time runs an external application.

The idea is that if it is detected that the necessary configuration has not been made, a program is loaded with which the necessary parameters can be configured.

The attachment contains the sample project that I am using with which I charge notepad.exe, however the problem I have is that at the time of loading the program it does not show the window although it appears in the Task Manager. My question is whether I want to do it or what am I doing wrong?

Any suggestions are welcome

dseligo

  • Sr. Member
  • ****
  • Posts: 446
Re: Run application from a Windows service
« Reply #1 on: December 01, 2021, 05:57:22 pm »
It could be the user under which you run service.

GetMem

  • Hero Member
  • *****
  • Posts: 3638
Re: Run application from a Windows service
« Reply #2 on: December 01, 2021, 08:21:52 pm »
@arodriguezfarfan

Quote
I'm trying to create a Windows service that after a certain load time runs an external application.
You have to break session 0 isolation. For security reasons, since windows vista, the services run in session 0. The first user logs on to session 1, the second one to session 2 and so on. Session 0 cannot interact with session 1,2...x, so you cannot show an UI element from your service. You have to start the external application in user space.  CreateProcessAsUser api for example will run the application in the security context of the user.

arodriguezfarfan

  • New member
  • *
  • Posts: 7
Re: Run application from a Windows service
« Reply #3 on: December 02, 2021, 02:27:54 am »
Thanks Getmen for the explanation. I researched about CreateProcessAsUser, but I think what I intend to do is not the right thing to do, so I'm going to look for another alternative. Thank you.

GetMem

  • Hero Member
  • *****
  • Posts: 3638
Re: Run application from a Windows service
« Reply #4 on: December 02, 2021, 07:02:29 am »
@arodriguezfarfan

Quote
Thanks Getmen for the explanation.
You're welcome!

Quote
I researched about CreateProcessAsUser, but I think what I intend to do is not the right thing to do
Sometimes there are good reasons to run a process from a service, then exchange message between them via inter process communication(sockets, pipes, named pipes, memory mapped files, etc...). If you change your mind, you can try something like this:
Code: Pascal  [Select][+][-]
  1. function FindWinlogon(ASessionId: DWord): DWord;
  2. var
  3.   PID, SessionId: DWord;
  4.   hSnap: THANDLE;
  5.   procEntry: TProcessEntry32;
  6. begin
  7.   Result := 0;
  8.   hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  9.   if (hSnap <> INVALID_HANDLE_VALUE) then
  10.   begin
  11.     procEntry.dwSize := Sizeof(PROCESSENTRY32);
  12.     PID := 0;
  13.     SessionId := 0;
  14.     if Process32First(hSnap, procEntry) then
  15.     begin
  16.       while Process32Next(hSnap, procEntry) do
  17.       begin
  18.         if Pos('winlogon.exe', LowerCase(procEntry.szExeFile)) > 0 then
  19.         begin
  20.           if ProcessIdToSessionId(procEntry.th32ProcessID, SessionId) then
  21.           begin
  22.             if SessionId = ASessionId then
  23.             begin
  24.               PID := procEntry.th32ProcessID;
  25.               Break;
  26.             end;
  27.           end
  28.         end;
  29.       end;
  30.       Result := PID;
  31.     end;
  32.   end;
  33. end;
  34.  
  35. function RunProcessAsUser(APath: WideString): DWord;
  36. var
  37.   pi: PROCESS_INFORMATION;
  38.   si: STARTUPINFOW;
  39.   dwSessionId, PID, DesiredAccess, dwCreationFlags: DWord;
  40.   hUserTokenDup, hPToken, hProcess: THANDLE;
  41. begin
  42.   Result := 0;
  43.   RevertToSelf;
  44.   dwSessionId := WtsGetActiveConsoleSessionID;
  45.   PID := FindWinLogon(dwSessionId);
  46.   if PID <> 0 then
  47.   begin
  48.     hPToken := 0;
  49.     hProcess := OpenProcess(MAXIMUM_ALLOWED, False, PID);    
  50.     DesiredAccess := TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_DUPLICATE or
  51.                      TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID or TOKEN_READ or TOKEN_WRITE;
  52.     if OpenProcessToken(hProcess, DesiredAccess, hPToken) then
  53.     begin
  54.       hUserTokenDup := 0;
  55.       if DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserTokenDup) then
  56.       begin
  57.         ZeroMemory(@si, SizeOf(si));
  58.         ZeroMemory(@pi, SizeOf(pi));
  59.         dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT;
  60.         if CreateProcessAsUserW(hUserTokenDup, PWideChar(APath), nil, nil, nil, False, dwCreationFlags, nil, nil, si, pi) then
  61.           Result := pi.dwProcessId
  62.       end
  63.     end
  64.   end;
  65. end;

Then inside your services, create a  worker thread, wait a few seconds, then run notepad "as user":
Code: Pascal  [Select][+][-]
  1. procedure TThreadWorker.Execute;
  2. var
  3.   WPath: WideString;
  4.   PID: DWord;
  5. begin
  6.   FTick := 0;
  7.   while not Terminated do
  8.   begin
  9.     Inc(FTick);
  10.     Sleep(1000);
  11.     if FTick = 5 then
  12.     begin
  13.       WPath := 'c:\windows\notepad.exe';
  14.       PID := RunProcessAsUser(WPath);
  15.       if PID > 0 then
  16.         //notepad successfully executed
  17.       Break;
  18.     end;  
  19.   end;
  20. end;


PS: Actually I ran a few test and notepad starts fine, with a nice visible window.
« Last Edit: December 02, 2021, 07:23:13 am by GetMem »

PascalDragon

  • Hero Member
  • *****
  • Posts: 3646
  • Compiler Developer
Re: Run application from a Windows service
« Reply #5 on: December 02, 2021, 08:57:04 am »
Thanks Getmen for the explanation. I researched about CreateProcessAsUser, but I think what I intend to do is not the right thing to do, so I'm going to look for another alternative.

There is no alternative except for using the functionality provided by the operating system as it's intended.

arodriguezfarfan

  • New member
  • *
  • Posts: 7
Re: Run application from a Windows service
« Reply #6 on: December 02, 2021, 03:15:57 pm »
Thank you Getmen for the solution you sent me, I will run the published code. I hope to see the notebook start fine, with a nice window visible  :D :D :D :D :D :D :D :D.

Thank you. 

GetMem

  • Hero Member
  • *****
  • Posts: 3638
Re: Run application from a Windows service
« Reply #7 on: December 02, 2021, 04:10:12 pm »
I forgot to attach my test daemon. After you build the executable, run _install.bat form the same folder. If the install was successful, start the service. In order for the notepad window to appear, you have to wait approximately five seconds. If something went wrong check log.txt.

PS: Tested with Lazarus Trunk/FPC 3.2.0(32 bit) on a 64 bit Windows

arodriguezfarfan

  • New member
  • *
  • Posts: 7
Re: Run application from a Windows service
« Reply #8 on: December 03, 2021, 03:57:26 pm »
Thank you Getmen. The project works perfectly. Now I have to study it and implement in my project.



GetMem

  • Hero Member
  • *****
  • Posts: 3638
Re: Run application from a Windows service
« Reply #9 on: December 03, 2021, 04:32:10 pm »
Thank you Getmen. The project works perfectly. Now I have to study it and implement in my project.
You are welcome, I'm glad it's working.
I made a few improvement, now the worker thread is properly terminated and freed, no more memory leaks. However I cannot properly "self" shut down the service. It would be nice to find a method to terminate the service from inside, without using the service manager. Of course a Halt always work, but I'm not satisfied with this method.

GetMem

  • Hero Member
  • *****
  • Posts: 3638
Re: Run application from a Windows service
« Reply #10 on: December 04, 2021, 10:05:48 am »
@GetMem
Quote
However I cannot properly "self" shut down the service. It would be nice to find a method to terminate the service from inside, without using the service manager. Of course a Halt always work, but I'm not satisfied with this method.

I will answer my own question. The idea is to pass the daemon to the workerthread constructor(FDaemon), when it's time to shut down the service:
Code: Pascal  [Select][+][-]
  1. var
  2.   Daemon: TCustomDaemon;
  3.   DaemonThread: TDaemonThread;
  4. begin
  5.   Daemon := TCustomDaemon(FDaemon);
  6.   DaemonThread := TDaemonThread(Daemon.DaemonThread);
  7.   DaemonThread.ShutDownDaemon;
  8. end;
  9.  

It works very nicely. The method though is somewhat convoluted. I would implement a procedure inside TCustomDaemon(daemonapp.pp) class to achieve the same thing. I wonder if a patch would be accepted.

PascalDragon

  • Hero Member
  • *****
  • Posts: 3646
  • Compiler Developer
Re: Run application from a Windows service
« Reply #11 on: December 06, 2021, 01:51:20 pm »
It works very nicely. The method though is somewhat convoluted. I would implement a procedure inside TCustomDaemon(daemonapp.pp) class to achieve the same thing. I wonder if a patch would be accepted.

If you make clear what the use case is and what implications it has you might at least try...

 

TinyPortal © 2005-2018