Whats the best way to handle the StartServiceCtrlDispatcherA call to ServiceMain

Trying to get a clarion app loaded as a Service.

Calling StartServiceCtrlDispatcherA which is calling a source procedure, but its failing with an error 1067 “The process terminated unexpectedly.”

So have a handlerExA and a ServiceMain coded, which is doing the SetServiceStatus updates before running a seperate thread to do stuff, but whats tripping me up is the StartServiceCtrlDispatcherA call.

In this link Service ServiceMain Function - Win32 apps | Microsoft Learn

The ServiceMain function should perform the following tasks:

  1. Initialize all global variables.
  2. Call the RegisterServiceCtrlHandler function immediately to register a Handler function to handle control requests for the service. The return value of RegisterServiceCtrlHandler is a service status handle that will be used in calls to notify the SCM of the service status.
  3. Perform initialization. If the execution time of the initialization code is expected to be very short (less than one second), initialization can be performed directly in ServiceMain.

the ServiceMain should initialise the Globals and ABC includes etc, so in theory that would mean having _main() as the ServiceMain, but I cant get Address(_Main) to work for obvious reasons.

So I wondered is there a better way to handle the StartServiceCtrlDispatcherA call to ServiceMain?

I can tell what the calling ParentProcessID is using NtQueryInformationProcess function (winternl.h) - Win32 apps | Microsoft Learn so I can tell when the Service Control Manager calls the clarion app because I strpos(Loc:ModuleFilename, ‘\\services.exe’, true) but I cant pass any parameters to the ServiceMain procedure using StartServiceCtrlDispatcherA. *

I know it can be done because Capesoft’s SelfService exists, but I need the extra functionality of rolling my own service in this case.

TIA.

Edit
* unless I write a flag to a tps file which controls what code is run from _Main() everytime the service app is loaded. Potentially this might work, off to see if it will…

Edit2
So whilst Address( var:string_main ) does not return the program’s entry point, GSCM:ProcessHandle = GetModuleHandleA( 0 ) does. So

    GSCM:ProcessHandle = GetModuleHandleA( 0 )
    GSCV:lpServiceName[1]   = Address( GSCM:ServiceName )       ! svcName
    GSCV:lpServiceProc[1]   = GSCM:ProcessHandle ! 04000000h ! Address( _main )    
    GSCV:lpServiceName[2]   = 0   ! This tells the Service Control Manager there are no more services in this app/exe
    GSCV:lpServiceProc[2]   = 0   ! This tells the Service Control Manager there are no more services in this app/exe

    Glo:RVBool = StartServiceCtrlDispatcherA( Address( GSCV:ServiceTableEntryA ) )

Works, so using a flag, either in an ini or tps or mutex count could be a way to then call different code for the Handler and Main.

Now just got to test it.

Co-Pilot even suggested:

Alternative: Disassemble and Trace

If you’re reverse-engineering or validating runtime behavior:

  • Use IDA Pro or Ghidra to disassemble the binary.
  • Look for the mainCRTStartup or WinMain equivalent in Clarion’s runtime.
  • Trace the call to your MAIN procedure — Clarion often wraps it in START() logic.

Edit 3
Looks like I’ll be needing AttachThreadToClarion, but I’ve found Vince Sorenson’s ABC free template, so can take a look at the Service template he’s created.

I think this post from Icetips will also be helpful.

Frustrating!

I recommend following what this does. Its a bit outdated - there are more options in the API calls now, but the basic architecture is sound.

It was the ground work for our SelfService product.

There we go, very first line of ServiceMain

AttachThreadToClarion(True)                                        #<! %ABCFreeTemplate (ABC Free)

This was where I was having problems, because obviously the Service Control Manager is trying to reload the clarion service exe by starting from the clarion procedure I’ve called ServiceMain, and so all the globals and classes couldnt be initialised as mentioned in the MS Docs, because they are all in the Program section found at 0400000h which is the program entry point.

All my other code is fine, I just didnt know about the existence of the AttachThreadToClarion function.

The first param in the API calls in the screenshot are the address of the api thats been loaded using LoadLibraryEx.

1 Like