Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Adding a service in the Registry
Message
From
05/10/2007 01:44:56
 
 
To
03/10/2007 10:51:20
Jay Johengen
Altamahaw-Ossipee, North Carolina, United States
General information
Forum:
Visual FoxPro
Category:
Coding, syntax & commands
Environment versions
Visual FoxPro:
VFP 8 SP1
Miscellaneous
Thread ID:
01258203
Message ID:
01258806
Views:
22
>>>>IIRC, you will need instsrv.exe to install service and srvany.exe to run your application as service.
>>>
>>>I wonder what vendors do when they install a service and they don't know if their clients have these intalled?
>>
>>I think the difference is that most actual services are written AS windows services using a development environment that supports creating services, rather than being wrapped in srvany and shoehorned in as a service.
>>
>>Since you can't actually write a windows service in VFP (at least not that I am aware of), srvany is what you'll need.
>
>Maybe I could just include the two required EXE files as part of the install process. Then all the network guy has to do is run the DOS command to set it up. Or does the WRK actually have to be officially installed for the utilities to work?

Jay:

I have been following your posts about creating a service with VFP. Maybe I can give you some advice.

You have to use srvany.exe because it is not possible to write a program that acts as a service with VFP alone. You could also use another activeX control or method, but srvany.exe works and is free.

You can get srvany as part of the Windows Server 2003 Resource Kit Tools. There is a comprehensive help file included that explains how it should be used.

Basically when a program runs as a service, it interacts with the service control manager. The SCM tells a service to start, stop, pause, and resume, and can query a service status.

srvany.exe acts as a middle man between the Service Control Manager and our VFP exe.

You do NOT need instsrv.exe, you can do what it does yourself, in the VFP program. And you can even include srvany.exe inside the vfp exe and extract it the first time your program is run.

A good strategy is to create only one executable, that will act as the service exe itself, and as a monitoring/control/config program for the service.

I will copy paste here some methods of my service class, so you can get an idea.

1) Ask Service Control Manager to add the exe as a service.
2) Add a service description, this is cosmetic only, and cannot be done in step 1.
2) Add the registry entries required by srvany.exe, so it knows where our VFP exe is and what it should do.
*!* ServiceInstall()

Local m.llRetVal As Boolean

m.llRetVal = FALSE

If This._OpenManager() = TRUE Then

	m.lnManagerHandle 	= This._ManagerHandle
	m.lcServiceName 	= This.ServiceName + NULCHAR
	m.lcDisplayName 	= This.ServiceDisplayName + NULCHAR
	m.lnDesiredAccess 	= SERVICE_ALL_ACCESS
	m.lnServiceType 	= SERVICE_WIN32_OWN_PROCESS

	If This.ServiceInteractive = TRUE Then
		m.lnServiceType = m.lnServiceType + SERVICE_INTERACTIVE_PROCESS
	Endif

	m.lnStartType 		= This.ServiceStartType
	m.lnErrorControl 	= SERVICE_ERROR_NORMAL
	m.lcBinaryPathName	= Lower(Getenv("windir")) + "\system32\srvany.exe" + NULCHAR

	m.lcLoadOrderGroup 	= Null
	m.lcTagId 			= Null
	m.lcDependencies 	= Null

	If This.ServiceUseSystemAccount = TRUE Then
		m.lcServiceStartName 	= Null
		m.lcPassword 			= Null
	Else
		m.lcServiceStartName 	= This.ServiceAccount + NULCHAR
		m.lcPassword 			= This.ServicePassword + NULCHAR
	Endif

	This._ServiceHandle = CreateService( ;
		m.lnManagerHandle, ;
		m.lcServiceName, ;
		m.lcDisplayName,;
		m.lnDesiredAccess, ;
		m.lnServiceType, ;
		m.lnStartType, ;
		m.lnErrorControl,;
		m.lcBinaryPathName, ;
		m.lcLoadOrderGroup, ;
		m.lcTagId, ;
		m.lcDependencies, ;
		m.lcServiceStartName, ;
		m.lcPassword)

	If This._ServiceHandle <> 0 Then
		m.llRetVal = TRUE
		This._SetServiceDescription()
		This._AddRegEntries()
	Else
		This._GetLastError()
	Endif

	This._CloseHandles()

