![]() |
|
Published 1999-06-28 Printer-friendly version
How do you know when your system is recovering from a crash or power failure? If you knew, you could check for any damage and run any necessary recovery procedures. This is not, however, an article about what to do when you crash; it is just concerned with figuring out that a crash happened.
If your application cannot work out that it is being restarted after a crash, then it has to rely on a user telling it that this is the case. But there are crashes and there are crashes, and your user may not always realise that a recovery is required. And even if they know, they may not always tell.
So, how to know? Here is a method that uses the Clarion DDE commands. I use it in an accounting app. It is intended to be used in combination with a log file (called EVENTS) which records each logon plus other significant events occurring within the app.
In a multi-user setting, when a user logs on the log file of current events will often show that other users logged on earlier and that they are presently doing various things using their respective instances of the app. That is, there will be unacquitted events (begun, but not ended) for each of these users. This contrasts with a single-user situation, where the simple presence of unacquitted events in the EVENTS file when a solitary user logs on is sufficient to tell the application that crash recovery is needed. In a multi-user situation, you need a detection method which is independent of the log file. Here's how you do it.
First you create a DDE server as a very small, separate program. It is not going to be much of a server, since its only purpose in life is to say: "I'm here, I'm here." Listing 1 shows the entire source code for such a server (called NoDelete.CLW to dissuade accidental deletion). (You can also download the source.)
PROGRAM
MAP
INCLUDE('DDE.CLW')
END
DDERetVal STRING(20)
Window WINDOW,AT(,,95,13),COLOR(0FFFF80H)
ENTRY(@s20),AT(0,0,5,12),USE(DDERetVal),HIDE
PROMPT('PowerBooks Sentinel'),AT(12,2),USE(?Prompt1)
END
MyServer LONG
CODE
OPEN(Window)
! Get the channel number
MyServer = DDESERVER('RunControl','CheckAlive')
ACCEPT
CASE EVENT()
OF EVENT:DDEexecute
RETURN ! blows away on any event
END
END
To create this program, click on Project in the main menu. Choose New and you will see the window in Figure 1.

Choose Hand Coded Project and enter your Working Directory. Click the OK button and fill in the following form as shown in Figure 2.

Click the OK button and the window in Figure 3 appears:

Now, double-click on "Nodelete.clw" and enter the code shown in Listing 1.
The business part of the code is the DDESERVER
function. This registers NoDelete.exe as a DDE server with Windows,
giving it an application name of "RunControl" and a topic of
"CheckAlive." This is a server that doesn't have to respond to
clients. In fact, the first time it detects any command (via OF
EVENT:DDEexecute it will terminate.
You could probably make the server even smaller, with a bit of experimenting.
When the main app is run it checks to see if NoDelete.exe is running. If it is not, and there are log file records that are unacquitted then very likely the application is recovering from a crash. The only other thing that can have happened is that NoDelete.exe has been accidentally deleted. I will discuss that eventuality later.
Listing 2 shows the code to run as you come into the app and process a request to log on from a user.
ServerString = DDEQUERY('RunControl','CheckAlive')! Attempt link
IF UPPER(ServerString) <> 'RUNCONTROL:CHECKALIVE' ! Not up
RUN('NoDelete.exe') ! Run it and return immediately
ServerBegun = '' ! Blank passed to returned parameter
ELSE ! Says server just started, non-blank
ServerBegun = ServerString ! Says server already running
END
When NoDelete.exe runs, it displays the window shown in Figure 4.

DDE communications depend on there being a window and an ACCEPT loop in the code that makes up the server. Without these, DDE won't work.
The DDEQUERY function does not communicate with the server. Rather, it addresses Windows and it asks only if a server with the specified application and topic name is registered. If it is, Windows returns the same information slightly differently formatted. That is all you want to know: that is, is your little sentinel program alive and well?
Well, there is one more thing you have to do at start up, but you cannot do it now because you need to give NoDelete.exe time to get up and running, if it isn't already. So, quite a ways into the Main Frame procedure put one line of code:
GLO:Server =
DDECLIENT('RunControl','CheckAlive')
This code defines your app as a client of the NoDelete server and gets a channel number for the connection between this instance of the app and the server. Every time the app is run, each instance of it gets a distinct channel number. You need a channel number for one reason only: when you are shutting down the last instance of your app, you want to terminate the server. You need a channel number to be able to do this.
Just before your app shuts down, embed the code shown in Listing 3.
A convenient Legacy embed point is "End of Procedure before Closing files".
IF RECORDS(EVE:Key_Begun) = 0 ! nothing left in LOG file
SETCURSOR(CURSOR:Wait)
OPEN(ClosingWindow)
ClosingString = ' Please wait a few seconds'
DISPLAY(?ClosingString)
DDEEXECUTE(GLO:Server,'[ShutDown]') ! so shut down server
SETCURSOR()
END
The DDE action here is to send a command to the server. The
command DDEEXECUTE requires a channel number as a
parameter. It doesn't matter which instance of your app is the
last to close. It doesn't matter that each instance will have
a different channel number. The effect on the server is the same.
It terminates. Trouble is, it takes a while to do so, so
that's why you see a "Please wait" message in the above
code.
What if a user accidentally deletes the little server? It's not that easy to do. NoDelete.exe does not appear in the toolbar (in a 16 bit version running under windows 95) and it has no [X] box to tick. A user would have to press Ctrl Alt Del and deliberately do a deletion from the list of running programs. But, in the spirit of free and independent enquiry, some do, don't they.
One method of protecting against this is simple redundancy. Have two servers with different names and don't recognise a crash unless both are not there. If one is there, restart the other. Give the second one a name similar to those always in the list of running programs. Make sure its window displays right on top of the first one, with exactly the same text so the user has no clue there are two. The amount of overhead goes up slightly, particularly on shut down. It takes around 5 seconds to terminate one server on a 300Mhz Pentium. You decide.
David Podger is from the old school. He joined IBM in Sydney, Australia in 1961 and has a Ph.D. from the 70's on the theory of software design, which he wrote in Manchester, England. These days he is focused on distance education and on the delivery of interactive case studies over the web.
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $184
(includes all back issues since '99)
Renewals from $134
Two years: $274
Renewals from $224