Detecting Crashes With DDE

by David Podger

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.)

Listing 1. The DDE server (NoDelete.CLW)
    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.

Figure 1. Creating a new project.

dde_fig1.gif (10894 bytes)

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

Figure 2. The project properties window.

dde_fig2.gif (12222 bytes)

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

Figure 3. The project tree.

dde_fig3.gif (15298 bytes)

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.

Listing 2. Checking for the existence of the DDE server.
 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.

Figure 4. The DDE server window.

dde_fig4.gif (1842 bytes)

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".

Listing 3. Shutting down the DDE server.
  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.

Download the source


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.

Printer-friendly version

Reader Comments

To add a comment to this article you must log in.

 
 

Search

 

Advanced Search
Topical Index

Related Articles

Subscribe to
ClarionMag

One year: $184

(includes all back issues since '99)

Renewals from $134

Two years: $274

Renewals from $224

More Info

Subscribe Now!

ClarionMag Blog

RSS Feeds

Updates via Email

Enter your Email


Powered by FeedBlitz

Quick Links