Cascaded ON CLOSE
Post by Michael Hutton on Jun 19th, 2010, 03:55am
Richard,
I was wondering what your thoughts would be to add cascaded ON CLOSE handling to BB4W?
It could be a useful feature for library writers who would like to cleanup any objects their library creates without having the user to clean them in the main program.
For instance:
Code:
REM Cascaded ON CLOSE Handling?
ON CLOSE PROCCleanup:END
PROC_INIT_MYLIB
REPEAT
WAIT 1
UNTIL FALSE
END
DEFPROCCleanup
PRINT "Cleaning!"
ENDPROC
DEFPROC_INIT_MYLIB
PRIVATE MYLIBVARS{}
PROCPRIVATE(MYLIBVARS{})
MYLIBVARS.close% = !392
ON CLOSE PROC_MYLIB_Cleanup : END
REM Create my objects here and add their handles to the MYLIBVARS.handles%()
ENDPROC
DEF PROC_MYLIB_Cleanup
PRINT "Cleaning the Library"
PRIVATE MYLIBVARS{}
PROCPRIVATE(MYLIBVARS{})
REM Cleanup the objects
LOCAL I%
FOR I% = 0 TO 10
IF MYLIBVARS.handle%(I%) THEN SYS"DeleteObject", MYLIBVARS.handle%(I%)
NEXT
!392 = MYLIBVARS.close%
WM_CLOSE = &10
SYS "SendMessage", @hwnd%, WM_CLOSE, 0,0
ENDPROC
DEF PROCPRIVATE(RETURN t{})
PRIVATE p{}
DIM p{close%. handle%(10)}
!(^t{}+0) = !(^p{}+0)
!(^t{}+4) = !(^p{}+4)
ENDPROC
Although this works, it is not guaranteed to work if an ON CLOSE statement is defined after initialising the library...
An ON CLOSE LOCAL is obviously not appropriate for the library.
I think it could be a useful feature so that any ON CLOSE statement (not ON CLOSE LOCAL) adds a pointer to a linked list of pointers for code to be processed when a WM_CLOSE message is received by a window.
It may be a feature which would help with library encapsulation?
Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 19th, 2010, 10:11am
on Jun 19th, 2010, 03:55am, Michael Hutton wrote:I was wondering what your thoughts would be to add cascaded ON CLOSE handling to BB4W? |
|
Well, as you know, I am treating the BBC BASIC for Windows interpreter as effectively frozen. So the question to ask is not what changes might be made to BB4W, but what workarounds are available without needing to make any changes.
It seems to me that the approach you've adopted is the right way to proceed. However, rather than restoring the ON CLOSE vector and sending another WM_CLOSE message, as you do, I would have been inclined to jump directly to the original handler, as below. It's easier and quicker.
Quote:Although this works, it is not guaranteed to work if an ON CLOSE statement is defined after initialising the library... |
|
I don't see that as an issue; the library simply has to specify that its initialisation function must be called after any ON CLOSE. Any theoretical extension to BB4W would need to impose a similar constraint, because automatically cascading ON CLOSE wouldn't be compatible with existing programs (and imagine what might happen if the ON CLOSE was in the main loop, something I quite often do!). By default a later ON CLOSE would have to replace the existing vector as now.
Code: REM Cascaded ON CLOSE Handling?
ON CLOSE PROCCleanup:END
PROC_INIT_MYLIB
REPEAT
WAIT 1
UNTIL FALSE
END
DEFPROCCleanup
PRINT "Cleaning!"
ENDPROC
DEFPROC_INIT_MYLIB
PRIVATE MYLIBVARS{}
PROCPRIVATE(MYLIBVARS{})
MYLIBVARS.close% = !392
ON CLOSE PROC_MYLIB_Cleanup : END
REM Create my objects here and add their handles to the MYLIBVARS.handles%()
ENDPROC
DEF PROC_MYLIB_Cleanup
PRINT "Cleaning the Library"
PRIVATE MYLIBVARS{}
PROCPRIVATE(MYLIBVARS{})
REM Cleanup the objects
LOCAL I%
FOR I% = 0 TO 10
IF MYLIBVARS.handle%(I%) THEN SYS"DeleteObject", MYLIBVARS.handle%(I%)
NEXT
PROC(^MYLIBVARS.close%)
ENDPROC
DEF PROCPRIVATE(RETURN t{})
PRIVATE p{}
DIM p{close%,handle%(10)}
!(^t{}+0) = !(^p{}+0)
!(^t{}+4) = !(^p{}+4)
ENDPROC
Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 20th, 2010, 08:09am
weird, I thought I had replied earlier on.... anyway:
I see your point about the problem if the ON CLOSE statement is in a loop.
I'll develop my thoughts a bit further, maybe a CLEANUPLIB could be fashioned to keep track of objects created by the user. For example, if someone creates an object
PROC_CLEANUPLIB_addObject( type )
and then the CLEANUPLIB could auto clean at any ON CLOSE event....
Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 20th, 2010, 09:36am
on Jun 20th, 2010, 08:09am, Michael Hutton wrote:weird, I thought I had replied earlier on |
|
I've never known Conforums lose a post. Almost certainly what you did was click Preview but never click Post. You don't get any warning if you navigate away from the page without posting your message (the Wiki is better in that respect).
You didn't comment on my suggested modification to your code of jumping directly to the old ON CLOSE handler, rather than messily restoring the vector and sending another WM_CLOSE message. I think that's a very worthwhile improvement.
Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 22nd, 2010, 12:33am
on Jun 20th, 2010, 09:36am, Richard Russell wrote: Almost certainly what you did was click Preview but never click Post. |
|
Yes, I was pretty sure that is what I did. Frustrating.
Quote:You didn't comment on my suggested modification to your code of jumping directly to the old ON CLOSE handler, rather than messily restoring the vector and sending another WM_CLOSE message. I think that's a very worthwhile improvement. |
|
Although sending the message is clumsier, it could be safer. For example, what happens if, the main ON CLOSE statement ends with a RETURN (unlikely, but possible):
Code:
jumping with the PROC(^MYLIBVARS.close%) will give a "not in a subroutine" error, whereas sending a message to the window will produce the expected behaviour.
Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 22nd, 2010, 08:48am
on Jun 22nd, 2010, 12:33am, Michael Hutton wrote:jumping with the PROC(^MYLIBVARS.close%) will give a "not in a subroutine" error, whereas sending a message to the window will produce the expected behaviour. |
|
ON CLOSE RETURN is used to ignore the Close button (and Alt-F4) entirely, the idea being that the program will continue execution as if nothing happened.
In that case your suggestion won't produce "the expected behaviour". If the user presses Close or Alt-F4 it won't be ignored, instead your library will perform its cleanup but the program will keep running! The result is likely to be a failure when the program subsequently attempts to use one of your library functions.
So, in fact, my code is actually better than yours, precisely because it results in a 'Not in a subroutine' error! It alerts the user to the fact that he's used an ON CLOSE in a way that is not compatible with your 'cascaded' handling.
Nice try, but no cigar!
Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 22nd, 2010, 10:08am
I did think of that, as you say, using the RETURN statement with an ON CLOSE would invoke the cleanup of the Library objects potentially creating an error when they are encountered again.
The upshot is to warn the user of the Library not to use a RETURN with the ON CLOSE statement when using the library.
However, I don't see another explicit advantage of jumping straight to the ON CLOSE vectors code rather than sending a WM_CLOSE message to the window, except maybe the length of code. Isn't it six of one and half a dozen of the other?
Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 22nd, 2010, 11:40am
on Jun 22nd, 2010, 10:08am, Michael Hutton wrote:However, I don't see another explicit advantage of jumping straight to the ON CLOSE vectors code rather than sending a WM_CLOSE message to the window, except maybe the length of code. Isn't it six of one and half a dozen of the other? |
|
Well, I gave you one good reason why jumping to the vector is better: it will result in a 'Not in a subroutine' error if the user has an ON CLOSE RETURN. Issuing an error must be better than relying on the user noticing a warning in the library documentation!
Another thing is that your code has a potential weakness. After you send the WM_CLOSE message, you immediately exit from PROC_MYLIB_Cleanup, and then you execute an END statement:
Code: ON CLOSE PROC_MYLIB_Cleanup : END
END disables ON CLOSE altogether, so it's possible the user's handler may never be called, if the END gets executed before the WM_CLOSE message has worked its way through the system.
Admittedly this is an unlikely scenario, because the chances are that the user's ON CLOSE will have been activated (as a result of your WM_CLOSE message) before the interpreter reaches the END statement - but this could easily change if I modified BB4W. For example, one thing I've considered more than once is testing for an 'interrupt' not every statement but (say) every five statements, to reduce the overhead. If I made that change your code would be guaranteed to fail.
Of course you could overcome this problem by following the SYS "SendMessage" with an 'infinite loop', rather than exiting via the ENDPROC to the END statement:
Code: SYS "SendMessage", @hwnd%, WM_CLOSE, 0,0
REPEAT : WAIT 0 : UNTIL FALSE
But why use a complicated solution when a simpler, faster, more straightforward one is available?
Richard.
Re: Cascaded ON CLOSE
Post by admin on Sep 24th, 2010, 4:32pm
It's taken a while, but I've finally worked out how to do this elegantly. The technique is described in this Wiki article:
http://bb4w.wikispaces.com/Cascaded+ON+CLOSE+handling
Here's the code of a demo program:
Code: PRINT "Click Close to test cascaded ON CLOSE handling"
PRINT "Press Escape to test cascaded ON ERROR handling"
PRINT
ON ERROR PRINT "Error handled in Main: " REPORT$ : END
ON CLOSE PRINT "Close handled in Main" : END
PROC0
END
DEF PROC0
LOCAL dummy
ON CLOSE LOCAL ERROR 111, "Close"
ON ERROR LOCAL RESTORE LOCAL \
\ IF ERR = 111 PRINT "Close handled in PROC0" : PROCclose ELSE \
\ PRINT "Error handled in PROC0" : ERROR ERR,REPORT$
dummy = FN1
ENDPROC
DEF FN1
ON CLOSE LOCAL ERROR 111, "Close"
ON ERROR LOCAL RESTORE LOCAL \
\ IF ERR = 111 PRINT "Close handled in FN1" : PROCclose ELSE \
\ PRINT "Error handled in FN1" : ERROR ERR,REPORT$
PROC2
= 1
DEF PROC2
ON CLOSE LOCAL ERROR 111, "Close"
ON ERROR LOCAL RESTORE LOCAL \
\ IF ERR = 111 PRINT "Close handled in PROC2" : PROCclose ELSE \
\ PRINT "Error handled in PROC2" : ERROR ERR,REPORT$
REPEAT
WAIT 1
UNTIL FALSE
ENDPROC
DEF PROCclose
IF !392 PROC(^@%+392) ELSE QUIT
ENDPROC
Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Oct 8th, 2010, 12:37pm
I think I understood the code but surely this is still a ON CLOSE LOCAL solution. I am not sure I can see how to incorporate this code into a library which would take care of it's own clean up of objects it creates unless you ask the programmer to put their main program in a PROC (in this case PROC2) which the library calls. Hence if an ON CLOSE is detected in PROC2 the cascading clean up is performed.
As soon as you exit the PROC2 all the ON CLOSE LOCALs are 'forgotten'. Or yet again have I missed the point.
Michael
Re: Cascaded ON CLOSE
Post by admin on Oct 8th, 2010, 3:17pm
on Oct 8th, 2010, 12:37pm, Michael Hutton wrote:I am not sure I can see how to incorporate this code into a library which would take care of it's own clean up of objects it creates |
|
Sorry, I'm being a bit slow today (it doesn't help having a nasty cough for the last 7 weeks that won't go away). You'll have to explain what you mean in more detail.
As an experiment I've transferred PROC2 into a library, to simulate the situation you describe. There's nothing 'shared' between the library and the main prog as far as I can see (I've chosen a different error code and called PROCclose something different) and the main prog doesn't have to know that any 'local' CLOSE handling happens in the library. But it all seems to work as it did before.
Quote:As soon as you exit the PROC2 all the ON CLOSE LOCALs are 'forgotten'. |
|
In what sense "forgotten"? If I move the WAIT code out of PROC2 and instead put it in FN1 after the PROC2 statement, a CLOSE still seems to be trapped at each 'level'.
Richard.
Don't put an apostrophe in ITS unless you mean IT IS: http://www.youtube.com/watch?v=Vc2aSz9Ficw
Re: Cascaded ON CLOSE
Post by admin on Oct 8th, 2010, 4:58pm
on Oct 8th, 2010, 12:37pm, Michael Hutton wrote:I think I understood the code but surely this is still a ON CLOSE LOCAL solution. |
|
The title of this thread is Cascaded ON CLOSE so of course it's an ON CLOSE LOCAL solution, because that's what you need in that case! In the same way, the solution to the Cascaded ON ERROR problem is an ON ERROR LOCAL solution.
If you're asking about a situation where a library creates a global object, which then needs to be cleared up when the program is eventually closed, that's got nothing to do with cascaded ON CLOSE or ON ERROR handling, and is off-topic for this thread.
The solution to that issue is for the library to provide its own cleanup procedure that the user must call from the main program in his global cleanup code, typically activated from both his ON ERROR and ON CLOSE handlers. Many libraries have just that:
COMLIB has PROC_comexit
GDIPLIB has PROC_gdipexit
MDILIB has PROC_exitmdi
SOCKLIB has PROC_exitsockets
etc.
Richard.