Recent

Author Topic: fphttpapp and Windows Server Service  (Read 1448 times)

Gapes

  • Newbie
  • Posts: 1
fphttpapp and Windows Server Service
« on: December 06, 2024, 03:49:58 pm »
Hello,

with FreePascal I am looking to embed a web server into a service application on Windows Server. I verified with examples produced with fphttpapp that I could achieve the desired functionality with FreePascal. But it seems to me that an fphttpapp cannot be a daemonapp at the same time.
I also consulted examples of daemonapp made with FreePascal.

But my knowledge of Lazarus is insufficient to allow me to embed an http server in a daemonapp. I'm not looking for a ready-made code but simply the principle of declaring and running fphttp in the daemonapp.
Thanks. Regards.
« Last Edit: December 06, 2024, 04:18:23 pm by Gapes »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8783
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: fphttpapp and Windows Server Service
« Reply #1 on: December 07, 2024, 05:09:04 pm »
I don't remember all the details, but:
  • Ports: bind only 49152-65535, anything under those either require specific permissions or administrative privileges
  • Firewall (Windows built-in or 3rd party): allow the service to listen on that port
  • Service account permission: the account that will run the service must also be given the permission to bind ports
I think that's all I can remember. Even things may change in newer Windows version. Feel free to consult MSDN as this should be development platform neutral.

Fibonacci

  • Hero Member
  • *****
  • Posts: 643
  • Internal Error Hunter
Re: fphttpapp and Windows Server Service
« Reply #2 on: December 07, 2024, 05:54:44 pm »
Ports: bind only 49152-65535, anything under those either require specific permissions or administrative privileges

Running a service requires administrative privileges anyway.



I know nothing about this "daemonapp" thing, so I took my pure WinAPI service template and added the example code from fpWeb tutorial on wiki, and here it is, working, serving a web page on port 9000.

As admin, run with "i" to install, with "u" to uninstall.

