Achieving Clean Shutdown of DP4 in Complex Configurations

DP4 is frequently loaded in a relatively complex configuration where multiple DP4 components are running, and in addition there are "daemon" applications that run in the background. In this type of configuration, if the DP4 service is stopped, it is sometimes difficult for applications to close down cleanly, because components are may be closed down in the wrong order, (or because of problems with DP4 itself)

This article details some changes in 4.622 that will make it easier to avoid this type of problem. It also documents the messages applications need to listen out for to know that DP4 is being shut down.

Listening for Shut Down

If the DP4 service is going to be shut down the DP4 system broadcasts a registered windows message to notify all top level windows. Applications can listen for this message and close themselves down when the message is received. In this situation applications should not prompt a user to save changes or do anything similarly interactive. Applications have only a very limited amount of time during which they can still call DP4, and they should do nothing except close their connections cleanly, possibly after a dp4_db_commit() if desired.

Interactive DP4 C applications that use the DP4 terminal manager will normally process this message correctly automatically, as it is handled inside trmw32.dll

To handle this message manually, you need to do the following:

  1. Discover the name and number used to register the message. This is a configuration option in DP4.INI so hard coding the name is incorrect. The correct code to do this from C or C++ is:
    #include <windows.h>
    #include "config.h"
    
    unsigned get_dp4_ipc_message()
    {
      gmem_type * gmem_ptr;
      cf_xinit_new(0,0,"",CFS_ALL,gmem_ptr = gm_gmem_get());
      return RegisterWindowMessage(cf_prefix2);
    }
    

    cf_prefix2 is a macro that evaluates to the correct string for the DP4 IPC message. It refers to gmem_ptr. C applications have this avilable as a global variable, but you may prefer not to access this, and it is not initialised until cf_xinit_new() has been called.

    The call to cf_xinit_new() can be omitted if you can guarantee that a DP4 Connection has already been established by the time you call this function. DP4 C programs that use the main() function in the DP4 C library (ntown or ccown) can omit this line. If you are connecting to DP4 via the DP4 class interface code in dp4dbase.cpp you might like to know that the 4.622 version of the code, which is compatible with 4.621, contains a static method, dp4_ipc_message() in CDP4Connection, for returning this value.

  2. Add code to the main WndProc() for your top level window to detect this message and take appropriate action. Here is example code for a WIN32 API program:

    static int dp4_ipc_message = get_dp4_ipc_message();
    
    switch (wMsg)
    {
      /* Process messages for main window */
      ...
      ...
    
      default:
        if (wMsg == dp4_ipc_message && wParam==2)
          PostQuitMessage(6); 
    }
    

    For an MFC program add a handler for this message in mainfrm.cpp :

    const UINT dp4_ipc_message = CDP4Connection::dp4_ipc_message();
    BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    //    DO NOT EDIT what you see in these blocks of generated code !
      ON_REGISTERED_MESSAGE(dp4_ipc_message,OnDP4IPCMessage)
      // More Message handlers
    END_MESSAGE_MAP()
    
    LRESULT CMainFrame::OnDP4IPCMessage(WPARAM wParam,LPARAM lParam)
    {
      if (wParam == 2)  // NB This message can occur with other wParam values. Only wParam == 2 is a close down
        PostQuitMessage(6);
      return 0L;
    }
    

    The declaration for OnDP4IPCMessage() looks like this in the CMainFrame mainfrm.h:

        afx_msg LRESULT OnDP4IPCMessage(WPARAM wParam,LPARAM lParam);
    

    If you prefer you can delay the initialisation of the dp4_ipc_message variable until the CMainFrame object is created. In this case omit the const from the declaration of dp4_ipc_message and initalise it explicitly in the CMainFrame constructor. Please note that the ON_REGISTERED_MESSAGE macro provided by MFC is particularly pernickety. Unlike some other message macros it cannot be used in CWinApp derived classes, only in CMainFrame or other CWnd classes. CMainFrame is the correct place as this is your application's top level window.

    For either WIN32 API or MFC programs you can exit by some other method than PostQuitMessage(6) if you prefer. For example, if the program might not be processing its main message loop you can call exit() explicitly instead of PostQuitMessage(). The exit code of 6 passed to PostQuitMessage() matches what DP4 C programs return as an exit code in this case, but you can use another value if you prefer.

Handling DP4 IPC Errors

If a DP4 program continues to run after the DP4 service has stopped or failed, then any call to DP4 will return a failure code. Typically you will be notified of the error in your error function callback or through a call to the dp4_error() function. An IPC error will always be reported at error level 4, and with an error code of 9. If you get this error your program must exit immediately without making further calls to DP4, and after displaying at most one error message notification. DP4 may not be restartable while any program with a failed IPC connection is still running (this depends on the details of the DP4 configuration and the connection handles of the unterminated applications). As of release 4.622 a failed program cannot get a new connection - clean up code, which is only executed when all existing DP4 applications have terminated, must be run first. IPC errors are not the same as network errors, database corruptions, etc, and programs must not try to survive such errors. In release 4.621 and earlier releases failed programs that continually try to continue to communicate with DP4 can eventually upset other applications. This is no longer the case in 4.622. In addition unterminated applications could cause problems with temporary files. Once acgain this is no longer the case.

In release 4.621 you might get an IPC error when there is actually not a problem with IPC on the local machine. For example you can get the error when the network manager on another machine cannot communicate with the database manager on that machine. This is no longer the case in release 4.622, as in this situation a new, different fail error code is returned, error 15, which means that there is an IPC error on a server machine. It is suggested that you treat this similarly to a fail 7 (network) error.

Issues with DP4

When shutting down programs should attempt to close their database connections. However it is possible that the DP4 service is already no longer available at this stage, and this can result in repeated or spurious error reports:

Restarting DP4

If DP4 is loaded in a "-noremain" configuration, so that the DP4 service terminates once the last DP4 application terminates, then there may be problems if a DP4 application is launched while the service is stopping. It was always the intention that DP4 applications would wait for DP4 to be restarted in this situation, but in fact you may see an error message. This has been fixed in release 4.622.