Run application from a Windows service lazarus / 34-Windows ---------------------------------------------------------------------- arodriguezfarfan · 2021-12-01 15:19 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 · 2021-12-01 16:57 It could be the user under which you run service. ---------------------------------------------------------------------- balazsszekely · 2021-12-01 19:21 @arodriguezfarfan QuoteI'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 · 2021-12-02 01:27 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. ---------------------------------------------------------------------- balazsszekely · 2021-12-02 06:02 @arodriguezfarfan QuoteThanks Getmen for the explanation.You're welcome! QuoteI researched about CreateProcessAsUser, but I think what I intend to do is not the right thing to doSometimes 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function FindWinlogon(ASessionId: DWord): DWord;var  PID, SessionId: DWord;  hSnap: THANDLE;  procEntry: TProcessEntry32;begin  Result := 0;  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);  if (hSnap <> INVALID_HANDLE_VALUE) then  begin    procEntry.dwSize := Sizeof(PROCESSENTRY32);    PID := 0;    SessionId := 0;    if Process32First(hSnap, procEntry) then    begin      while Process32Next(hSnap, procEntry) do      begin        if Pos('winlogon.exe', LowerCase(procEntry.szExeFile)) > 0 then        begin          if ProcessIdToSessionId(procEntry.th32ProcessID, SessionId) then          begin            if SessionId = ASessionId then            begin              PID := procEntry.th32ProcessID;            Break;            end;          end        end;      end;    Result := PID;    end;  end;end; function RunProcessAsUser(APath: WideString): DWord;var  pi: PROCESS_INFORMATION;  si: STARTUPINFOW;  dwSessionId, PID, DesiredAccess, dwCreationFlags: DWord;  hUserTokenDup, hPToken, hProcess: THANDLE;begin  Result := 0;  RevertToSelf;  dwSessionId := WtsGetActiveConsoleSessionID;  PID := FindWinLogon(dwSessionId);  if PID <> 0 then  begin    hPToken := 0;  hProcess := OpenProcess(MAXIMUM_ALLOWED, False, PID);        DesiredAccess := TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_DUPLICATE or                      TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID or TOKEN_READ or TOKEN_WRITE;    if OpenProcessToken(hProcess, DesiredAccess, hPToken) then    begin      hUserTokenDup := 0;      if DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, nil, SecurityIdentification, TokenPrimary, hUserTokenDup) then      begin        ZeroMemory(@si, SizeOf(si));        ZeroMemory(@pi, SizeOf(pi));        dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT;        if CreateProcessAsUserW(hUserTokenDup, PWideChar(APath), nil, nil, nil, False, dwCreationFlags, nil, nil, si, pi) then          Result := pi.dwProcessId      end    end  end;end; Then inside your services, create a  worker thread, wait a few seconds, then run notepad "as user": Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TThreadWorker.Execute;var  WPath: WideString;  PID: DWord;begin  FTick := 0;  while not Terminated do  begin    Inc(FTick);    Sleep(1000);  if FTick = 5 then    begin      WPath := 'c:\windows\notepad.exe';    PID := RunProcessAsUser(WPath);      if PID > 0 then        //notepad successfully executed      Break;    end;     end;end; PS: Actually I ran a few test and notepad starts fine, with a nice visible window. ---------------------------------------------------------------------- PascalDragon · 2021-12-02 07:57 Quote from: arodriguezfarfan on December 02, 2021, 02:27:54 amThanks 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 · 2021-12-02 14:15 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  . Thank you. ---------------------------------------------------------------------- balazsszekely · 2021-12-02 15:10 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 · 2021-12-03 14:57 Thank you Getmen. The project works perfectly. Now I have to study it and implement in my project. ---------------------------------------------------------------------- balazsszekely · 2021-12-03 15:32 Quote from: arodriguezfarfan on December 03, 2021, 03:57:26 pmThank 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. ---------------------------------------------------------------------- balazsszekely · 2021-12-04 09:05 @GetMem QuoteHowever 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  Daemon: TCustomDaemon;  DaemonThread: TDaemonThread;begin  Daemon := TCustomDaemon(FDaemon);  DaemonThread := TDaemonThread(Daemon.DaemonThread);  DaemonThread.ShutDownDaemon;end;  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 · 2021-12-06 12:51 Quote from: GetMem on December 04, 2021, 10:05:48 amIt 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...