Showing posts with label Internal Affairs. Show all posts
Showing posts with label Internal Affairs. Show all posts

The Missing Type – VOID

Version: 1.00.00 - last update: Sunday, September 7, 2014 [created]

WINAPI & VFP Internals (Home)


Is it possible to really return a 'NOTHING' from a method?

Intro

FoxPro makes coding easy for us! We are allowed to name our methods Procedures or Functions interchangeably. The Fox don't care if we return a value from one of our Procedures, or if we omit it completely! The Fox gracefully adds his own Return .T. and, voilĂ , nothing can go wrong. What Lucky guys we are!

Some Short Mantras

The following "mantras" should remind you of some basics of almost any (non-functional) programming language.

A Function is a Function is a Function

A class method that acts as a function should be declared so (if PRG-based in VFP).

Function MyFunc(tcParameter As String) As Boolean
EndFunc

A Function must return at least on value. One should always code the return statement at the end of the Function, to remember novice VFP programmers of that fact!

Function MyFunc(tcParameter As String) As Boolean
	Return .T.
EndFunc
  • A Function must not return more than one value (which can be an array in VFP 9, too)!
  • A Function can signal internal malfunctions, or parameter errors (broken contracts), by returning an appropriate error value, or by raising an error!
  • A Function must never alter its object's state.
  • A Function obeying to the rules above can be said to be side effects free.

A Procedure is a Procedure is a Procedure

A method that acts as a Procedure must be declared so, if PRG-based.

Procedure MyProc(tcParameter As String) As Void
EndProc
  • A Procedure must never return any value. Thus, its return type is called VOID!
  • A Procedure is allowed to alter its object's state.
  • A Procedure can signal any internal malfunction, or parameter errors (broken contracts), only by raising an error!
  • A Procedure obeying to the rules above can be said to be well behaved.

What's Not Good About It?

There's nothing really wrong! If only FoxPro would not return a Boolean .T. value from methods we named Procedures in our code!

Instead of this odd behavior, wouldn't it be much cooler if FoxPro could really return nothing from a Procedure? That would have the consequence that any assignment of the return value of a procedure would raise an error, because one cannot add nothing to a variable.

Why Would We Like to Have That?

Maybe novice VFP programmers get tricked by the existence of the return value, wondering what the <.T.> return value is all about?

My personal favorite is that it always brings back my focus to the elemental rules described above! When I come to the point during coding, thinking about to return some value from a method I defined to be a Procedure before, then I'm instantly warned! It always seems to be a good thing to take a short break, or at least a deep breath, and then, start refactoring the formerly procedure-based concept!

Another real existing problem could also be solved. What if you need an additional value type? At the moment we have "U" for undefined, "X" for .Null. and a list of uppercase letters for all other real existing types. What, if we could make a property holding another virtual type of value, namely .Void. returning an empty type-letter <" "> when queried using VFP's Vartype() and Type() functions?

A VOIDED value (.VOID.) is an abstract concept denoting some
Item Without Content And Type

How to Engage The Concept?

The integration into our existing code base is very simple and looks like this:

Procedure MyProc(tcParameter As String) As Void
	Return This.Void
EndProc

The above code overrides the default return value of <.T.> of the MyProc() Procedure. What gets returned now is the content of an property called .void.
Now, we only have to add the above Return statement to the end of our procedures, that's all it takes!

What Happens if We Touch The Void?

Not much, unless we try to assign a voided value to another variable/property, or try to use it in calculations. Here come the DOs and DONTs:

DOs

We can query a property, or the return value using Vartype() and Type() like so:
VARTYPE(m.goReferenz.MyProc())
TYPE("m.goReferenz.MyProc()")
Which will give you a .Void. return value, a value of without type and content!

DONTs

We cannot assign the property, or return value, to another variable like so:
loRetVal = m.goReferenz.MyProc() 
which would throw an exception immediately!

What Gets Stored in The Void Property Actually?

Well, to be exact, in VFP .VOID. is a value represented by a corrupted value structure (structure with empty 'ev_type' member). To understand the last sentence we have to know how VFP handles values internally (using C-structures). The elements of the structure describing the value handled by the structure. Short data-types are stored directly within the structure, strings (stored somewhere else in the heap) are pointed to by a special member of the structure. There is more about the VFP C-API basics in VFP's dv_foxhelp.chm file. Try the following URL into your Explorer:

mk:@MSITStore:C:\Program%20Files\Microsoft%20Visual%20FoxPro%209\dv_foxhelp.chm::/html/ad9896fa-643e-48d3-a3b9-d738971e71a3.htm

the link above points to one of the API-chapters called "Parameters in External Libraries".

How to Get That Damage Done?

This was cumbersome to find out! I started some years ago with a little test form like shown in the screenshot below. Note, that there will be NO download for the SCX, shown below, because you can write it on your own after having read all this, and if that is still interesting for you afterwards.

My first little Void test

I found VFP's value checking flaw incidentally while experimenting with some specific VFP behavior. At that time, I was chasing for detailed .WriteExpression() and .ReadExpression() method behavior, when I incidentally called the Object.WriteExpression() method passing in an empty value, I encountered errors while trying to access the property subsequently!

I created the above demo form only to preserve my findings. After I had finished my exhaustive observations of VFP's .WriteExpression(), I came to some very interesting findings, I kept secret - until now…

Something to Suit my Needs

When I needed a new return value attribute in my framework, I called it contract broken. I restarted thinking about the possibilities how to signal the fact back to a caller, that she just broke the signature contract protecting the current message.

One part of any Function Contract covers the return value of the function. A warranted type, and a set of valid values is described in the contract there, as well as the values to be returned if an error occurred!

What could we do in cases where the returned value is of type Variant – and there are no restrictions on the returned value, too? A returned .Null. value could be valid just as well as any other kind of date. What's still missing is a memory-based equivalent of a BLANK field value in VFP.
Okay, you might argue, in our case it's time to raise an exception. Right you are, I must admit, this is a proven way out of there. But…

…signaling any "soft-error" stemming from a broken contract raising a VFP exception definitely is to much (IMHO)! User defined errors should be traded on another message transportation level. Exceptions are reserved for what they are intended to be – for all situations at runtime, where the system runs into a not predicted, invalid state (for whatever un-anticipated reasons)!

Thus, I designed my framework to never except any other 'return value' from a procedure (method) but the '.Void.' one. Soft-errors are reported back through an IN/OUT parameter that is passed in through a parameter object.

How to Apply The Void?

We could implement a protection scheme for our PEMs. If a caller isn't authorized to query our instance we could simply return our void property value and watch the "Kaboooom!" :-)

We also are able to change the voided property value from the inside. This could become handy in some internal programming scenarios: Chaining of procedure calls can be written more easily, if the involved procedures return some Boolean value all together, like so:

*\\_____________________________________
*//                    Work the ToDoList
Do Case
	Case Not This.CheckPermission()
		This.Log([CheckPermissionError])
	Case Not This.LoadConfig()
		This.Log([LoadConfigError])
	Case Not This.BuildObject()
		This.Log([BuildObjectError])
	Case Not This.StartObject()
		This.Log([StartObjectError])
Otherwise
	*\\_______________________________
	*//			     Start main app
This.StartEngine() This.ShowGui() This.ReadEvents() This.HouseKeeping() EndCase

One nice thing is that we can change the real value of our .void. property from the inside on the fly! We are able to store any other 'unbroken' values there temporarily. Thus, to run the last example code above, we would simply store <.T.> to our This.Void property before running the Do Case chain above. Afterwards, we will reset our property back to its voided value.

To make our code bullet-prove, we have to check all occurrences of VFP's Vartype() and Type() functions in our code, and try to evaluate, if a .void. scenario might apply in one or more of our contexts!

The following code fragment shows us how to to test a property at runtime fail-safe:

If Empty(Vartype(m.loInstance.void)
	DebugOut "Class instance is Void!"
Endif

We cannot test the return value of a method without running its behavior. Thus there is not much we can do to trap for a returned void value. Thus, there should be an appropriate documentation, or even better, an Intellisense-based guidance available, telling us what methods are functions and what other methods are procedures that will return THE .VOID.!

Some Evil Pitfall

It may happen to you, that this trick doesn't work: You cannot call you object's WriteExpression() without throwing an exception!

It took me some time spent on a lot of investigative testing to find out what causes this misbehavior to appear. This is not a trivial one, because it forces us to adapt out overall implementation strategy…

Stay tuned!

<to be continued…>

 


WINAPI & VFP Internals (Home)

The Madlen Effect

Version: 1.00.00 - last update: Saturday, September 6, 2014

WINAPI & VFP Internals (Home)


This Thread is About Some Native VFP Cursor Behavior

I worked together with a VFP colleague for two years; her name was Madlen. Madlen discovered a remarkable VFP feature while debugging some weird side effects, that were produced by exact that 'feature'. Thus, it is not my discovery, but let's give honor to whom honor is due. Obviously, Madlen don't find enough time to share here new knowledge with the VFP community beside here daily work. Madlen allowed me liberally to post about her findings on this blog.

I have to state that this VFP 'feature' was absolutely new to me, and all of my former colleagues, too. That's why I supposed that even today scarcely anybody knows about it.

The Madlen Effect

Madlen discovered that, if you create a temporary cursor using an array-based schema definition stemming from a schema array created using VFP's AFields(), you better have to be very careful! Madlen used one of the DBC-based 'master' tables as her schema source, just as we all would have done it. Alas, in this particular case, the schema source table had some triggers attached. Naturally, these trigger expressions were caught by VFP's AFIELDS() function, and got transferred into Madlen's schema definition array. From there they were applied to the new temporary cursor. What could I say, I think you might already have guessed it: The trigger were alive in the newly created cursor, too!

Such an undocumented(?) side-effect may cause you a lot of headache! Not so much if you incidentally close the trigger procedure, which would raise a traceable exception immediately, but in cases you are not aware of the triggered behavior at all, wondering about 'strange' values appearing in you temporary cursor, obviously without any good reason…

picard-fail_499x327

I know, how that hurts…

I'm currently refactoring my top level modules' asynchronous message queues to incorporate VFP Cursors! What a show: Semi-persistent message queue stacks that don't pollute my DBC, but behave like the Big DBC-Bros do – somehow slick ;-)

<to be continued…>


WINAPI & VFP Internals (Home)

How to Jump Over the Hidden Hurdle

Version: 1.00.00 - last update: Monday, September 1st, 2014

WINAPI & VFP Internals (Home)


Hiding a PEM in Visual FoxPro makes this feature unavailable for reuse and takes it out of inheritance, right?
Are You Sure?
…

Intro

If you've ever wondered what VFP's Scope Resolution Operator really is good for, this is the place to stay!

VFP's Scope Resolution Operator (::)

On the internet, there is not much to read about the obviously orphaned scope resolution operator provided since VFP 3.0. Most comments run out on telling you that it is better today to use VFP's DODEFAULT() function to forward a message to the precursor.

Well, within the calling context of a simple DODEFAULT() they are right, all together!

But, there is more to say about the scope resolution operator than what's written in the help file, or any internet site I reached so far (while searching for already published information dealing with this topic).

This post assumes that you are familiar with what is written about the scope resolution operator in VFP's documentation. At least, you should have a clue what you can do with it, according to its help file entries.

What VFP's Scope Resolution Operator CAN and CAN NOT

Okay, we know, we have to use the class name instead of the instance name when invoking a Scope Resolution Operator Call. Let's assume, we want to invoke a method from one of our classes named "SuperContainer" using VFP's SROC like so:

llOkay = AbstractContainer::GetSecurityHandle(m.lcSecToken, This)

… where AbstractContainer is the name of a parent class somewhere 'above' the current one in the inheritance tree. Let's use the following inheritance pedigree as a basis to ask some questions further below:

Inheritance Example
(click to enlarge)

Can you answer the following questions?

    1. Inside a ClsCnt_CtrolsHost class instance, what class names can be used in a SRO call without raising an exception?
    2. Can you use a different method name on your ancestor class (can you invoke BaseContainer_GUI::Move() for example if such a method would exist)?
    3. Can you use the base class name of your class instance within your SRO invocation (can you invoke Container::Click() for example)?
    4. Can you use class names of other instantiated classes that are not part of your current inheritance pedigree (can you invoke SuperCollection::Add() for example if an instance of that collection-based class would exist in memory)?
    5. Can you use the class name of an descendant class (can you invoke ClsCtrlHost_Toolbar::Dock() for example if such a method would exist)?
    6. Can you invoke protected methods (can you invoke SuperContainer::RegisterItem() for example)?
    7. Can you invoke hidden methods (can you invoke AbstractContainer::GetSecurityHandle() for example)?
    8. Can you invoke method code "beyond" hidden methods (can you invoke _Container::GetSecurityHandle() for example)?
    9. Will the SRO invocation on a parent class trigger any event-bound delegates on your current class instance?
    10. Will a SRO invocation trigger VFP events at all?
    11. Will a SRO invocation cause the execution of DODEFAULT()s?
    12. At what inheritance level then does the DODEFAULT() pipelining start?
    13. Are there any behavioral differences between PRG-based, VCX-based, and Mixed (VCX->PRG-based) classes?
    14. Is it possible to introduce recursion (unwanted, or at will)?

As you can see, there's more about the VFP's Scope Resolution Operator than to think it's almost useless :-) Believe me, if you do know all about it, you will get one good new idea after the other how to employ Scope Resolution Operator Calls in unusual ways to solve unusual problems…

Ending for today…

One thing I would never do, even not in my wildest dreams, is to use the Scope Resolution Operator instead of a DODEFAULT(), at least in cases where a 'simple' DODEFAULT() is sufficient! The way I'm primarily using Scope Resolution Operator invocations is using them indirectly, like so:

lcRetVal = Evaluate(laAnchestors[3]+"::"+m.lcFeatureName+"()")

where the local laAnchestors array was filled using one of VFP's reflection-related function, like so:

= ACLASS(laAnchestors, This).

<to be continued…>


WINAPI & VFP Internals (Home)

WINAPI & VFP Internals

Neat Solutions HomeFoxtools HomeFoxQuill HomeVFPX Projects HomeMiscellaneous HomeWINAPI & VFP INternals HomeNews HomeBasics HomeTips&Tricks HomeToolbox HomeComplex Controls Home


The Scope Resolution Operator | Into The Void | The Madlen Effect | ADSs, Hard Links & Junctions | How to Tame an Editor | How to Tame a Debugger | Unbreakable Objects Revisited | Corral Your Libraries | Two-Faced Properties | Private Affairs | The Unknown Species

WINAPI And VFP Internals

Calling into the Windows API using 'self-declared' WINAPI calls always was a true adventure … in the early days this seemed reserved for a skilled group of C/C++ programmers, I never belonged to. Today, we have excellent support, for example, through using the VFPX VFP2C32 Project.

Intro

I do believe that there still is a huge number of qualified and fully skilled VFP developers out there that have no idea, and they also don't care, about most WINAPI function calls. They are used to solve all challenges of their business domains writing pure VFP code only. Good Boys!

Do it Your Own Way

It's alright for me not to use WINAPI calls, if you can solve your problems without them in a reasonable time, creating code that runs fast enough to make your boss happy. But within the (problem-) domain of this blog, we would be stranded sooner or later without the assistance of the underlying operation system.

On The Other Hand…

Sometimes, constructing C-structures and passing them to Windows functions using referenced VFP strings, sounds like a mission to mars to me… At least in those precious moments, when I luckily could unearth some unknown/forgotten native VFP feature! Inevitably, an old saying comes to my mind: "Why must we targeting the stars as long as we haven't found all treasures of earth?" hmm, that's a pretty free-style translation.

Content of This Thread

FoxPro's behavior is full of oddities. Luckily, most of us will never stumble upon most of them… But wait, there is another VFP developer 'species' – THOSE, who are haunting THEM! This section is especially made for all those VFP-Hackers – Welcome!

We will put the main focus on VFP here, not on how to define and execute WINAPI calls only, at least, to a certain extend. There are enough little treasures buried inside VFP to be raised. Sometimes oddities have to be moved to (or viewed from) another application context first, before they render useful. In other words, you might better think: "What else could I do with this bug?", instead of: "How can I code a work-around?" in the first place! Remember, a bug must not be your enemy, but can be an occasion for you to discover something new and useful, at least sometimes…


The Scope Resolution Operator

Scope Resolution OperatorUsing VFP's Scope Resolution Operator definitely is my least used method invocation strategy. Sounds familiar, right? The only thing that is written about the Scope Resolution Operator is, that it is used to call a method of its parent class. And then, all examples show an almost identical calling scheme: a method named Click() calls another method named Click() on its ancestor class. After that, one is advised to better use the DoDefault() method instead. I'm really no fan of conspiracy theories, but… ;-) Go and read the whole truth by yourself…


Into The Void

Into The Void  link

What if I would have asked you, if it's possible to really return NOTHING from a method? I mean, not that 'little' .NULL. value, but a real .VOID. one!? What would you have answered? Is it possible, and if so, what could it be good for?
Basically, a voided property value in VFP is one without a value AND without a type! Indeed, I found a special way to corrupt the internal type member of a VFP value structure using a native VFP method; let's say, by invoking it "a little bit outside of the recommended implementation guidelines"!


The Madlen Effect

The Madlen Effect link

I worked together with a VFP colleague for two years; her name was Madlen. Madlen discovered a remarkable VFP feature while debugging some weird side effects, that were produced by exact that 'feature'. Thus, it is not my discovery, but let's give honor to whom honor is due. Obviously, Madlen don't find enough time to share here new knowledge with the VFP community beside here daily work, and allowed me liberally to post about her findings. I have to state that this VFP 'feature' was absolutely new to me, and all of my former colleagues, too. That's why I supposed that even today scarcely anybody knows about it.

What's it all about?

Did you ever wondered about why temporary VFP cursors can have long field names? I mean, these guys are common free tables! VFP maintains long field names only for its DBC-bound "database tables", right? If we are able to make use of long field names, maybe it is possible to enable other DBC-only features, too? What's about implementing triggers on temporary tables? If it can it be done, how can we achieve that? And what can we (ab)use it for? …


ADSs, Hard Links & Junctions

ADSs, Hard Links & Junctions

Did you ever heard about the Delorean Copy DeLorean Copy? The link on the left leads you to the very interesting page of Mr. Schinagl from Austria. He is working on his Link Shell Extensions since 1999! Please read his enlightening, very thoroughly written documentation first, before we dive deeper into how we can use theses things from within our VFP code! If you prefer even more technically biased documentation, let me recommend this page FlexHEX Resources: NTFS Hard Links, Directory Junctions, and Window Shortcuts! It is full of detailed technical information, written from the viewpoint of a software developer who had developed the ultimate Hex-Editor, which is able to deal with raw hard drive data!

I will show you how to bootstrap/generate whole sets of classes at any level with the help of some very small PRG-based generators. Chief attraction: All PEMs, class–, and inheritance-definitions, as well as all module compositions are based on simple TXT-files and directory entries of a NTFS-file system. Okay, okay, naturally, they are highly "decorated" with Alternate Data Streams, and most of all files are purest NTFS Hard Links, or Directory Junctions. Therefore, you can move/copy them within your Windows Explorer at will. Only at the end of each design cycle you have to start a short generator run once. Refactoring and bulk modifications of whole feature sets never have been easier!


How to Tame an Editor…

How to Tame an Editor

Many, many years ago we were excited about syntax coloring. There was a company that offered syntax coloring for FoxPro editor sessions even before Microsoft implemented it natively. Today, I'm hoping for some code-folding feature, and other cool stuff we get for free from Visual Studio, would fall out of the sky…

At least, wouldn't it be nice to be able to launch any editor session in a top level window so that it could be dragged to our second screen? No, I don't mean that 'dusty' ACTIVATE WINDOW IN MACDESKTOP trick, that, by the way, ruins your editor session's copy & paste functionality instantly!
Or wouldn't it be nice to share a document between multiple editor sessions, each showing another part of the code? And, of course, code-folding! Is it possible to inject/apply code-folding into a VFP editor session window? That would be a valuable functionality, right?


How to Tame a Debugger…

Doc_Light_116

Do you need programmatic control over your debugger at design-time? Do you know what gets stored in the FoxUser.dbf records and how to unearth and reuse it? Do you know what the undocumented SYS(989,n) function is for? Do you know how to hook on a running VFP session from a Visual Studio debugger? If not, then this section is for you…


Unbreakable Objects Revisited…

Unbreakable Objects Revisited

One of my discoveries was, that it is possible to cancel a CLEAR ALL invoked from VFP's command window by putting a CANCEL command in the .QueryOnload() event of a SCX-based form instance (read more here). Well, canceling a CLEAR ALL is slightly not what we expect to happen when we explicitly type a CLEAR ALL in our VFP command window, right?
Good news: Luckily, there are instances of one distinct, native VFP base class which, if treated right, survive every interactively entered CLEAR ALL!
Another good news: If you treat one of your PRG-based procedure libraries the right way, all defined function in it will also survive an otherwise disastrous SET PROCEDURE TO.
Once more my questions:
Can we create such a class instance using VFP's Createobject() function? Can we then issue a CLEAR ALL that will destroy all instances immediately, but not our special one? Can we achieve this without resorting to the _SHELL trick, or using a CANCEL in one of our SCX-based form's QueryOnload() methods?
The answers are: YES, Yes, and Yes again…


Corral Your Libraries

Corral Your Libraries

This is all about VFP's Class Libraries, the visual, and the non-visual ones. We will discuss amongst other details:

  1. why it sometimes makes sense to add them to a FoxPro DBC.
  2. when and why we should using library alias names.
  3. why it is better to always use CreateObject() instead of Newobject().
  4. how to setup a generic loader mechanism for PRG-based class libraries.
  5. how to setup a reasonable Include File Management that can be dropped later, in cases the new target platform prevents us from using Include Files at all (like C# does).

Some of the above topics might be trivial pursuit for some of you, but wait, there is more to know about class libraries than just that! Many reflection-related VFP functions behave slightly different when used with SCX-, VCX-, or PRG- based objects. There are also subtle behavioral differences between PRG-based class instances that have a VCX-based ancestor somewhere "up" in their inheritance pedigree, and those PRG-based class instances that were build exclusively from flat file libraries (PRG-based ones)!
And, last but not least, we have numerous reflection-related function like AMEMBERS() that share one strange, optional parameter value. Can you describe exactly, how to use the cClassName parameter option of those functions? Tell us, how to use it with VFP's Amembers() function, as an example…
To be honest, for a long time I thought I had to simply pass in the name of the class to fill the Amembers array. I thought that this was some kind of design-time related option while dealing with an instance inside VFP's class designer. But, finally wondered, that non of my assumptions were true at all, obviously…
 


Two-Faced Properties

Two-Faced Properties

Please be honest, do you think it is possible to store more than one single value in a scalar (non-array) VFP property at a time? At design- and runtime? NO! you say? Right you are, I used to share this truism since I started working with Visual FoxPro.
One fine day, I had to muse about VFP's .ResetToDefault() method, because I've had some troubles getting it to do what I expected it to do.
Somewhere down the road I asked myself, how this darn .ResetToDefault() method could work internally. What do you think?
My number one question was: "From where do VFP's
.ResetToDefault() methods get their default values at runtime? Perhaps 'out of the air'?"; believe me, they don't…


Private Affairs

Private Affairs

How many ways do you know to create a private variable in VFP? I mean, beside the common declarations PRIVATE pMyPrivate, PRIVATE ALL, and the forgotten one (assigning a value to a variable not declared LOCAL). Well, let me tell you, there is at least one more non-obvious way, which is a little bit nasty, by the way. It is hard to debug, because VFP isn't complaining even if _VFP.LanguageOptions = 1 (which means: "Strict Memvar declaration required"), and it's hard to replace with another, more obvious, implementation, because the whole operation is a pure side-effect! But this place wouldn't be called The Hackers Corner, if we couldn't find a way to make use of that feature, right?


The Unknown Species (this 'teaser' will be moved into a new post, ASAP! So, do not read this;-)

The Unknown Species

The best has yet to come! Earlier this year (March 2014) it so happened that I stumbled over some source code example in the Regular VFP Help File. I couldn't believe my eyes, it must have been written down there during the outcome of VFP 3.0 – to be buried in oblivion…

I DO Tell The Truth, Believe Me!

I never saw such a piece of code! Nowhere! Neither silently applied, nor explicitly explained, neither published on the internet, nor privately kept by any of the companies I've ever worked for! Sounds weird, I know, as we all believe that we've seen every possible crime committable writing XBASE-Code, don't we?

What I Can Tell You so Far…

Well, the FF (Forgotten Feature)…

  1. is available since VFP version 3.0.
  2. obviously is not related to event binding, or other advanced concepts that were introduced only later.
  3. is a real, native VFP behavior, that radically hooks into the default VFP message handling between container instances and their children.
  4. is almost undocumented other than the short code snippet I was able to find inside VFP's documentation.
  5. allows us to write code once for all classes, even stemming from different base classes(!), and lets that class-agnostic code execute on every message sent to any child instance, independently of its class type!

Okay, I know, that you have to read the last point(5) twice! Take all the time you need, that smokeless tobacco is hard to chew :.)

A Native IoC-Container

Employing this orgotten feature, for me is the first implementation of an Inversion of Control Container in VFP, which has rightfully earned its name, because it's just that, what's going on behind the scenes, if you take that confusing buzzword literally! I've read a lot of bullsh** about how to implement an IoC-Container in VFP. Let me tell you that most of what I've read so far about IoC was even more obfuscating than the term as is! Especially, when it came to explain what the heck is really meant by Inversion of Control, and why, for heavens sake, we should try to apply that pattern within a native VFP runtime environment (without thinking about any future migration options).

What Our IoC-Container Does

Calling any method of any object added to our Native IoC-Container (that's how I'm going to call such a specialized container from now on), at first fires our IoC-Implementation, and not the other way round! We would expect that our code within the child instance would run first. From inside that child implementation then, we normally would decide to run a DODEFAULT() to invoke our parent class' implementation, or just not (in cases we want to override the feature completely with our child object's behavior).

Born into a Native IoC-Container, our child instances have no longer control over their message handling! For example, they cannot overwrite any behavior of a Native IoC-Container, because the Native IoC-Container implementation gets called first by Visual FoxPro – ALWAYS! Within a Native IoC-Container we are able (and have) to decide if we want to execute any child behavior, and when we want to do that.

The best of all (for me) is, that this Native IoC-Container feature gets invoked for all instances natively by VFP, and not by some dirty trick I came up with! VFP behaves totally type-agnostic: If a Native IoC-Container implements Click(), every Click() event raised from any object inside the Native IoC-Container will be intercepted by VFP, and will be redirected to the Native IoC-Container Implementation first, whatever class the child object was created from, and whatever implementation its local click() method has, or is missing.

I LOVE Good Cliffhangers Zwinkerndes Smiley

Whenever code of a Native IoC-Cotainer gets executed by Visual FoxPro natively, that code is run within the memory context of the child control! In other words, we have to (better said, we must) reflect VFP's type-agnostic behavior within the implementation of every Native IoC-Container, because it will be executed in totally different instance contexts at runtime…

Finally, For The Still Unconvinced

If you now ask: "What if there are a lot of buttons, textboxes and labels on my form – how does FoxPro let me know, which object's message it is just interfering?" - let me tell you, that there are two answers to this question:

  • a.) As I've told you, during the Native IoC-Container Call, we only have to query the actual THIS reference, which then is the child reference we are just taking over control.
  • b.) The next observation sounds equally unbelievable, but is also true:
    FoxPro adds another parameter to every signature of all forwarded messages, if their invocations were triggered from one of the controlled IoC-children! The parameter value passed in is an integer index into the IoC-Container's children enumeration. This element index parameter is passed in at the 1st position of any signature, always! For example, if we want to implement a type-agnostic, Native IoC-Container Hook for, let's say, the MouseDown() events of all class instances that implement that feature, we have to define our IoC.MouseDown() like so:
PROCEDURE Object.MouseDown
LPARAMETERS tnIndex, tnButton, tnShift, tnXCoord, tnYCoord
Is that cool, or am I just going to make a total fool out of myself, because this is common sense, and I am the only VFP guy on earth that wasn't aware of the whole story?