Creating a binary file that consists of a service and a normal application can be done very simple without using the service manager. Just check for SYSTEM account. It is very unlikely that your app is run as SYSTEM (only RunAsSys does ).

  1. uses …, JwsclToken;
  2. begin
  3.   if JwIsSystem then
  4.   begin
  5.     try
  6.     Application.Initialize;
  7.     Application.CreateForm(TMyService, MyService);
  8.     Application.Run;
  9.   end
  10.   else
  11.   begin
  12.   end;
  13. end;

Update

Mark Robinson suggested (in a comment) to check for a “service.exe” parent process. Since I did not find a solution within seconds, I decided to show how it works. Quite in handy came a CodeProject article about finding a parent process. The code is also available through download. Use it on your own risk.

  1. function GetParentProcess(out ProcessEntry32 : TProcessEntry32W; const ProcessID : DWORD = 0) : DWORD; overload;
  2.  
  3. function InternalGetParentProcess(
  4.   const Condition : Boolean;
  5.   out ProcessEntry32 : TProcessEntry32W; const ProcessID : DWORD = 0) : DWORD;
  6. var
  7.   hSnap : THandle;
  8.   ProcEntry : TProcessEntry32W;
  9.   currentID : DWORD;
  10.   Continue : Boolean;
  11.  
  12. begin
  13.   hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  14.   result := INVALID_HANDLE_VALUE;
  15.  
  16.   ZeroMemory(@ProcessEntry32, sizeof(ProcessEntry32));
  17.  
  18.   try
  19.     ProcEntry.dwSize := sizeof(ProcEntry);
  20.     Continue := Process32FirstW(hSnap, ProcEntry);
  21.  
  22.     if (ProcessID = DWORD(-1)) or (ProcessID = 0) then
  23.       CurrentID := GetCurrentProcessId
  24.     else
  25.       CurrentID := ProcessID;
  26.  
  27.     result := INVALID_HANDLE_VALUE;
  28.  
  29.     while (continue) do
  30.     begin
  31.       if CurrentID = ProcEntry.th32ProcessID then
  32.       begin
  33.         result := ProcEntry.th32ParentProcessID;
  34.  
  35.         if not Condition then
  36.         begin
  37.           InternalGetParentProcess(true, ProcessEntry32, result);
  38.         end
  39.         else
  40.           ProcessEntry32 := ProcEntry;
  41.       end;
  42.  
  43.       continue := Process32NextW(hSnap,ProcEntry) and (result = INVALID_HANDLE_VALUE);
  44.     end;
  45.   finally
  46.     CloseHandle(hSnap);
  47.   end;
  48. end;
  49.  
  50. begin
  51.   result := InternalGetParentProcess(false, ProcessEntry32, ProcessID);
  52. end;
  53.  
  54. function GetParentProcess(const ProcessID : DWORD = 0) : DWORD; overload;
  55. var ProcessEntry32 : TProcessEntry32W;
  56. begin
  57.   result := GetParentProcess(ProcessEntry32, ProcessID);
  58. end;
  59.  
  60. function GetParentProcess(out ExeName : WideString; const ProcessID : DWORD = 0) : DWORD; overload;
  61. var ProcessEntry32 : TProcessEntry32W;
  62. begin
  63.   result := GetParentProcess(ProcessEntry32, ProcessID);
  64.   if result <> INVALID_HANDLE_VALUE then
  65.     ExeName := ProcessEntry32.szExeFile
  66.   else
  67.     ExeName := ;
  68. end;
  69.  
  70. var P : DWORD;
  71.   Name : WideString;
  72. begin
  73.   P :=GetParentProcess(Name);
  74.   writeln(P,‘ ‘,Name);
  75.   readln;
  76. end.

There are three versions of GetParentProcess, so you can get more information than usual necessary.
The reason why the first version has an internal function is that we need to iterate the process list twice to get the parent’s process name.

Finally all you have to do is to check for its name “services.exe”.

  1. if CompareText(Name,’services.exe’) then

However there can be a bad coincidence and another process who started your app is named like that. In this case we should just check for the full path.

  1. function RetrieveModuleFileName(const PID : DWORD) : WideString;
  2. var
  3.   FileName : Array[0..MAX_PATH] of Widechar;
  4.   hP : THandle;
  5. begin
  6.   hP := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
  7.   if hP = 0 then
  8.     RaiseLastOSError;
  9.  
  10.   try
  11.     if GetModuleFileNameExW(hP,0,FileName, sizeof(FileName)) = 0 then
  12.       RaiseLastOSError;
  13.     result := FileName;
  14.   finally
  15.     CloseHandle(hP);
  16.   end;
  17. end;

Now we can be sure that it is the correct process.

  1. function GetSystem32Path : WideString;
  2. var Path : Array[0..MAX_PATH] of Widechar;
  3. begin
  4.   Result := ;
  5.   if SUCCEEDED(SHGetFolderPathW(0,CSIDL_SYSTEM,0,SHGFP_TYPE_DEFAULT, @Path)) then
  6.     result := IncludeTrailingBackslash(Path);  //Oopps, may convert unicode to ansicode
  7. end;
  8.  
  9. var PID : DWORD;
  10. begin
  11.   PID := GetParentProcess(Name);
  12.  
  13.   Path := RetrieveModuleFileName(P);
  14.   SysPath := GetSystem32Path+’services.exe’;
  15.   if CompareText(SysPath, Path) = 0 then
  16.     //OK
  17.   else
  18.     //not a service
  19. end.

We are not at the end. I should mention that your service application can also be in a service pool hosted by the parent process “svchost.exe”. In such a case of course, we have to check for “svchost.exe” instead of “services.exe”

Send post as PDF to www.pdf24.org
convert this post to pdf.