Yesterday I found the root cause of a strange bug that had been pestering me for some time already.
As you may or may not know, I wrote a desktop utility for the Nokia 9300/9500 called iDesk. One of it's features is a task manager that, among other things, allows you to close other applications. But, sometimes after closing another OPL application from iDesk, iDesk crashed with a 'Does Not Exist' error. Why I sometimes happened and other times it didn't I couldn't figure out.
This now turns out to be a problem with the OPL runtime and, until it's fixed in there, can be easily circumvented.
Background
The cause of this behaviour is the fact that the OPL runtime only loads the font file needed for OPL applications once. It it is already in memory, it doesn't load it a second time. This is completely ok, but what is not ok is that when the first instance of the OPL runtime gets unloaded, it also unloads the font, making it unavailable to other running OPL applications. An example scenario:
First OPL application 1 starts. The OPL runtime loads EON14.GDR into memory.
The OPL application 2 is started. The OPL runtime detects that EON14.GDR is already loaded and doesn't load it again.
OPL application 1 is terminated and the OPL runtime UNLOADS EON14.GDR.
Now OPL application 2 tries to load a bitmap, create a bitmap or set the font and subsequently crashes with a 'Does not exist' error, since all these operations rely on EON14.GDR being loaded into memory.
This also explains why this did not always happen. If my application was started first and then closed some other OPL applications, the font would still be loaded in memory. Only if my application was not started first and closed the first started OPL application, the problem would occur.
Workaround
Luckily the Symbian OS is quite smart and doesn't load a font twice even if that is requested by applications. It simply keeps a reference counter to a loaded file. When a new font is added, the Symbian OS first checks whether it is already loaded and, if so, increases the reference counter. If not, the font is loaded and the reference counter is set to 0. Similarly, when a font is unloaded, the reference counter is decreased. Only when it reaches 0, the font is actually unloaded.
This behaviour makes it easy, although a little cumbersome, to work around the problem: simply load the EON14.GDR font file in every OPL program and unload it when the program ends. The code for doing that looks something like this:
REM Make sure this is the first procedure in your application
PROC Startup:
LOCAL OPLFontID&
REM If the runtime is installed on C:, the font is in C:\System\Libs,
REM otherwise it's in D:\System\Libs
IF EXIST("C:\System\Libs\Eon14.gdr")
OPLFontID&=gLOADFONT("C:\System\Libs\Eon14.gdr")
ELSE
OPLFontID&=gLOADFONT("D:\System\Libs\Eon14.gdr")
ENDIF
REM Now we call the main routine of the application. Substitute this with
REM the real name of this main procedure
Main:
REM Program is now finished, so unload the font
gUNLOADFONT(OPLFontID&)
ENDP
I've tried this with several applications and it works flawlessly. Of course the problem will be fixed in the next release of the OPL runtime (very likely using exactly the same approach of always loading and unloading the font). The best part of this workaround is that it will still work as expected after the problem has been fixed in the runtime, so you don't need to make any changes to your application anymore after the new runtime is released.