Endif

Return m.llRetVal

****************************************************************************
*!* SetServiceDescription()

If This._OpenService() = TRUE

	m.lnService 	= This._ServiceHandle
	m.lnInfoLevel 	= SERVICE_CONFIG_DESCRIPTION
	m.lcInfo		= This.ServiceDescription + NULCHAR

	m.lnHeap 	= GetProcessHeap()
	m.lnLen 	= Len(m.lcInfo)

	*!* Allocate memory to hold m.lcInfo string:
	m.lnInfo = HeapAlloc(m.lnHeap, HEAP_ZERO_MEMORY, m.lnLen)

	*!* If memory allocation succeeded:
	If m.lnInfo <> 0 Then
	
		*!* Copy lcInfo string to allocated memory 
		Sys(2600, m.lnInfo, m.lnLen, m.lcInfo)

		ChangeServiceConfig2(m.lnService, m.lnInfoLevel, @m.lnInfo)
		
		*!* Release allocated memory
		HeapFree(m.lnHeap, 0, m.lnInfo)
	Endif

Endif

****************************************************************************
*!* _AddRegEntries()

Local ;
	m.lcServiceAppDirectory As String, ;
	m.lcServiceApplication As String

This.oRegistry.ctlKey = "HKEY_LOCAL_MACHINE"
This.oRegistry.ctlSubKey = "SYSTEM\CurrentControlSet\Services\" + This.ServiceName + "\Parameters"

m.lcServiceAppDirectory = This.ServiceAppDirectory

This.oRegistry.ctlValueSet("AppDirectory", m.lcServiceAppDirectory, REG_SZ)

If Not Empty(This.ServiceAppEnvironment) Then
	This.oRegistry.ctlValueSet("AppEnvironment", This.ServiceAppEnvironment, REG_MULTI_SZ)
Endif

m.lcServiceApplication = This.ServiceApplication

This.oRegistry.ctlValueSet("Application", m.lcServiceApplication, REG_SZ)

If Not Empty(This.ServiceAppParameters) Then
	This.oRegistry.ctlValueSet("AppParameters", This.ServiceAppParameters, REG_SZ)
Endif

****************************************************************************
As you can see. I have added a parameter that will be passed to the exe: This.ServiceAppParameters, that has a value of "-service"

Now in the main prg of the project i do this:
If Upper(Transform(m.param1)) = "-SERVICE" Then
	Do Form Service_Program NOSHOW
Else
	Do Form Service_Control
Endif

Read Events
This makes it possible to have only one exe that acts as the service and the monitoring/control/config program

Now to know the status of the service, to start, stop and uninstall:
*!* ServiceStatus()

*!*	typedef struct _SERVICE_STATUS {
*!*	DWORD dwServiceType;					4
*!*	DWORD dwCurrentState;					4
*!*	DWORD dwControlsAccepted;				4
*!*	DWORD dwWin32ExitCode;					4
*!*	DWORD dwServiceSpecificExitCode;		4
*!*	DWORD dwCheckPoint;						4
*!*	DWORD dwWaitHint; } SERVICE_STATUS,		4
*!*	*LPSERVICE_STATUS;						28

*!*	#DEFINE SERVICE_CONTINUE_PENDING	0x5
*!*	#DEFINE SERVICE_PAUSE_PENDING		0x6
*!*	#DEFINE SERVICE_PAUSED				0x7
*!*	#DEFINE SERVICE_RUNNING				0x4
*!*	#DEFINE SERVICE_START_PENDING		0x2
*!*	#DEFINE SERVICE_STOP_PENDING		0x3
*!*	#DEFINE SERVICE_STOPPED				0x1

*!*	#Define ERROR_ACCESS_DENIED						5
*!*	#DEFINE ERROR_SERVICE_NOT_FOUND					1243
*!*	#Define ERROR_INVALID_HANDLE					6
*!*	#Define ERROR_INVALID_NAME						123
*!*	#DEFINE ERROR_SERVICE_DOES_NOT_EXIST			1060


