tags:

views:

284

answers:

6

I have code which is used both in services and within VCL Form applications (win32 application). How can I determine if the underlying application is running as a NT Service or as an application?

Thanks.

+2  A: 

I doubt that

System.IsConsole
System.IsLibrary

will give you the expected results.

All I can think of is to pass an Application object as TObject to the method where you need to make that distinction and test for the passed object's classname being a

TServiceApplication 
or
TApplication

That said, there shouldn't be a need for you to know if your code is running in a service or a GUI. You should probably rethink your design and make the caller to pass an object to handle messages you want (or don't want) to show. (I assume it is for showing messages/exceptions you'd like to know).

Lieven
Unfortunately Application is declared in BOTH Forms and SvcMgr and just using either automatically creates an instance so you can't check application directly.
skamradt
@skamradt, if you pass it as a TObject and check on classname there's no need to use SvcMgr and/or Forms, hence they don't get created automatically. The calling code offcourse uses either SvcMgr or Forms.
Lieven
+2  A: 

You can try something like this

Function IsMyformInsideaWindowsService(aForm:TObject) :Boolean;
Begin
   Result:=aForm.ClassParent.ClassName='TService';  //When a form is running under a service the Class Parent is a TService
End;
RRUZ
The problem with the second function is one of scope and order of units in the uses clause. If you use forms after svcmgr in your uses clause then this will ALWAYS return false, or vice versa.
skamradt
skamradt, you're right, I just delete the second option.
RRUZ
+4  A: 

The application object (Forms.application) mainform will be nil if it is not a forms based application.

uses
  Forms, ... ;

function IsFormBased : boolean;
begin
  Result := Assigned(Forms.Application.MainForm);
end;
skamradt
A: 

I actually ended up checking the application.showmainform variable.

The problem with skamradt's isFormBased is that some of this code is called before the main form is created.

I am using a software library called SvCom_NTService from aldyn-software. One of purposes is for errors; either to log them or show a message. I totally agree with @Rob; our code should be better maintained and handle this outside of the functions.

The other intention is for failed database connections and queries; I have different logic in my functions to open queries. If it is a service then it will return nil but continue the process. But if failed queries/connections occur in an application then I would like to display a messaage and halt the application.

M Schenkel
+2  A: 

You can check if the parent process is SCM (service control manager). If you are running as service this is always the case and never the case if running as standard application. Also I think that SCM has always the same PID.

You can check it like this:

  PL := TProcessList.Create;
  try
    PL.CreateSnapshot;
    MyProcessId := GetCurrentProcessId;

    MyProcess := PL.FindProcess(MyProcessId);
    if MyProcess <> nil then
    begin
      ParentProcess := PL.FindProcess(MyProcess^.th32ParentProcessID);
      if ParentProcess <> nil then
      begin
        GrandParentProcess := PL.FindProcess(ParentProcess^.th32ParentProcessID);

        if GrandParentProcess <> nil then
        begin
          Result := SameText(string(ParentProcess^.szExeFile), 'services.exe') and
            SameText(string(GrandParentProcess^.szExeFile), 'winlogon.exe');
        end;
      end;
    end;
  finally
    PL.Free;
  end;
Runner
+1, much better approach (even though the whole idea of checking for execution in a service isn't sound). I have a service coded without any VCL support for services, so most of the other checks would fail for it.
mghie
I agree, the whole idea is a little hack. But the reality is, that there are legitimate reasons for checking sometimes.
Runner
How can you check the "Parent Process"?
M Schenkel
I edited my answer with the solution
Runner
+1  A: 

A single project cannot (or I should say ideally is not) both a service and a forms application, at least not if you are able to distinguish between the Forms Application object and the SvcMgr Application object - you must presumably have separate projects for the forms code and the service code.

So perhaps the easiest solution is a project conditional define. i.e. in your project settings for the service project add "SERVICEAPP" to the Conditional Defines.

Then whenever you need to change behaviour simply:

{$ifdef SERVICEAPP}
{$else}
{$endif}

For belts and braces you might adopt one of the previously described tests within some startup code to ensure that your project has been compiled with the expected symbol defined.

program ... ;

 :

begin
{$ifdef SERVICEAPP}
  // test for service app - ASSERT if not
{$else}
  // test for forms app - ASSERT if not
{$endif}
  :
end.

It is possible that your Forms app is actually running as a service, using the crude technique that allows any application to be running as a service.

In that case of course your app will always be a Forms application and the easiest way to handle that situation is to have a command line switch that you specify only in the service definition for your executable so that your app can respond appropriate by testing for that command line switch.

This does allow you to more easily test your "service mode" behaviour of course, since you can run your app in "debug" mode with that switch defined from within the IDE, but it's not an ideal way to build a service application so I would not recommend it on the strength of that alone. It's a technique that is usually only used when you have an EXE that you wish to run as a service but have no way to modify the source code to turn it into a "proper" service.

Deltics
It is possible (with some conditional code in the dpr) to create a single EXE which acts as both a service and GUI application - not always a good idea, but possible.
Gerry
Yes it's possible, for example see the socket server (scktsrvr.dpr).
TOndrej
We have used the conditional defines in the past. The problem is, sometimes we forgot to include it. But I think your "assert" is a good "check".
M Schenkel