“Unbreakable” Objects Revisited - Final(?) Part IV

Version: 1.01.00 - last update: Saturday, September 26, 2009, 17:00:00

Previous ChapterNeat Solutions Home (TOC)Next Chapter


This is all about how to create a CLEAR ALL resistant class to speed-up things while developing.

Intro

In part three of this multi-part blog we finished our “unbreakable” form. Well, at least I thought so, now be prepared to learn something new!

In this part (IV) finally I will show you how to build a VFP form that really isn’t breakable! I am pretty sure, I am the first “VFP guy” who found out how to do that! I was very exited when I stumbled across the solution by accident. But first, let’s resume why I didn’t give up looking for the perfect way.

The solution I presented (long time ago :-) was based on storing a restart command to VFP’s _SHELL system variable. After destruction of my “unbreakable” form (and all other object hanging around) VFP examines the content of its _SHELL variable before returning control to the developer (Command window). If _SHELL is not empty, VFP executes the command stored in there and clears _SHELL after execution. Well, so far so good. What really sucks is that VFP’s _SHELL variable is a public resource! You never can be sure that your form is the only/last one which writes its “farewell letter” to the _SHELL.

Lately, I downloaded the cool new PEM-Editor from the VFPX site. It is a great tool and the guys who made it are using some restart technique after a CLEAR ALL like mine. There are other neat development tools available on VFPX, some of them are also utilizing _SHELL. What if you want to use two or more of them at the same time? Type CLEAR ALL and only the luckiest will survive - what a shame!

After a long pause (some of you may have noticed my blogging inactivity) I resumed working on some really cool tools I will make available on VFPX sooner or later. Most of my time I spend on a new source code editor for VFP. I don’t want to go into detail here only show you a screenshot of a development session.

FoxEditorDevEnv

Each editor session form is able to host up to 4 VFP editor sessions (editing the same code!) in a split screen view. Another cool thing is, that an editor sessions can be run as a dockable, a MDI, or a Top-Level form! There are millions of other smart features waiting to get implemented ;-) One thing still needs to be mentioned here: the editor areas are regular VFP editor windows. My “FoxEditorSession”- form only hosts these normal VFP editor windows (well and extends them). Typing a CLEAR ALL in the command window would have caused my running host form(s) to be destroyed. Even if I would have utilized my restarting algorithm described in part III, there would have been too many undesired side effects! Imagine you are working with, let’s say 10 open editor sessions spread all over your multi monitor desktop, all with dirty/unsaved text buffers and you have to “clear all” or “release all” – what a mess! Destroying the host forms will close the child editor window inside, too (naturally). Each VFP editor session will ask you then, if you want to store the unsaved changes! A regular VFP editor window instead just stays on screen without getting touched by any other internal memory reorganisations. That was exactly the behaviour I was searching for! I wanted to be able to create forms that behave just like VFP’s editor session windows but with a regular VFP memory handle (some m.oFormRef-variable). After all, it seemed crucial to me to create such a behaviour first, before implementing any other editor features on my list, because without a strong self-protection of the hosting form against a CLEAR ALL, all other features would have been nothing more than a bunch of futile gizmos without a comfortable home.

Yesterday I spent some hours investigating VFP-Form’s destruction behaviour. I must admit that I was doing some very strange Frankenstein experiments :-) My intention was to create a “controllable” dangling reference inside my host form, that would prevent that form instance from being closed by a CLEAR ALL. What a show! I never had so much complete VFP-crashes like yesterday evening! :-) It turned out that it was possible to go that way, but it also turned out that it was the wrong direction. I was on the verge of giving up, when I started some final test runs. I had added some new code blocks to my test form’s QueryUnload event. Unfortunately, every code block had some errors in it (replicated by copy&paste inheritance:-). I knew from countless runs before, that using the debugger’s “Fix” functionality at that point was no good idea, coz it would end up in some C5 crash caused by my handmade dangling references. On the other side I had no good mind to step through all errors that were still waiting to pop up. The buggy QueryUnload code got executed because I had typed a CLEAR ALL in the command window to initiate the destruction sequence I wanted to manipulate and monitor. At that point yesterday evening, I typed CANCEL into the command window to abort the run. And there it was: My form was still up and running/responding! I must admit, the fact that I noticed it was more or less a happenstance. Anyway, yesterday evening I was focused on all kinds of form-destruction relevant impacts and their effects. After a short time wondering why my form did not show up in the form designer but still was running, it leaped into my mind that my cancelling the form’s QueryUnload event code execution could have caused that effect.

Everybody knows, that putting a NODEFAULT into a QueryUnload prevents the form from being closed. But only if the user clicks the form’s close box or presses CTRL+F4. Some code like this could be used in your form’s QueryUnload event code:

IF MESSAGEBOX("Do you want to quit form?",;
		4+32+256,"CLEAR ALL detected!") = 7 && No
   NODEFAULT
   RETURN
ENDIF

Well, after thinking about it for a while, I gave it a try and replaced the QueryUnload event code with the following:

IF NOT THIS.ReleaseType = 2 && Close VFP
   *\\ DO NOT REMOVE CANCEL STATEMENT! 
   *\\ This is the ÜberCool trick to
*\\ make an IDE form instance
*\\ REALLY UNBREAKABLE !!!!!
CANCEL *// ENDIF

I ran my form, I typed a CLEAR ALL – my form was still there! Even its variable was accessible. I explicitly removed the variable (using something like RELEASE oMyForm in the command window) – my form was still there. Okay, latter is a normal/documented behaviour for SCX-based forms that were instantiated using something like “DO FORM myForm NAME xyz” without(!!) the LINKED keyword.

When and where does this übercool trick work?

  • You can only protect SCX-based FORM instances running within VFP’s development environment (a CLEAR ALL doesn’t make much sense in the runtime environment, right? :-)
  • You must use a SCX-based form instantiated with DO FORM, because those instances can exist without a memory reference (they are always accessible using _SCREEN.FORMS[x]).
  • You must NOT use the LINKED statement, which binds the form instance to the variable specified in NAME <varname>. CLEAR ALL destroys that variable causing an immediate destruction of your form instance (your “special” QueryUnload Event code never executes!)
  • The same is true for class based forms instantiated like so: loForm = NEWOBJECT(“MyFormClass”, “MyFormClassLibrary”).
  • BTW: The close box of such a form doesn’t work any longer, too. I already mentioned that. Best you can do is, to disable it by setting THISFORM.Closable = .F.
    Okay, you can do something else :-) You can add the following code to your QueryUnload
*\\ Code block "borrowed" from PEM Editor's 
*\\    sources >>> RestartAfterClearAll.prg
*\\ SYSMETRIC(33) := Width of a button in a half-caption window's caption or title bar
*\\ SYSMETRIC(28) := Window controls width
*\\ [BS]: this is true for dockable forms and normal child forms "IN SCREEN"
#DEFINE CloseButtonWidth ;
SYSMETRIC( IIF(VERSION(5) >= 900 AND THISFORM.DOCKABLE # 0, 33, 28)) *\\ [BS]: added calculation to reflect different [X]-button width in TopLevel Forms LOCAL lnTLF AS Integer lnTLF = IIF(THISFORM.ShowWindow = 2,13,0) IF NOT THIS.ReleaseType = 2 && Close VFP *\\ Mouse NOT over Close 'X' Button IF NOT BETWEEN ( MCOL(THISFORM.NAME, 3) ,; THISFORM.WIDTH - CloseButtonWidth – lnTLF ,; THISFORM.WIDTH ) ; AND ; MROW(THISFORM.NAME, 3) = -1 *\\ make CLEAR ALL RESISTENT CANCEL
*//
ENDIF ENDIF *\\ Maybe you like to ask the user IF NOT THIS.ReleaseType = 2 && Close VFP *\\ [BS]ToDo: Check if master editor session buffer is dirty! *\\ only in that case ask for user’s confirmation! IF MESSAGEBOX("Do you want to quit your session?",; 4+32+256,"Security Query") = 7 && No NODEFAULT RETURN ENDIF *// ENDIF

How to shut down such an unbreakable form?

Well there are many ways to do that. The easiest way is by using a special close button on your form. The click event of the button may look like this:

PROCEDURE .oBtnCloseForm.Click()
   THISFORM.TAG = "ALLOWED_TO_CLOSE"
   THISFORM.RELEASE()
ENDPROC

and your form’s release method could hold the following code fragment:

IF NOT ATC("ALLOWED_TO_CLOSE", THISFORM.TAG)
   = MESSAGEBOX("Form is in 'unbreakable' mode!",;
	0+64,"Cannot release form instance")
   NODEFAULT
   RETURN
ENDIF

Protecting your form’s RELEASE() method and/or TAG property prevents all attempts to shut down that form instance from outside!

What is so ÜberCool about that?

We do not need to access VFP’s _SHELL variable any longer! No more fighting around who’s the last _SHELL writer!
No more forms that disappear after a CLEAR ALL to reappear some seconds later (at different places:-)!

Now we all are able to create

VFP tools that LOOK AND BEHAVE as if they

were written in C++ by Microsoft’s VFP developers themselves!

Maybe you now understand why I was so exited yesterday evening! And if you are not yet, you are no thrilled VFP developer ;-))  I would like to hear from you all – I hope this trick gets spread quick and ASAP all over the VFP developer community (a hint/link to this blog’s thread would be appreciated).

Keep things rolling!

Some IMPORTANT things are still unsaid!


Previous ChapterNeat Solutions Home (TOC)Next Chapter