Local ;
	m.lnRetVal As Integer, ;
	m.lnApiRetVal As Integer, ;
	m.lnService As Integer, ;
	m.lcServiceStatus As String

m.lnRetVal = 0

If This._OpenService() = TRUE

	m.lnService			= This._ServiceHandle
	m.lcServiceStatus	= Replicate(NULCHAR, 28)

	m.lnApiRetVal = QueryServiceStatus(m.lnService, @m.lcServiceStatus)

	If m.lnApiRetVal <> 0 Then
		m.lnRetVal = CToBin(Substr(m.lcServiceStatus, 5, 4), "4RS")
	Else
		This._GetLastError()
	Endif

	This._CloseHandles()

Else

	m.lnRetVal = This.ServiceErrorCode

Endif

Return m.lnRetVal

*********************************************************************
*!* ServiceStart()

Local ;
	m.llRetVal As Boolean, ;
	m.lnApiRetVal As Integer

m.llRetVal = FALSE

If This._OpenService() = TRUE

	m.lnApiRetVal = StartService(This._ServiceHandle, 0, 0)

	If m.lnApiRetVal <> 0 Then
		m.llRetVal = TRUE
	Else
		This._GetLastError()
	Endif

	This._CloseHandles()

Endif

Return m.llRetVal

**********************************************************************
*!* ServiceStop()

*!*	typedef struct _SERVICE_STATUS {
*!*	DWORD dwServiceType;					4
*!*	DWORD dwCurrentState;					4
*!*	DWORD dwControlsAccepted;				4
*!*	DWORD dwWin32ExitCode;					4
*!*	DWORD dwServiceSpecificExitCode;		4
*!*	DWORD dwCheckPoint;						4
*!*	DWORD dwWaitHint; } SERVICE_STATUS,		4
*!*	*LPSERVICE_STATUS;						28


Local ;
	m.llRetVal As Boolean, ;
	m.lnApiRetVal As Integer, ;
	m.lnService As Integer, ;
	m.lnControl As Integer, ;
	m.lcServiceStatus As String

m.llRetVal = FALSE

If This._OpenService() = TRUE

	m.lnService			= This._ServiceHandle
	m.lnControl			= SERVICE_CONTROL_STOP
	m.lcServiceStatus	= Replicate(NULCHAR, 28)

	m.lnApiRetVal = ControlService(m.lnService, m.lnControl, @m.lcServiceStatus)

	If m.lnApiRetVal <> 0 Then
		m.llRetVal = TRUE
	Else
		This._GetLastError()
	Endif

	This._CloseHandles()

Endif

Return m.llRetVal

*****************************************************************************
*!* ServiceUninstall()

*!* First try to stop service:
If This.ServiceStatus() = SERVICE_RUNNING Then
	This.ServiceStop()
Endif

Local ;
	m.llRetVal As Boolean ;
	m.lnApiRetVal As Integer

m.llRetVal = FALSE

If This._OpenService() = TRUE

	m.lnApiRetVal = DeleteService(This._ServiceHandle)

	If m.lnApiRetVal <> 0 Then
		m.llRetVal = TRUE
	Else
		This._GetLastError()
	Endif

	This._CloseHandles()

Endif

Return m.llRetVal

*****************************************************************************
*!* _CloseHandles()

If This._ServiceHandle <> 0 Then
	CloseServiceHandle( This._ServiceHandle)
	This._ServiceHandle = 0
Endif

If This._ManagerHandle <> 0 Then
	CloseServiceHandle(This._ManagerHandle)
	This._ManagerHandle = 0
Endif

*****************************************************************************
*!* _DeclareDlls()

Declare Integer ChangeServiceConfig In advapi32 ;
	Integer hService, ;
	Integer dwServiceType, ;
	Integer dwStartType, ;
	Integer dwErrorControl, ;
	String  lpBinaryPathName, ;
	String  lpLoadOrderGroup, ;
	String  lpdwTagId, ;
	String  lpDependencies, ;
	String  lpServiceStartName, ;
	String  lpPassword, ;
	String  lpDisplayName

Declare Integer ChangeServiceConfig2 In advapi32 ;
	Integer hService, ;
	Integer dwInfoLevel, ;
	Integer @lpInfo

