Alan in wonDLLand

Yesterday evening I had another poke at the Java wrapper for OziExplorer that I'm sporadically working on, OziExplorer is the GPS mapping application that I use. It's a pretty fat API so I'm gradually chipping away at it. My latest tweak was to add a simple Swing GUI to my test app, but as soon as I did it blew up every time I exited with two rather foreboding messages, The exception unknown software exception (oxoeedfade) occurred in the application at location 0x7c81eb33 and Runtime error 217 at 0009D54. Google told me that second message was coming from Delphi - OziAPI.dll is generated with Delphi, but didn't shed much light on exactly what was causing the error. Some experimentation revealed that the problem was actually nothing to do with Swing, I could get the same effect by just calling System.exit immediately after calling any of the functions in the Delphi DLL. If I just let the program fall off the end of main the problem didn't happen, which suggested that whatever was going belly up was being caused by something that System.exit was doing, either directly or by indirectly perturbing the ordering of the JVM exit processing.

A bit more googling revealed that other people had hit the same problem and that one workaround was to manually open the DLL with LoadLibrary, find the symbol addresses via GetProcAddress and finally manually free the DLL with FreeLibrary on exit. (For those in the Solaris seats that's the Win32 equivalent of dlopen, dlysm and dlclose). This suggested to me that the problem was something to do with the timing of the DLL initialisation and/or cleanup. In my case I was pulling in the OziAPI DLL via a linker dependency from the JNI DLL, and I was loathe to have to manually pull in OziAPI.dll and look up the symbols just to work around this problem. Some more spelunking through the Microsoft documentation revealed the following interesting snippets:

If you terminate a process by calling TerminateProcess or TerminateJobObject, the DLLs of that process do not receive DLL_PROCESS_DETACH notifications. If you terminate a thread by calling TerminateThread, the DLLs of that thread do not receive DLL_THREAD_DETACH notifications.

The TerminateProcess function is used to unconditionally cause a process to exit. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.

I grabbed a copy of strace for NT and traced the execution of my test application, and although I could see calls to TerminateProcess I couldn't see any calls at all to ExitProcess, not only when I called System.exit but also when I let the program fall of the end of main. Hmm. If that was what was happening there didn't seem to be much I could do to change the behaviour of System.exit, but I already knew that if the process exited by falling off the end of main the problem didn't occur - what I needed to do was to find out how to make that happen from inside a Swing callback. I was wandering around the JDK documentation when I found an interesting document titled AWT Threading Issues, which says:

Therefore, a stand-alone AWT application that wishes to exit cleanly without calling System.exit must:
  • Make sure that all AWT or Swing components are made undisplayable when the application finishes. This can be done by calling Window.dispose on all top-level Windows. See Frame.getFrames.
  • Make sure that no method of AWT event listeners registered by the application with any AWT or Swing component can run into an infinite loop or hang indefinitely. For example, an AWT listener method triggered by some AWT event can post a new AWT event of the same type to the EventQueue. The argument is that methods of AWT event listeners are typically executed on helper threads.

So the code to work around my problem turned out to be trivial, I just needed to put the following in my exit button callback to make sure that all the threads in the application exit cleanly:

        for (Frame f : Frame.getFrames()) {
            f.dispose();
        }

Hey presto, everything now works. Obviously if the application creates it's own threads they will also need to be gracefully terminated. I'm still discussing the exact cause of the problem with the JVM folks, but for now I'm happy that I've got a workaround. Hopefully this information will save someone some grief as my searching revealed that quite a few people had been hit by this issue but that nobody quite understood how to work around it other than the nasty manual DLL loading hack.

Categories : Java, Tech


Re: Alan in wonDLLand

I have also a problem with the OZIExplorer API, unfortunatly I'm not able to solve it. I writes my code in C++ with the Borland compiler. The problem is the following: When I close my application if I have used some OZI API in my OZIExplorer thread there is an error "Win32 Error code 1400". I have think that the problem is within the API and I have tried to manualy release the API... This is the function which I wrote... void FreeOziApiDll(void) { FreeLibrary(oziApiDll); } The original file provided with the OZI API have only a routine... HINSTANCE oziApiDll; //Handle to DLL ToziSaveMap oziSaveMap = NULL; ToziLoadWPfile oziLoadWPfile = NULL; ToziLoadTRKfile oziLoadTRKfile = NULL; . . . . int LoadOziApiDll(void) { oziApiDll = LoadLibrary("oziapi.dll"); if (oziApiDll == NULL) {return -1;} oziCloseApi = (ToziCloseApi)GetProcAddress(oziApiDll,"oziCloseApi"); if (!oziCloseApi) { FreeLibrary(oziApiDll);return -2;} oziSaveMapFlag = (ToziSaveMapFlag)GetProcAddress(oziApiDll,"oziSaveMapFlag"); if (!oziSaveMapFlag) { FreeLibrary(oziApiDll);return -2;} . . . . . } Could you help me in the resource release and send me a mail with you idea? Thanks a lot Fabrizio

Re: Alan in wonDLLand

Fabrizo, you need to be sure to call OziCloseApi() before your application exits. I've also cracked the 'hanging OziExplorer click callback' issue as well - see http://blogs.sun.com/roller/page/alanbur?entry=wondlland_revisited