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:
- Initialize all global variables.
- 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.
- 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
mainCRTStartuporWinMainequivalent in Clarion’s runtime.- Trace the call to your
MAINprocedure — Clarion often wraps it inSTART()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!
