A standalone Delphi application that can also be installed as windows service

14,351

Solution 1

Totally possible. The trick is to edit the .dpr to create main form when you want to run as an application and the service form when you want to run as a service. Like this:

if SvComFindCommand('config') then begin
  //When run with the /config switch, display the configuration dialog.
  Forms.Application.Initialize;
  Forms.Application.CreateForm(TfrmConfig, frmConfig);
  Forms.Application.Run;
end
else begin
  SvCom_NTService.Application.Initialize;
  SvCom_NTService.Application.CreateForm(TscmServiceSvc, scmServiceSvc);
  SvCom_NTService.Application.Run;
end;

The code above uses SvCom to run the service but exactly the same effect could be achieved using the standard TService.

I wrote an article about that for The Delphi Magazine many years ago. You can read it here: Many Faces Of An Application.

Solution 2

It'll be hard to explain but I will try :)

I've done it in my project like that (Delphi 5):

program TestSvc;
uses SvcMgr, 
     SvcMain, //the unit for TTestService inherited from TService
     ...
     ;

var
  IsDesktopMode : Boolean;

function IsServiceRunning : Boolean;
var
  Svc: Integer;
  SvcMgr: Integer;
  ServSt : TServiceStatus;
begin
  Result := False;
  SvcMgr := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if SvcMgr = 0 then Exit;
  try
    Svc := OpenService(SvcMgr, 'TestService', SERVICE_QUERY_STATUS);
    if Svc = 0 then Exit;
    try
      if not QueryServiceStatus(Svc, ServSt) then Exit;
      Result := (ServSt.dwCurrentState = SERVICE_RUNNING) or (ServSt.dwCurrentState = SERVICE_START_PENDING);
    finally
      CloseServiceHandle(Svc);
    end;
  finally
    CloseServiceHandle(SvcMgr);
  end;
end;


begin
  if (Win32Platform <> VER_PLATFORM_WIN32_NT) or FindCmdLineSwitch('S', ['-', '/'], True)  then
    IsDesktopMode := True
  else begin
    IsDesktopMode := not FindCmdLineSwitch('INSTALL', ['-', '/'], True) and
      not FindCmdLineSwitch('UNINSTALL', ['-', '/'], True) and
      not IsServiceRunning;
  end;

  if IsDesktopMode then begin //desktop mode
    Forms.Application.Initialize;
    Forms.Application.Title := 'App. Title';
    ShowTrayIcon(Forms.Application.Icon.Handle, NIM_ADD); // This function for create an icon to tray. You can create a popupmenu for the Icon.

    while GetMessage(Msg, 0, 0, 0) do begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;

    ShowTrayIcon(Forms.Application.Icon.Handle, NIM_DELETE); // for delete the tray Icon
  end else begin // Service mode
    SvcMgr.Application.Initialize;
    SvcMgr.Application.CreateForm(TTestService, TestService);
    SvcMgr.Application.Run;
  end;
end.

Solution 3

Another almost simpler option is available at http://cc.embarcadero.com/item/19703, you just need to include a unit and change your DPR to something like:

begin
  if CiaStartService('SERVICE NAME') then begin
    CiaService.CreateForm(TMain, Main);
    CiaService.Run;
    Exit;
  end;

  Application.Initialize;
  Application.Title := 'SERVICE NAME';
  Application.CreateForm(TMain, Main);
  Application.Run;
end.

While this example is now quite dated, the technique is simple enough that it still works, even with Delphi XE2. With this in place, your application will continue to operate as a non-service until you use the "/install" parameter (on an elevated command prompt). After which it will operate as a service until you use the "/uninstall" parameter (also on an elevated command prompt).

Solution 4

There is a solution for this problem without writing a single line of code. It depends a little on your application, but generally it is achievable. Try this: http://iain.cx/src/nssm. Don't forget to start all services that you application depends on BEFORE you start your application as a service. Google around for info on how to do that.

Solution 5

It is possible but in that case you cannot use the normal TServiceApplication and TService. You should implement all the service specific code yourself.

We had a similat problem and made two frame applications: one for the sand alone exe and one for the service. Now we can create a single BPL/DLL that is embedded in both containers.

If you want to spend some money: you should look at SvCOM, I think they have a solution to the problem.

Share:
14,351
Vic
Author by

Vic

Updated on June 02, 2022

Comments

  • Vic
    Vic almost 2 years

    In Delphi you can create a standalone Windows VCL Forms application. You can also create a Windows service application.

    Is it possible to combine the two in a single application that can run as a standalone application and can also be installed as a Windows service?

  • dipold
    dipold over 8 years
    Is it possible to create a {$DEFINE} global compilation that identifies if it is running on standlone or windows service mode?