BBC BASIC for Windows
Programming >> BBC BASIC language >> *.exe does not exit cleanly.
http://bb4w.conforums.com/index.cgi?board=language&action=display&num=1261807312

*.exe does not exit cleanly.
Post by 19Grumpah42 on Dec 26th, 2009, 05:01am

A couple of my programmes run fine both in BB4W and compiled, and they exit properly in BB4W; however, the compiled versions want to exit with an error popup titled "Error Deleting File or Folder" with message "Cannot delete file: Cannot read from source file or disk."

The programmes are not trying to delete any file, and I am not involving any removable drive. The programmes are coded to exit at either ON ERROR (I don't knowingly have any of these) or ON CLOSE or in the main listing, as *QUIT*. I run a PROC_cleanup to catch and close all the services I know about (I think).

Does anybody have any ideas what could be causing this? I'm stumped. I usually have all the compiler options checked.

--G
Re: *.exe does not exit cleanly.
Post by admin on Dec 26th, 2009, 09:52am

Quote:
The programmes are not trying to delete any file

Your program may not be, but the BB4W executable certainly is. That's why you're receiving the "Cannot delete file" message!

By far the most likely explanation is that an error is occurring when your executable tries to delete a file or folder in the Temporary directory. If you navigate there with Windows Explorer (typically C:\Documents and Settings\<User>\Local Settings\Temp) - note that you'll need to have enabled 'Show hidden files and folders' - you'll more than likely find several sub-directories named BBCxx.tmp which should have been deleted but haven't been.

Looking in one of those sub-directories may give you a clue as to what is going on. For example is there a DLL file there that you use in your program? If there is, have you remembered to do a SYS "FreeLibrary" on exit? Are there any other files there that you open in your program but fail to close again?

The problem will almost certainly turn out to be that your program is missing a necessary 'cleanup' operation in its ON CLOSE or ON ERROR handler.

Richard.
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Dec 27th, 2009, 02:07am

Thanks, Richard, that certainly sounds right, although I don't quite understand how this is happening.

I currently have BBCCB1.tmp and BBCCB2.tmp in my TEMP/TMP directory. They both contain a collection of ..LIB.BBC files. Both directories are (according to WinXP {Properties}) READ ONLY, but the files are not. I can delete the folders myself without changing the property, so I have done that.

The SYS "FreeLibrary" call sounds most helpful in this connection. Is there perhaps a listing somewhere (documentation) of all known useful "cleanups" for BB4W? This would be most helpful, and avoid piecemeal troubles.

I keep my \TEMP directory on a separate HDD so I can monitor all swapping activity, which will include reads of the ....LIB.BBC files.

I have found that \TEMP I/O during BB4W execution can be at least greatly reduced by assigning a value of {HIMEM = LOMEM + a lot} which is much more than is required to avoid "Out of Memory" errors. Does this fit your expectation? Is there some general guideline for setting up a BB4W programme so that it will make zero use of \TEMP once it has loaded its modules? Is that possible?

Happy New Year to one and all,
--Grahame
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Dec 27th, 2009, 02:22am

Richard, I see that SYS "FreeLibrary" is expected to take a handle argument, and I ran into this....

REM**************
It is not safe to call FreeLibrary from DllMain. For more information, see the Remarks section in DllMain.

Calling FreeLibrary does not affect other processes that are using the same module.

Use caution when calling FreeLibrary with a handle returned by GetModuleHandle. The GetModuleHandle function does not increment a module's reference count, so passing this handle to FreeLibrary can cause a module to be unloaded prematurely.

A thread that must unload the DLL in which it is executing and then terminate itself should call ** FreeLibraryAndExitThread ** instead of calling FreeLibrary and ExitThread separately. Otherwise, a race condition can occur. For details, see the Remarks section of FreeLibraryAndExitThread.
REM***********************

Do I need to worry about all this? Load time or Run time, handles, and, if necessary, decrement its reference count? Over to you....

--Grahame

Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Dec 27th, 2009, 04:35am

I'm rather ignorant of the inner workings of BB4W, so the matter of clearing up "loaded" files in the \TEMP sub-directories leaves me with uncertainties. I note that each instance of any BB4W executable creates its own sub-directory. So far, I am only finding regular ...LIB.BBC files in there. Are there any situations when other relevant files would be put there?

I have just run several Run/Exit cycles of a couple of .exe which have often caused the "file closure" error in the past (I tend to leave them running for several days, only exiting for some particular reason). They have exited cleanly this morning! I really do deprecate seemingly intermittent faults!huh

Out of general curiosity, why does BB4W write out ...LIB.BBC files to the \TEMP directory? Will it ever use them at run time? It says in the BBC-Guide that they are loaded above HIMEM during INSTALL ..... Would there be any situations when they have to be re-loaded? undecided

--Grahame

Re: *.exe does not exit cleanly.
Post by admin on Dec 27th, 2009, 10:27am

Quote:
Is there perhaps a listing somewhere (documentation) of all known useful "cleanups" for BB4W?

Not that I'm aware of; it would be a long list! The need for cleanup is generally mentioned in the section of the documentation to which it is relevant.

Quote:
I keep my \TEMP directory on a separate HDD so I can monitor all swapping activity

Are you sure that's wise? Why would you want to "monitor" swapping activity? In any case, 'swapping' and the use of the temporary directory are completely different things, so why put the temporary directory in a non-standard place? I suspect this is the underlying cause of your problem.

Quote:
I have found that \TEMP I/O during BB4W execution can be at least greatly reduced by assigning a value of {HIMEM = LOMEM + a lot}

Unless you have very little physical memory, running BB4W should not result in swapping. The maximum amount of memory which can be used by a BB4W program (other than by calling API functions) is 256 Mbytes, and it's extremely unusual for less 'physical' RAM than that to be available. If you're seeing swapping, check Task Manager to see what is going on.

Quote:
Is there some general guideline for setting up a BB4W programme so that it will make zero use of \TEMP once it has loaded its modules?

By \TEMP are you referring to the swapfile, the temporary directory or both? It seems to me that you may be muddling up the two things.

Quote:
Do I need to worry about all this?

Not usually. You can't use BB4W to write a DLL, so references to 'DllMain' are irrelevant. The comments about 'GetModuleHandle' are valid, but I wouldn't expect you to be calling that function from a BBC BASIC program. If you are calling it, I'd be interested to see your code.

Quote:
note that each instance of any BB4W executable creates its own sub-directory.

Yes, the 'compiled' executable calls GetTempFileName each time it is run. Normally you won't see these directories because they're automatically deleted on exit - unless for some reason they can't be.

Quote:
Out of general curiosity, why does BB4W write out ...LIB.BBC files to the \TEMP directory?

It's got to put @lib$ somewhere, because BB4W executables must be able to assume that folder exists. The temporary directory has always seemed to me to be the perfect place for it - it always exists, it's guaranteed to be writable and its use is temporary (once the executable has terminated, @lib$ and its contents can safely be deleted).

You seem to feel that accesses to the temporary directory are somehow 'undesirable' and something you need to monitor and perhaps even suppress. I can't understand why you would think that; use of the temporary directory is the norm for Windows programs and should be no cause for concern.

Richard.
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Dec 27th, 2009, 5:10pm

Thanks for the detailed "blow by blow" reply, Richard.

Regarding..

>The temporary directory has always seemed to me to be the perfect place for it - it always exists, it's guaranteed to be writable and its use is temporary (once the executable has terminated, @lib$ and its contents can safely be deleted). > Yes, that sounds good to me.

I am still left with the ambiguity over why the BB4W.exe \TEMP directories are not being deleted upon exit. I'm hoping that the SYS "FreeLibrary" you indicated may solve that.

Incidentally, just to clarify something for you, the \TEMP or \TMP directory is where programmes can write/read/delete their temporary files. The WinXP pagefile.sys is always in the root directory of whatever HDD(s) it is assigned to, and is quite different! [I use a fixed 8182 MB pagefile on this PC, and I usually have more than 1400 MB RAM free].

--Grahame


Re: *.exe does not exit cleanly.
Post by admin on Dec 27th, 2009, 10:45pm

Quote:
I am still left with the ambiguity over why the BB4W.exe \TEMP directories are not being deleted upon exit.

I thought you said that, in your most recent tests, they were being correctly deleted?

Quote:
I'm hoping that the SYS "FreeLibrary" you indicated may solve that.

SYS "FreeLibrary" is unlikely to help in your situation (that would only be the case if there were DLL or OCX files etc. in the BBCxx.tmp directory, and you didn't mention any).

The problem, if one remains, is likely to be either something in your BASIC program(s) or a side-effect of your non-standard Windows configuration.

Quote:
Incidentally, just to clarify something for you, the \TEMP or \TMP directory is where programmes can write/read/delete their temporary files.

I have neither a \TEMP nor a \TMP directory on my PCs (XP or Vista). The normal location for the temporary directory is user-specific and, as I said before, is typically at C:\Documents and Settings\<User>\Local Settings\Temp. To discover the location of the temporary directory you should use the GetTempPath API function.

Quote:
The WinXP pagefile.sys is always in the root directory of whatever HDD(s) it is assigned to

That's not what it says here:

http://support.microsoft.com/kb/555223

"The location and size of the page file is configured in SystemProperties, Advanced, Performance (click the Settings button)".

Richard.
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Jan 3rd, 2010, 05:44am

Thanks for the further information Richard.

I'm still not sure why the actual location of either my temp directories or pagefile.sys has anything to do with the price of tea in Birmingham, since all other programmes and WinXP seem to get along just fine. However, since you were kind enough to show an interest in my particular configuration here is a list of my "SET" statements, which includes a certain amount of what is most probably "other people's junk".

ALLUSERSPROFILE=C:\Documents and Settings\All Users.WINXPNEW
APPDATA=C:\Documents and Settings\Grahame Pratt\Application Data
CLASSPATH=.;C:\Program Files\Java\jre6\lib\ext\QTJava.zip
CLIENTNAME=Console
CommonProgramFiles=C:\Program Files\Common Files
COMPUTERNAME=CORE-2-QUAD
ComSpec=C:\WINXPNEW\system32\cmd.exe
FP_NO_HOST_CHECK=NO
HOMEDRIVE=C:
HOMEPATH=\Documents and Settings\Grahame Pratt
INCLUDE=P:\DownLoad_INSTALLERS\C-NET_Apps\xblite_240\xblite\include
LIB=P:\DownLoad_INSTALLERS\C-NET_Apps\xblite_240\xblite\lib
LOGONSERVER=\\CORE-2-QUAD
M4PATH=P:\DownLoad_INSTALLERS\C-NET_Apps\xblite_240\xblite\include
NUMBER_OF_PROCESSORS=4
OS=Windows_NT
Path=C:\WINXPNEW\system32;C:\WINXPNEW;C:\WINXPNEW\System32\Wbem;C:\Program Files\Common Files\Ulead Systems\MPEGlaugh:\QuickTime\QTSystem\laugh:\DownLoad_INSTALLERS\C-NET_Apps\xblite_240\xblite\bin\laugh:\HDD_parameters\hdparm\bin;C:\Program Files\Common Files\Ahead\Lib\
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 6 Model 15 Stepping 11, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=0f0b
ProgramFiles=C:\Program Files
PROMPT=$P$G
QTJAVA=C:\Program Files\Java\jre6\lib\ext\QTJava.zip
SAN_DIR=P:\SiSoftware_Sandra -Professional_Home -2009
SESSIONNAME=Console
SystemDrive=C:
SystemRoot=C:\WINXPNEW
TEMP=Z:\Temp
TMP=Z:\Temp
USERDOMAIN=CORE-2-QUAD
USERNAME=Grahame Pratt
USERPROFILE=C:\Documents and Settings\Grahame Pratt
windir=C:\WINXPNEW
XBLDIR=P:\DOWNLO~1\C-NET_Apps\xblite_240\xblite
__COMPAT_LAYER=EnableNXShowUI

As you can see from the above listing, I always put TMP and TEMP into the same \TEMP directory, but no-one else is obliged to do that. I simply think that it makes for easier system management. No doubt you have your own personal preferences.

Incidentally, I do apologise for what may have been regrettable shorthand when I said that pagefile.sys is "always on the root directory". I should have been clearer and said that that is merely where I always put them.

Sadly, yes my app. just now closed again with the same error message. I don't have any other applications (about a thousand total here) which do this, so perhaps there is some "flaw" in my hand-crafted BB4W programmes. I'm grateful to you for trying to resolve this one; although I suppose it is not exactly fatal, it does create a bad impression, as I'm sure you will be the first to acknowledge.

By the way ... Happy New Year to everybody.
--Grahame
Re: *.exe does not exit cleanly.
Post by admin on Jan 3rd, 2010, 09:41am

Quote:
I suppose it is not exactly fatal, it does create a bad impression, as I'm sure you will be the first to acknowledge.

Indeed, if it was my program I would consider it completely unacceptable. Hopefully I've now given you enough information for you to debug the problem successfully. Looking in the BBCxx.tmp folder will show you which file(s) could not be deleted, and the only likely reasons why they couldn't be deleted are that they were in use at the time or for some reason couldn't be accessed.

Being 'in use' is most likely to be the fault of your BASIC program, for example failing to carry out some necessary cleanup operation before exit. Carefully check how each of the files which remain in the BBCxx.tmp folder are used in your program, and ensure that none could still be open or locked at the time of exit.

Being 'inaccessible' is more likely to be a 'system' problem, such as the drive on which your temporary directory is located momentarily becoming 'non-ready' (it's not by any chance a removable or USB drive is it?).

I would suggest, as an experiment, moving your temporary directory back to a more conventional place (or at least on your main Windows drive) to see if it helps. This would provide useful diagnostic data.

Incidentally a good reason not to put your temporary directory in the root of a drive is that it isn't a 'user specific' location. Remember that XP is a multi-user OS (even if you're normally the only user!) so it's better for each user to have his own temporary directory. That's the way XP configures itself by default.

I remain very confident that the problem you are experiencing is nothing whatever to do with BB4W, and I would encourage you to redouble your efforts to fix it.

Richard.

P.S. Don't forget to upgrade to v5.91a; v5.90b is no longer supported.

Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Jan 4th, 2010, 02:38am

What is a smiley?
laugh

Thanks for the continuing advice Richard. I got in a right mess with this bb because it decided that the laugh in my "SET" listing was a smiley, so I thought that you had kindly "commented" on my listing (and were laughing at some of the bizarre entries) so I deleted them from my PATH. What a mess up I made!. Ah well.

This is still an awkward problem for me, because I have already done the "experimentt" you suggest: I also run both applications on a Core-2-Duo machine (Win XP Home) which still uses the MS default settings ({My Computer}{Properties}{Advanced}{Environment Variables} gives...
:
User variables for Grahame
TEMP C:\Documents and Settings\Grahame\Local Settings etc.
:
System Variables
TEMP C:\WINDOWS\TEMP
:
(TMP matches) just as you like to see it.

As I said before, I do not use removable drives (Duh?) and the only files in the "hung" BB4W sub-directories are LIB files. It is the entire sub-directory which is not deleted upon Exit. Neither programme has any open files at ON CLOSE time, and I always put CLOSE# before the end of any PROC which W/R files, and PROC_exitsprites (as appropriate) in my PROC_cleanup.

What I find most infuriating is that they exit cleanly if I do an [Exit][Run][Exit][Run] series of tests. It is so far only after they have been running for over a day (sometimes it is a week) that they fail during Exit. I don't know how to cope with seemingly irrational behaviour, never could.

P.S. There is an important difference between "making a root directory your TEMP directory" [BAD IDEA] and "making a \TEMP directory the specified TEMP directory."

Thanks again, anyway.
--Grahame
Re: *.exe does not exit cleanly.
Post by Michael Hutton on Jan 4th, 2010, 02:55am

Quote:
Neither programme has any open files at ON CLOSE time


But the ON CLOSE is an interrupt. How can you be sure there is not an open file when the user clicks on the close button of a window? Surely, you must explicitly test for any open files or use CLOSE#0 in the cleanup routine. You can never assume that the interrupt is not happening when a file is open. The CLOSE#0 in the PROC doing the w/r might be itself interrupted and the CLOSE#0 statement never actually processed.

Michael
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Jan 4th, 2010, 03:29am

I apologise, bad copy/paste "typo" in the preceding. Absent mind and tired eyes, mea culpa. "CLOSE #" should have been "CLOSE #0".
--G
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Jan 4th, 2010, 03:47am

Thanks for the input Michael, good to have many minds on my side. I do know that coping with interrupts is what Windows programming is really all about, and I'm still learning I am sure.

In these cases, I have not put an explicit test for " if file open then close" because the programmes could not run at all if the PROC were interrupted (the R/W all happens at the beginning, before the real action starts, they are just "config" files). So it would do no harm to put that in PROC_cleanup anyway, I think, just to be triple sure?

This is part of MAIN in one of the prog.s, does this look right or wrong? ....
:
ON ERROR ON ERROR OFF :error%=TRUE
ON CLOSE Quit%=TRUE :RETURN
:
:
WHILE ((found% = TRUE) AND (error%=FALSE) AND (Quit%=FALSE))
PROC_IRQ_poll
ENDWHILE
PROC_cleanup
QUIT
REM====================

The file(s) which is opened in the programme is permanently located in @dir$ (until it is over-written by a re-Run), so it does not go in the \TEMP\BBCxyz sub-directory (and I have confirmed that during run-time) yet it is the \TEMP\BBCxyz directory (and its @lib$ xyzLIB.BBC crunched contents) which are not deleted.
Do you think there could be some "cross-wires" operating here? I cannot figure that one out.

--Grahame
Re: *.exe does not exit cleanly.
Post by Michael Hutton on Jan 4th, 2010, 05:01am

Two things come to mind, although they may not be relevant:

First, using @dir$ to store a temporary file which your program creates can lead to failure because the @dir$ may not be accessible to the current user. You should use @tmp$ which is guaranteed to be writable. Vista, especially, is particularly strict about this. But I don't think this is the problem you are encountering.

Second, as Richard said in the second post the BB4W exe creates a temporary @lib$ folder to extract all the embedded files (LIBS, DLLS etc) which to store them. On exit the exe is trying to delete these files, but I am assuming that your program is still using (or rather has a reference to it) when the exe is trying to exit. Are you sure you has used SYS "FreeLibrary" to free up the library? Does PROC_IRQ_poll create any additional references to a DLL you haven't specified in your main program?

Also, lets us say an error does occur. The ON ERROR interrupt takes the BB4W interpreter back to the next statement after ON ERROR, which is ON ERROR OFF then will set Error% = TRUE, the trouble is that *if* the PROC_IRQ_poll is actually trying to do something when the error occured - has an open thread to an internet connection, or whatever, it hasn't had a chance to do it's own cleanup, it was interrupted. It might still be using the DLL which is the problem and hence the exe can't delete those files...

I think this is a good example of when to use LOCAL ERROR handling and 'cascaded' error handling, where each PROC/FN does it's own cleanup if an error is detected... there is nothing wrong with having multiple cleanup routines.

I am not sure if I have really helped as I am not sure if I have expressed myself very succinctly!! Would it be possible to know what files exactly are left behind? I am sure this would give us the answer.

<afterthought> Another possibility is that you have changed @lib$ within your program, but I think this is *very* unlikely and is to be avoided.... you wouldn't/can't do it without trying to, so unless you have expressly prgrammed this, it is not an issue.

I hope this helps somewhat... undecided
Re: *.exe does not exit cleanly.
Post by admin on Jan 4th, 2010, 09:33am

Quote:
does this look right or wrong?

I wouldn't go as far as to say it's 'wrong', but I would prefer (unless there's some specific reason why it's not acceptable) the more straightforward:

Code:
ON ERROR ON ERROR OFF : PROC_cleanup : QUIT
ON CLOSE PROC_cleanup : QUIT
:
:
WHILE (found% = TRUE) 
  PROC_IRQ_poll
ENDWHILE 
PROC_cleanup
QUIT 

The less code you execute after an error has occurred the better, as far as I'm concerned. In your original version you relied upon executing quite a lot of code (including the code which I presume exists between the two lines you've shown with a colon) before actually quitting. What's more, that code was executed in an ON ERROR OFF condition so should a second error occur it wouldn't be trapped.

Quote:
the only files in the "hung" BB4W sub-directories are LIB files.

There's an element of ambiguity in the term 'LIB files'. Do you mean BASIC files loaded by INSTALL, or DLL (etc.) files loaded by SYS "LoadLibrary" (or both)?

Quote:
It is the entire sub-directory which is not deleted upon Exit.

That's not particularly significant, because BB4W simply deletes the entire folder, not the individual files. However, as Michael says, you should certainly check that nowhere in your program are you writing to the variable @lib$ (or making it LOCAL, or a formal parameter, or anything like that). @lib$ must be treated by your program as a constant, but since BBC BASIC doesn't have true constants it can't detect you breaking the rule.

Quote:
It is so far only after they have been running for over a day (sometimes it is a week) that they fail during Exit.

Whenever a problem seems to get worse the longer your program runs you should suspect a resource leak. If you've not already done so, monitor your program using Task Manager and check that none of the object counts (particularly Handles, USER Objects and GDI Objects) are gradually increasing.

Another thing I'd consider, when something fails only after some time, is the possible involvement of a virus scanner. Make sure there are no background scans taking place which could lock the files in your temporary directory.

Richard.
Re: *.exe does not exit cleanly.
Post by 19Grumpah42 on Jan 5th, 2010, 01:46am

That's very instructional Michael, thanks.

I am not [yet!] creating files within a programme, so I note your ref. to @temp$ (new to me) for further use. The data files used by these programmes are all created elsewhere, they are just ASCII configuration files.

As I had mentioned earlier in the thread, the only files I am finding in the abandoned "temp" directories are BB4W *LIB.BBC files. Thus far I am using no .DLL files (don't really know how to, so I won't play with that!), so that is not the problem. Richard says that SYS "FreeLibrary" would not help for only .BBC libraries anyway, and he certainly knows.

I take firm note of your recommendation to use ON ERROR LOCAL. I wrote both these programmes over 2 months ago and because they have run (maybe not exit!) 24/7 ever since, I did not fuss with error trapping. Indeed my subsequent projects (what a grand word!) have blossomed with ON ERROR LOCALs (a sure testament to my poor programming skills ?) so I am appreciating their value in tricky situations. I will revisit both these app.s and take a closer look at the possibility of "silent errors". As a first stab, I need to ensure that not just PROC_IRQ_poll but all those called by it - as required by current flag(s) status - all have internal, customized ERROR trapping.

[I seem to be ending up with slightly more code to handle interrupts and errors than to actually do the job. Perhaps I just needed to get my head around that idea, start to design the interrupt and error trapping scheme *before* I design the working code].cool

This starts to sound like a productive approach, thanks. smiley

And I promise never to change @lib$ !

--Grahame