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.
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:
#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.
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.
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.
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:
In 4.620 and 4.621 the dp4_db_close() function, through which all database close requests are routed contains a bug (now fixed). In normal operation this function may request that a system message be displayed. This would normally be some message about the database log file or backup status. The bug is that dp4_db_close() does not check the error status before setting up the parameters for this message. As a result, if an error message is already pending because of a problem during the dp4_db_close() the parameters for that error are corrupted with new and spurious numeric values. Therefore you may receive a message:
Database nr not open for user nr
with corrupt and implausible numeric values. Or worse, you may receive this message:
DP4 System error 9. Origin: Program bug. No record for wkf_fetch() on type nr index nr
with corrupt and implausible numeric values, when actually you should have received this completely different message:
DP4 Fail Error 9 Inter-process Communication Error nr nrThis problem can be fixed by upgrading usrlibt.dll (or syslibce.dll on Windows CE) to the latest patch release.
There is usually not much point reporting errors that arise when closing the database (though it may be worth doing so in "debug" builds in order to check you are closing down DP4 correctly when your program terminates). In 4.622 the DP4 C++ interface (dp4dbase.cpp) has been modified so that the Close() method in CDP4Database will not report DP4 errors unless you pass an explicit TRUE parameter. This will not suppress normal db_close() messages which might in theory arise (such as "Remove Log Disk"). In practice such messages never curently appear in typical DP4 configurations, though it is quite possible that new messages of this type might be required in future.
If you use the named connection configuration, then when DP4 is closed down, tcpmgr may terminate and log your locally running applications programs off from the database manager, before you have a chance to clean up, and you will get system error 10 errors as a result. In 4.621 you can partially work around this problem by running tcpmgr at a lower priority that normal, but this does not work on machines with more than one processor, or with hyper-threading, and will not always work in any case. In 4.622 tcpmgr has been modified to alleviate this problem as follows:
This means that locally running applications have approximately three seconds during which they can continue to call DP4 before exiting.
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.