Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Windows Services with VFP
Roberto C. Ianni, August 1, 2006
The present article is one of those that Martín Salías calls "low level fox", the general idea is to develop a service for the operative system whose only goal will be to run a VFP application in charge of handling the business logic.
Summary
The present article is one of those that Martín Salías calls "low level fox", the general idea is to develop a service for the operative system whose only goal will be to run a VFP application in charge of handling the business logic.
Description
The present article is one of those that Martín Salías calls "low level fox", the general idea is to develop a service for the operative system whose only goal will be to run a VFP application in charge of handling the business logic.

Goals

In order to develop the service we need to use VC++, hence I will work with VC6 and take advantage of a base class developed some time ago by "Nigel Thompson", since it is very well written and is worthless to waste time in this because he did a very good job and explained it even better.

We will discuss the following points:

  1. Creation of a service in VC++.
  2. Creation of a VFP application for testing.
  3. Test of the service

Creation of a service in VC++

To create the service we should first to create an application of the type console in C++, but keeping in mind that it will be able to use the MFC libaries in order to make the coding easier in some ways, such as these two images show.

At this point we already created a work environment fit to develop any C++ program set as console type, now, since we intend our application to be a service we will add the classes developed by "Nigel Thompson" to the project. These happen to be the files NTService.cpp, NTService.h y NTServmsg.h as shown bellow..

Once the classes are included in the project we need to create our class to Start working, but this class has to inherit from the class "CNTService" and to achieve this we will define a new class called "CVFPService", as shown.

At this point we have the base to start with, now we need to overwrite the methods of the class CNTService to include or logic. The methods we will use are:

  1. OnInit
  2. OnShutdown
  3. OnStop
  4. Run

Method OnInit

This method will run only when the operative system try to start the service. Here we will try to read the required info for the Fox application that we have to execute as a service and will record an the Event Log that we passed this point.

Method OnShutdown

This method will run only when the operative system try to stop the service, either automatically or by the user action.

Method OnShutdown

This method will run when the service up to be shutdown of the process list of the O.S.

Method OnStop

This method will run only when the operative system stops the service, either automatically or by the user action.

Method Run

This method is perhaps the most important, for we will write here the logic that will run as long the service is started. For our case, it will execute the Fox program and will control that it keeps running.

Now, to add the first method just right click on the class CVFPService and chose the option new method from the context menu, as the figure shows.

Now we add the code to run when starting the service, which is:

BOOL CVFPService::OnInit()
{
	BOOL bReturn = TRUE;
	DWORD dwType = EVENTLOG_INFORMATION_TYPE;
	CString sMsg;
	LogEvent( EVENTLOG_INFORMATION_TYPE, 101,
		"The service 'VFP - Service 1.0' is starting" );

	// Read configuration.
	LoadConfig();

	// Erases the flan file, if exists.
	DeleteFile( m_sAppVFPEnd );

	// Records the name of the application to run.
	if( !m_sAppVFP.IsEmpty() )
		sMsg.Format( "The application to run is: %s", m_sAppVFP );
	else
	{
		sMsg.Format( "No application to run, the service will be "+;
                 stopped. [Config file was taken from:%s]", GetPathINI() );
		bReturn = FALSE;
		dwType  = EVENTLOG_ERROR_TYPE;
	}

	LogEvent( dwType, 101, sMsg );

	return bReturn;
}

As we can see at the example, the code is not complex. We first record at the Event Log that the service is about to be started, then we read the settings from the ini file and erase a file, which we will use as a flag so that the service can indicate to the VFP application that it sould stop.

void CVFPService::OnShutdown()
{
	LogEvent( EVENTLOG_INFORMATION_TYPE, 101,
		"Shutdown - 'VFP - Service 1.0'" );

	CNTService::OnShutdown();
}

void CVFPService::OnStop()
{
	LogEvent( EVENTLOG_INFORMATION_TYPE, 101,
		" 'VFP - Service 1.0' service is stopping" );

	CNTService::OnStop();
}

The methods OnShutdown and OnStop are quite simple, they just record they were executed so that the events can be controlled from the console.

void CVFPService::Run()
{
	CString sMsg;
	sMsg.Format( "About to run the application '%s'", m_sAppVFP );
	LogEvent( EVENTLOG_INFORMATION_TYPE, 101, sMsg );

	// Starts the application in a separated process.
	if( !IniciarVFPApp() )
	{
		sMsg.Format( "Couldn't start the application de VFP\r\n%s\r\n%s",;
                 m_sAppVFP, m_sError );
		LogEvent( EVENTLOG_ERROR_TYPE, 101, sMsg );
		SetStatus( SERVICE_STOPPED );
		return;
	}

    while( m_bIsRunning )
	{
		// Nothing to do, I keep waiting....
        Sleep( 5000 ); // 5 seconds.

		// Verify that the VFP application is running.
		if( !ConsultarVFPApp() )
		{
			sMsg.Format( " VFP application closed while the service "+;
                         "was executing: ['%s']", m_sAppVFP );
			LogEvent( EVENTLOG_ERROR_TYPE, 101, sMsg );
			SetStatus( SERVICE_STOPPED );
		}
    }

	// Stops the application because the service is about to stop.
	DetenerVFPApp();

	LogEvent( EVENTLOG_INFORMATION_TYPE, 101,
		"is finishing to run the application" );
}

Let us see now thel method Run(), the most important of the class. What it does is to Stara the application through the function IniciarVFPApp() and then goes into a loop as long the variable m_bIsRunning is true, d, this variable will be true until the service be stopped and each loop the service keeps waiting 5 seconds and then checks that the VFP application is still running. Once it exits the bucle, the method calls the function DetenerVFPApp() to inform to the Fox application that it must to shutdown, or shut it down by itself otherwise.

The method IniciarVFPApp, simple creates anew process of the type OutProcess and keeps it running.

BOOL CVFPService::IniciarVFPApp()
{
	...
	if( CreateProcess( m_sAppVFP, NULL, NULL, NULL, FALSE,
					NORMAL_PRIORITY_CLASS,NULL, NULL, &siData, &piData) )
		m_hProcess = piData.hProcess;
	else
	{
		DWORD dwErr;
		LPVOID lpMsgBuf;
		bReturn = FALSE;
		dwErr   = GetLastError();

		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dwErr,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL );

		// Error Message.
		m_sError.Format( "Error:0x%X, Msg:%s", dwErr, lpMsgBuf );

		LocalFree( lpMsgBuf );
	}
	...
}