Declare Integer CloseServiceHandle In advapi32 ;
	Integer hSCObject

Declare Integer ControlService In advapi32;
	Integer hService,;
	Integer dwControl,;
	String  @lpServiceStatus

Declare Integer CreateService In advapi32 ;
	Integer hSCManager,;
	String  lpServiceName,;
	String  lpDisplayName,;
	Integer dwDesiredAccess,;
	Integer dwServiceType,;
	Integer dwStartType,;
	Integer dwErrorControl,;
	String  lpBinaryPathName,;
	String  lpLoadOrderGroup,;
	String  lpdwTagId,;
	String  lpDependencies,;
	String  lpServiceStartName,;
	String  lpPassword

Declare Integer DeleteService In advapi32 ;
	Integer hService

Declare Integer FormatMessage In Win32api ;
	Integer dwFlags,;
	Integer lpSource,;
	Integer dwMessageId,;
	Integer dwLanguageId,;
	String  @lpBuffer,;
	Integer nSize, ;
	Integer Arguments

Declare Integer GetLastError In win32api

Declare Integer GetModuleHandle In Win32api ;
	String  lpModule

Declare Integer GetProcessHeap In win32api

Declare Integer HeapAlloc In win32api ;
	Integer hHeap, ;
	Integer dwFlags, ;
	Integer dwBytes

Declare Integer HeapFree In win32api ;
	Integer hHeap, ;
	Integer dwFlags, ;
	Integer lpMem

Declare Integer OpenSCManager In advapi32 ;
	String  lpMachineName,;
	String  lpDatabaseName,;
	Integer dwDesiredAccess

Declare Integer OpenService In advapi32 ;
	Integer hSCManager,;
	String  lpServiceName,;
	Integer dwDesiredAccess

Declare Integer QueryServiceStatus In advapi32 ;
	Integer hService,;
	String  @lpServiceStatus

Declare Integer StartService In advapi32;
	Integer hService,;
	Integer dwNumServiceArgs,;
	Integer lpServiceArgVectors

*****************************************************************
*!*	#Define ERROR_ACCESS_DENIED						5
*!*	#DEFINE ERROR_SERVICE_NOT_FOUND					1243
*!*	#Define ERROR_INVALID_HANDLE					6
*!*	#Define ERROR_INVALID_NAME						123
*!*	#DEFINE ERROR_SERVICE_DOES_NOT_EXIST			1060

*!* _OpenService()

Local ;
	m.llRetVal As Boolean, ;
	m.lnManagerHandle As String, ;
	m.lcServiceName As String, ;
	m.lnDesiredAccess As Integer

m.llRetVal = FALSE

If This._OpenManager() = TRUE Then

	If This._ServiceHandle = 0 Then

		m.lnManagerHandle 	= This._ManagerHandle
		m.lcServiceName 	= This.ServiceName + NULCHAR
		m.lnDesiredAccess 	= SERVICE_ALL_ACCESS

		This._ServiceHandle = OpenService(m.lnManagerHandle, m.lcServiceName, m.lnDesiredAccess)

	Endif

	If This._ServiceHandle <> 0 Then
		m.llRetVal = TRUE
	Else
		This._GetLastError()
	Endif

Endif

Return m.llRetVal

************************************************************************
*!* _OpenManager()

Local ;
	m.llRetVal As Boolean, ;
	m.lcMachineName As String, ;
	m.lcDatabaseName As String, ;
	m.lnDesiredAccess As Integer

m.llRetVal = FALSE

If This._ManagerHandle = 0 Then

	m.lcMachineName 	= Null
	m.lcDatabaseName 	= Null
	m.lnDesiredAccess 	= SC_MANAGER_ALL_ACCESS

	This._ManagerHandle = OpenSCManager(m.lcMachineName, m.lcDatabaseName, m.lnDesiredAccess)
Endif

If This._ManagerHandle <> 0 Then
	m.llRetVal = TRUE
Else
	This._GetLastError()
Endif

Return m.llRetVal

*******************************************************************
This is not a complete, ready to use solution, but I think you may be able to get some good ideas from this code.

Carlos Alloatti
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform