“Unbreakable” Objects Revisited – Final Part V

Version: 1.50.00 - last update: Tuesday, October 13, 2009, 18:40: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

As I expected, part IV wasn’t the last part of this almost “never ending story” :-) Jim Nelson, lead developer of VFPX’s PEM Editor, checked my “cancelling a QueryUnload” code and wrote this message (follow the link and read the whole thread if you’re interested in, or stay here and read the quintessence).

What I missed to explain last time…

Do you know this situation? You are trying to explain some complex stuff to your audience and nobody grasps what you mean? This is, because you’ve missed to explain something basically, you’d better done before diving deep into constrained details!

Let’s talk about the details:

Any form instantiated using DO FORM (I’m going to call them DOFORM from now on) that is CANCELling the QueryUnload event keeps all following FORMS stay alive, too. With “Following FORMS” I mean all FORMS in _SCREEN.FORM[x] collection with a lower index value than the cancelling DOFORM. If you “did” your FORM like this: DO MyForm NAME (SYS(2015)) LINKED it will be released when typing a CLEAR ALL + RELEASE ALL. All FORM instances created using CREATEOBJECT() will be destroyed in any event. Thus, typing in a CLEAR ALL + RELEASE ALL in the command window will destroy all of those DOFORMS and FORMS!

Two things you should remember

1st: CANCELling a form’s QueryUnload() that was caused by a global CLEAR ALL isn’t scoped to that single form instance but applies to all “following” forms in the _SCREEN.Forms[n] collection!
2nd: VFP’s destruction sequence of FORM instances isn’t at random, but loops through the _SCREEN.FORMS[n] collection as if one wrote:
FOR lnLoop = _SCREEN.FormCount TO 1 STEP -1
    _SCREEN.Forms[m.lnLoop].Release()
NEXT

The first FORM or DOFORM that got instantiated will be on top of the _SCREEN.FORMS[n] collection – always! When VFP’s IDE starts up we have to DO a FORM with a CANCEL in its QueryUnload (of course). This first form always stays at top-position in VFP’s _SCREEN.Forms[n] collection. In other words: When this first instance of a DOFORM (stored in _SCREEN.Forms[_Screen.FormCount]) CANCELs a QueryUnload event, all other DOFORMS stay alive, too! Tools like VFP’s Toolbox and any Class-Browser instance are protected against all CLEAR ALLs then.  Best of it: We don’t have to alter any tool source code but to create some clever enhancements within a Global Resource Manager. (My next thread will be about this GRMgr!)

More Findings

Today, after some days went by, giving me enough time to explore my initial findings more thoroughly, I must admit that just CANCELling the QueryUnload of an “unLINKED” DOFORM isn’t without additional side-effects! After all observations and studies I’ve made, I can tell you so far that cancelling a CLEAR ALL in VFP’s IDE cancels the pending process steps completely! We have to read VFP’s help carefully:
<< CLEAR ALL Releases from memory all variables and arrays and the definitions of all user-defined menu bars, menus, and windows. CLEAR ALL also closes any tables, including all associated index, format and memo files, and selects work area 1. CLEAR ALL also releases from memory all external shared library functions registered with DECLARE - DLL.
CLEAR ALL does not release system variables, and does not clear the compiled program buffer. Use CLEAR PROGRAM to clear the compiled program buffer.
Issuing CLEAR ALL within an event or method for an active control or object generates a Visual FoxPro error message. An object type variable cannot be released from memory when its associated control or object is active.
>>

CLEAR ALL can be seen as a sequence of actions VFP performs internally. I found out, that releasing all forms in the _SCREEN.FORMS[n] collection is the first step VFP takes! Thus, CANCELLING the sequence just inside the topmost DOFORM lets VFP jump out of the sequence immediately! This means that neither all pending DOFORMS are released, nor any public memory gets dropped, all tables stay open, all declared DLLs remain declared and no menu is released as well! Only some form-class instances are gone.

If one tends to settle on any negative point of view, he/she/it could say >>Your CANCELling isn’t useful!<< IMHO, this isn’t true at all! I like the idea to gain full control over critical direct-commands like CLEAR ALL. Thus, I created a routine, that simulates a CLEAR ALL. But in contrast to VFP’s unspecific original CLEAR ALL, I can spice up my own, home-grown one with a lot of useful extensions! For instance, if I like to “clear all protect” some memory vars, why not create an exclusion list? Why not create such a list for every group I’m touching within this routine?