As we see, the API function CreateProcess does the job and should it fails it gets the error to be recorded at the Event Log. The variable m_sAppVFP is the one that carries the name and full path of the application to run, and it was previously set when the configuration file was read.

All we have to do now is how to stop the VFP application and to check that is running. To stop the application we use the method DetenerVFPApp creating a file and wait that the application normally ends its execution, otherwise, we suddenly kill the process as is shown bellow.

BOOL CVFPService::DetenerVFPApp()
{
	int nCount = 0;
	FILE *fEnd;

	// Generates the end of process mark.
	fEnd = fopen( m_sAppVFPEnd, "wt" );
	fclose( fEnd );

	// Once the flag file is created, wait up to
	// 5 seconds, otherwise kills the app.
	while( ConsultarVFPApp() && nCount < 5 )
	{
		nCount++;
		Sleep( 1000 );
	}

	// Erases the flan file.
	DeleteFile( m_sAppVFPEnd );

	if( ConsultarVFPApp() )
		return TerminateProcess( m_hProcess, VFP_EXIT_CODE_OK );
	else
		return TRUE;
}

At last, we have juste to see the method that checks that the application is running, the method ConsultarVFPApp. It reads from the O.S. the time corresponding to the process (the application) and check that the "exit" equals zero, otherwise, it would be a sign that the process ended for any reason.

BOOL CVFPService::ConsultarVFPApp()
{
	...
	if( GetProcessTimes( m_hProcess,
					&lpCreationTime, &lpExitTime,
					&lpKernelTime, &lpUserTime ) )
	{
		// ExitTime has to be zero, otherwise
		// the process is over.
		if( lpExitTime.dwHighDateTime != 0 ||
			lpExitTime.dwLowDateTime  != 0 )
			bReturn = FALSE;
	}
	else
		bReturn = FALSE; // The process couldn't be checked.
	...
}

We use for this the API function GetProcessTimes, which provides quite info, but now we are specially interested in the variable lpExitTime, which should be zero.

So, we already Developed our VC++ service to run a VFP application, so that we just compile it and -the most important- we ended my boring lectura on C++, very few guyslike it, but it wouldn't me if I don't mention it somewhere at my articles.

Creation of a VFP application for the test.

To create the application just do a Fox project with a main program that will loop until it finds the exit file.

LOCAL lnHand, lnPaso
DECLARE Sleep IN "kernel32" Long dwMilliseconds

lnHand = FCREATE( "C:\FileProcess.txt" )
lnPaso = 0

DO WHILE !FILE("C:\EndProcess.txt")
	lnPaso = lnPaso + 1
	FPUTS( lnHand, "Paso número " + ALLTRIM( STR( lnPaso ) ) )
	Sleep( 1000 )
