Recent

Author Topic: fphttpapp and Windows Server Service  (Read 1465 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: 647
  • 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: 16407
  • 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.
There is nothing wrong with being blunt. At a minimum it is also honest.

 

TinyPortal © 2005-2018