I like that!

*\\ This method executes all steps that are taken by a regular CLEAR ALL, too.
*\\ Start sequence of CLEAR ALL related commands
*\\	all variables and arrays 
RELEASE ALL EXTENDED
*// [BS]ToDo: Create an exclusion list for variables that 
*//	should stay in memory (protected memory)
*//
*\\	user-defined menus
DEACTIVATE POPUP ALL
CLEAR POPUPS
*//
*\\	user-defined menu bars
DEACTIVATE MENU ALL
CLEAR MENUS
RELEASE MENUS
*//
*\\	user-defined windows
*\\ If we wouldn't manipulate (increase) VFP's form reference count, 
*\\	CLEAR WINDOWS would break our form!! CLEAR WINDOWS is an absolute
*\\ killer-command (on windows and forms)! See THISFROM.LOAD() for more
*** CLEAR WINDOWS
*// It seems that CLEAR WINDOWS does "too much" internally - no restarting
*// seems to be possible (at least PEM Editor 4.10 doesn't)
*// IN ADDITION Recursion appears!!
*//
*\\ Next loop does a "softer" RELEASE()
*** [BS]ToDo: We still have to implement another protection algorithm
***  	  for forms and other objects that should be made "unbreakable"...
LOCAL lnLoop AS Integer
*\\ protect our own form from being released!
THIS.EnableRelease = .F.
*//
FOR lnLoop = 1 TO _SCREEN.FormCount
   TRY
	*\\ this works for toolbars, forms and windows (<< old fashioned DEFINEd ones)
		_SCREEN.Forms[1].Release()
	CATCH
		TRY
			? "Error releasing form/window/toolbar - name='" + _SCREEN.Forms[1].name + "'"
		CATCH
		ENDTRY
	ENDTRY
NEXT
*//
*\\ any tables, including all associated index, format and memo files
IF TXNLEVEL() = 0
	*\\ Although still listed and described in help file, the next three
	*\\	commands throw compiler & runtime errors!
	*CLOSE TABLES ALL
	*CLOSE INDEXES 
	*CLOSE FORMAT
	*\\ very interesting: next line works! As it seems CLOSE TABLES ALL
	*\\		only is allowed in interactive mode! (This info is missing in VFP's help!
	_SHELL = "CLOSE TABLES ALL"
	*// [BS]ToDo: Create an exclusion list for tables that should stay open!
	*//
ENDIF
*//
*\\ shared library functions registered with DECLARE - DLL.
CLEAR DLLS
*// [BS]ToDo: Create an exclusion list for DECLAREd API calls that should stay!

The listing is part of my new Global Resource Manager. Therefore, some of my comments above needs to be explained. While test-driving my “CLEAR ALL substitution” it turned out that the pretty seldom used CLEAR WINDOWS command is a real killer! My so carefully protected “unbreakable window” suddenly was gone after I typed a CLEAR WINDOWS into the command window during a test-run! Damn! What other VFP keywords else could do any harm to my “work”? Even though CLEAR WINDOWS is not often used (at least not by me), it is still there to kill all my “Cancelling-QueryUnload”-protected forms at user’s will. After a CLEAR WINDOWS command VFP doesn’t call DOFORM’s QueryUnloads first, but immediately runs their Destroys!

How to protect against such a nuke-attack?

Fortunately, I recalled an article from Christof Wollenhaupt that I read not so long ago. You can read it here (highly recommended! Go to 2nd topic "Object reference counters in VFP"). From there I downloaded the little ObjRef.FLL which has two functions: IncrementRef(object) and DecrementRef(object).

To get “full break-down protection” I now add an additional (self-)reference to my unbreakable DOFORMS on init (using IncrementRef(THISFORM)) and decrement VFP’s internal reference counter on form’s release and queryunload (using DecrementRef(THISFORM)). This was the final break through! Thank you, Christof! Now, even a CLEAR WINDOWS cannot drop my unbreakable forms any longer!

Conclusion

It cannot be done totally without help of C++, but almost. There are some very useful things realizable now. My Global Resource Manager will utilize what I found here, another tool that couldn’t exist without the technique I described so far, is my FoxEditor. Both tools behave as if they were originally made by Microsoft to be a native part of VFP’s IDE like the project manager.


Previous ChapterNeat Solutions Home (TOC)Next Chapter