ENDDO

FCLOSE( lnHand )

An important point is to define a config.fpw file for the project that indicates that it won't have resources files, otherwise, the first time it runs it will ask for the creation of the files and -as a service- it is not interactive and it will "hang" waiting for an input that the user will never provide.

Config.fpw

RESOURCE = OFF

So we just compile the application with the business logic, in this case, to write a file once per second.

Test of the service

To run the test, we must first to define the configuration file, its name must be "Services.ini" and has to be at the same place where the.exe of the service "Services.exe" lies. Two entries must be filled in it, the first one indicates which is the Fox application to be ran and the second indicates the name of the file to be created in order the application learns that it must stop in a controlled way.

[GENERAL]
VFPApp=C:\SvrFOX\SvrFox.exe
EndVFPApp=C:\EndProcess.txt

Once configured, we need to install the service on the machina, in order to do this just execute the service created with VC++ with the parameter "-i" to install it and "-u" to uninstall it.

C:\Services\Services.exe -i
Service VFP installed
C:\Services\Services.exe -u
Service VFP removed.

If we look now at the services list of the operative system, or service will appear as installed but shut down.

If we want to Start it, we get this.

And when trying to stop it, will get this.

As we can see, this is no other thing that a service of the operative system, and we are running our VFP application totally without effort.

Ok, I hope you liked it and I also wanted to say that if anybody wants me to talk about any particular issue, just let me know… I'm running out of ideas...

Roberto C. Ianni, Banco Hipotecario
Roberto Ianni (Buenos Aires, Argentina) is a professional software developer for the last 4 years. He has programmed in C++, VFP, VC++, C#, and has lots of experience in VFP and VC++. He currently works in the Banco Hipotecario as an Analyst-Developer.
More articles from this author
Roberto C. Ianni, December 1, 2004
Today we are going to talk about data compression. This is something we do frequently, something which is useful for many different tasks.
Roberto C. Ianni, June 1, 2004
The "EventLog" or "Event viewer" is something we use on a daily basis, to obtain information about what happened to our computer at a certain moment; for example, when an application generates an error, most of us will intuitively look at the EventLog to see what information it left for us.
Roberto C. Ianni, May 1, 2004
The "EventLog" or "Event viewer" is something we use every day, to obtain information about what happened to our computer at a certain moment. For example, when an application generates an error, most of us instinctively go to the EventLog to see what information it left for us.
Roberto C. Ianni, July 1, 2004
One of the great limitations I find in VFP is the lack of low-level manipulation I can obtain from it. For many, this might not be an inconvenience, but several times I have encountered a problem which can't be solved with Fox, and it is then that I use a DLL or an FLL.
Roberto C. Ianni, August 1, 2004
As an objective for this issue, I want to propose the following points, to complete the first part of the development of an FLL. Advanced development with an FLL; Accessing VFP data; Executing VFP commands from a FLL; MultiThreading; FLL with VFP 9.0; Executing Assembler from VFP.
Roberto C. Ianni, October 1, 2004
This article is the continuation of "Sending email through Visual FoxPro without additional components", published last March. Back when I wrote this article, I didn't think it would get such a huge response, but it did, hence, due to the mails coming from everywhere I decided to write again on that...
Roberto C. Ianni, November 1, 2004
In this second part we will learn how to: Implement MIME, Attach binary files, the final implementation of the WSendMail class, and the use of the WSendMail class.
Roberto C. Ianni, May 1, 2005
Windows Control Panel holds the configuration from several of the operating system's applications, and from some other applications that provide an applet to appear there. Learn how to build this kind of applets to give your application a professional configuration mechanism.
Roberto C. Ianni, April 1, 2005
The intention of this article is to finish with the use of POP3 (Post Office Protocol 3), implementing everything we have seen so far.
Roberto C. Ianni, January 1, 2005
This article is the beginning of receiving email from VFP; this is complemented with the articles you have already seen about sending email from VFP. To be able to do this, we will have to use the POP3 protocol (Post Office Protocol 3).
Roberto C. Ianni, February 1, 2005
We will continue some of the points mentioned in the previous article, in order to be able to implement message reception. The points to be developed are: POP3 Authentication (both Flat and MD5 authentication methods) and EMail parsing.
Roberto C. Ianni, March 1, 2004
Many of us send email using third-party tools such as Outlook Express or Microsoft Outlook, among others, and most likely have encountered registration problems in the client, licencing problems, OLE errors that only occurr at the client's site but not in our developer environment. Also problems if ...