Code: Pascal  [Select][+][-]
  1. uses Windows, fphttpapp, httpdefs, httproute;
  2.  
  3. var
  4.   svc_name: string = '_testservice';
  5.   svc_disp: string = '_My Test Service';
  6.  
  7.   servicetable: TSERVICETABLEENTRY;
  8.   servicestatus: TSERVICESTATUS;
  9.   statushandle: SERVICE_STATUS_HANDLE;
  10.  
  11.   ghSvcStopEvent: dword;
  12.  
  13.   stopped: boolean = false;
  14.   paused: boolean = false;
  15.  
  16. procedure DoHello(ARequest: TRequest; AResponse: TResponse);
  17. begin
  18.   AResponse.Content := '<html><body><h1>Hello World!</h1></body></html>';
  19. end;
  20.  
  21. procedure httpapp;
  22. begin
  23.   HTTPRouter.RegisterRoute('*', @DoHello);
  24.   Application.Port := 9000;
  25.   Application.Initialize;
  26.   Application.Run;
  27. end;
  28.  
  29. function install_service(name, displayname, path: string): boolean;
  30. var
  31.   manager, service: SC_HANDLE;
  32.   starttype: dword;
  33. begin
  34.   result := false;
  35.  
  36.   starttype := SERVICE_AUTO_START;
  37.   //starttype := SERVICE_DEMAND_START;
  38.  
  39.   manager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  40.   if manager = 0 then exit;
  41.  
  42.   service := CreateService(manager, pchar(name), pchar(displayname), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS +
  43.     SERVICE_INTERACTIVE_PROCESS, starttype, SERVICE_ERROR_IGNORE, pchar(path), nil, nil, nil, nil, nil);
  44.  
  45.   if service > 0 then begin
  46.     result := true;
  47.     CloseServiceHandle(service);
  48.   end;
  49.  
  50.   CloseServiceHandle(manager);
  51. end;
  52.  
  53. function uninstall_service(name: string): boolean;
  54. var
  55.   manager, service: SC_HANDLE;
  56.   status: TSERVICESTATUS;
  57. begin
  58.   result := false;
  59.  
  60.   manager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  61.   if manager = 0 then exit;
  62.  
  63.   try
  64.     service := OpenService(manager, pchar(name), SERVICE_ALL_ACCESS);
  65.     if service = 0 then exit;
  66.  
  67.     ControlService(service, SERVICE_CONTROL_STOP, status);
  68.  
  69.     DeleteService(service);
  70.     CloseServiceHandle(service);
  71.  
  72.     result := true;
  73.   finally
  74.     CloseServiceHandle(manager);
  75.   end;
  76. end;
  77.  
  78. function start_service(name: string): boolean;
  79. var
  80.   manager, service: SC_HANDLE;
  81.   args: LPPCSTR;
  82. begin
  83.   result := false;
  84.  
  85.   manager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  86.   if manager = 0 then exit;
  87.  
  88.   try
  89.     service := OpenService(manager, pchar(name), SERVICE_ALL_ACCESS);
  90.     if service = 0 then exit;
  91.  
  92.     args := nil;
  93.     result := StartService(service, 0, args);
  94.   finally
  95.     CloseServiceHandle(manager);
  96.   end;
  97. end;
  98.  
  99. procedure service_report_status(dwCurrentState, dwWin32ExitCode, dwWaitHint: DWORD);
  100. begin
  101.   ServiceStatus.dwCurrentState := dwCurrentState;
  102.   ServiceStatus.dwWin32ExitCode := dwWin32ExitCode;
  103.   ServiceStatus.dwWaitHint := dwWaitHint;
  104.  
  105.   case dwCurrentState of
  106.     SERVICE_START_PENDING: ServiceStatus.dwControlsAccepted := 0;
  107.   else
  108.     ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP + SERVICE_ACCEPT_PAUSE_CONTINUE
  109.   end;
  110.  
  111.   case (dwCurrentState = SERVICE_RUNNING) or (dwCurrentState = SERVICE_STOPPED) of
  112.     true: ServiceStatus.dwCheckPoint := 0;
  113.     false: ServiceStatus.dwCheckPoint := 1;
  114.   end;
  115.  
  116.   SetServiceStatus(StatusHandle, ServiceStatus);
  117. end;
  118.  
  119. function service_handler(control: dword): longbool; stdcall;
  120. begin
  121.   result := true;
  122.  
  123.   case Control of
  124.     SERVICE_CONTROL_STOP:
  125.     begin
  126.       stopped := true;
  127.       SetEvent(ghSvcStopEvent);
  128.       ServiceStatus.dwCurrentState := SERVICE_STOP_PENDING;
  129.       SetServiceStatus(statushandle, servicestatus);
  130.     end;
  131.     SERVICE_CONTROL_PAUSE:
  132.     begin
  133.       paused := true;
  134.       ServiceStatus.dwcurrentstate := SERVICE_PAUSED;
  135.       SetServiceStatus(statushandle, servicestatus);
  136.     end;
  137.     SERVICE_CONTROL_CONTINUE:
  138.     begin
  139.       paused := false;
  140.       ServiceStatus.dwCurrentState := SERVICE_RUNNING;
  141.       SetServiceStatus(statushandle, servicestatus);
  142.     end;
  143.     SERVICE_CONTROL_INTERROGATE: SetServiceStatus(StatusHandle, ServiceStatus);
  144.     SERVICE_CONTROL_SHUTDOWN: stopped := true;
  145.   end;
  146. end;
  147.  
  148. function svcmain(p: pointer): ptrint; stdcall;
  149. begin
  150.   //while true do begin
  151.   //  if stopped then break;
  152.   //  if paused then begin sleep(500); continue; end;
  153.   //
  154.   //  // do something?
  155.   //end;
  156.  
  157.   // start httpapp
  158.   httpapp;
  159.  
  160.   result := 0;
  161. end;
  162.  
  163. procedure svcloop;
  164. var
  165.   d: dword;
  166. begin
  167.   ghSvcStopEvent := CreateEvent(nil, true, false, nil);
  168.  
  169.   if ghSvcStopEvent = 0 then begin
  170.     service_report_status(SERVICE_STOPPED, NO_ERROR, 0);
  171.     exit;
  172.   end;
  173.  
  174.   // start svcmian
  175.   CreateThread(nil, 0, @svcmain, nil, 0, d);
  176.   service_report_status(SERVICE_RUNNING, NO_ERROR, 0);
  177.  
  178.   // wait for stop
  179.   WaitForSingleObject(ghSvcStopEvent, INFINITE);
  180.  
  181.   // cleanup here
  182.   // ...
  183.  
  184.   service_report_status(SERVICE_STOPPED, NO_ERROR, 0);
  185. end;
  186.  
  187. procedure svcregister(dwArgc: longword; lpszArgv: LPPSTR); stdcall;
  188. begin
  189.   with servicestatus do begin
  190.     dwServiceType := SERVICE_WIN32_OWN_PROCESS;
  191.     dwCurrentState := SERVICE_START_PENDING;
  192.     dwControlsAccepted := SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_PAUSE_CONTINUE;
  193.     dwServiceSpecificExitCode := 0;
  194.     dwWin32ExitCode := 0;
  195.     dwCheckPoint := 0;
  196.     dwWaitHint := 0;
  197.   end;
  198.  
  199.   statushandle := RegisterServiceCtrlHandler(pchar(svc_name), @service_handler);
  200.  
  201.   if statushandle <> 0 then begin
  202.     service_report_status(SERVICE_RUNNING, NO_ERROR, 0);
  203.     svcloop;
  204.     service_report_status(SERVICE_STOPPED, NO_ERROR, 0);
  205.   end;
  206. end;
  207.  
  208. begin
  209.   if paramstr(1) = 'i' then begin
  210.     writeln('installing...');
  211.  
  212.     if not install_service(svc_name, svc_disp, paramstr(0)) then begin
  213.       writeln('install failed');
  214.       exit;
  215.     end;
  216.  
  217.     if start_service(svc_name) then
  218.       writeln('service started')
  219.     else
  220.       writeln('service start failed');
  221.   end else if paramstr(1) = 'u' then begin
  222.     writeln('uninstalling...');
  223.  
  224.     if uninstall_service(svc_name) then writeln('ok!') else writeln('fail');
  225.   end else begin
  226.     servicetable.lpServiceName := pchar(svc_name);
  227.     servicetable.lpServiceProc := @svcregister;
  228.  
  229.     if not StartServiceCtrlDispatcher(@servicetable) then begin
  230.       writeln('i am not a service');
  231.       writeln('run with "i" param to install');
  232.       exit;
  233.     end;
  234.   end;
  235. end.
« Last Edit: December 07, 2024, 06:00:36 pm by Fibonacci »

Thaddy

  • Hero Member
  • *****
  • Posts: 16312
  • Censorship about opinions does not belong here.
Re: fphttpapp and Windows Server Service
« Reply #3 on: December 08, 2024, 08:36:36 am »
There is no reason why the server/routing example should not work with the Daemonapp template code.
That has the advantage that it will run on more platforms, not only just Win32.
If I smell bad code it usually is bad code and that includes my own code.

 

TinyPortal © 2005-2018