Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

form still continues even if one object.INIT fails

Status
Not open for further replies.

EinTerraner

Technical User
Jul 17, 2024
54
DE
Any suggestions? Why does the form show, even if i return false in the button-init? see picture.
There is only a native form and a native button
Frm_035.jpg
only the button is missing, if i return .f. in it's init-method

its fine not to load the object, but how can i stop the form to continue loading?
 
Last edited:
From help... To prevent a control from being created, return false (.F.) from the Init event. The Destroy event will not be triggered. For example, the following code returns false (.F.) if the Invoice table is not available:
Code:
PROCEDURE INIT
IF NOT FILE("INVOICE.DBF")
ERROR 'Initialization Failed: File not found'
RETURN .F. 
ELSE
USE INVOICE IN 0 AGAIN
THIS.WorkArea = SELECT()
ENDIF
ENDPROC

If you want stop form to continue loading, then you must put RETURN .F. to form's Init() or form's Load().
 
If forms would work as you think, i.e. not be started just because one control (or any object) of it is terminated by the Return .F. from Init() mechanism, then you couldn't use that mechanism to have optional elements controlled by permissions, for example. Which is one intended use of that.

It's as mJindrova tells, the only way it works for the whole form is from the forms Init() and on top of that even already in the Load() event, which other classes don't have, obviously. That's allowing a very early termination of the form before even any control init.

That opens another question for you, perhaps. How do you let the form close, when one of its controls goes missing? For example when a necessary ActiveX control isn't registered (your setup should handle that, but there might be some intentional or unintentional unregistration of it), it will error, be removed at form start and the form still starts. Obviously that's not the RETURN .F. mechanism but a total failure to get the control.

Anyway, you could verify the existance of all form controls in form init and then choose to quit the form. The most drastical method would be to subclass all controls, and put Thisform.Release() in their Destroy() event, so destructions of the controls also tear down the form, if you want that behaviour. You then could also counteract that in the Release() event of form classes.

Putting Thisform.Release() in the Click() event of the button, as you did - did you expect the click event to happen? It happens "on click" only.
 
Last edited:
If you want stop form to continue loading, then you must put RETURN .F. to form's Init() or form's Load().
Yepp, this I know, but i could not yet figure out how to pass a failed init-method (return .f.) (done workaround see below)
Anyway, you could verify the existance of all form controls in form init and then choose to quit the form.
well with an cascaded FOREACH Obj in Form.obects i can check the exitence of an object and/or their sub-objects. But in can't find any unloaded objects. Where i can see what objects where added in the form by using the form-designer. In the LOAD-method i dnt see yet anything

Do you have any program code in the form's Init event?
not only in the form.init. But also in some objects that I glue into the form.

simplified representation of my current solution.
Code:
&& MyForm has a property named "DontRunForm"
&& already declared in the form-classlib

&& This is the basic-init-method in my form-classlib
LPARAMETERS Parm1, Parm2
IF VARTYPE(This.DontRunForm) == "C"
    =MESSGEBOX(This.DontRunForm, 16, "Can't show Window ["+This.Name+"]", 10000)
ENDIF
&& Initialize basic form requirement
RETURN EMPTY(This.DontRunForm)

&& This is the init-method in my used form (designer)
IF !DODEFAULT(Parm1, Parm2)
    RETURN .f.
ENDIF

&& This is the init-method in any used objects-classes or used in the form
LPARAMETERS Parm1, Parm2
&& Initialize this Object if required
IF AnyThingFails
    ThisForm.DontRunForm = "Error description"
ENDIF
RETURN
any better idea?
 
First of all I don't understand why you already display a messagebox, when the vartype of the DontRunForm property is C. If you would have such a form property that's already set before the form even starts, what is the point? The form would never start or always start. Notice an empty string is still also a string.

The idea I had in mind when I said...
Anyway, you could verify the existance of all form controls in form init
...is that you would know what objects you expect. You could only check that in Init(), in Load() nothing is yet initialized. And you can only check so with knowledge of what structure you expect to exist.

The simpler general approach is to let every control have the form release in its destroym as already said. The question is really, why you would want that behavior? If you actively return .F. from somewhere, then to remove that object, only that object, and that's intended for example to remove a feature of a form to which a user has no permission, without destroying the whole form. If you want a permissen driven prevention, what hinders you to check the permission for the whole form in the form load or init? That would have nothing to do with form controls removing themselvses or not, i.e. you could easily have an optional feature in a command button, that's not essential for the form to work, and remove that without releasing the whole form, but you can still easily define for users, which don't have permission to the whole form to let it quit at init or load.

What you obviously need for a general thisform.destroy in objects destroy event is subclassing all native base classes. If you think it's unusual to have such sub classes of every control/visual base class that has some code in it for a general behavior, you miss that this is a general advice about VFP OOP usage, as you can't add behavior like that retroactively, your only chance is never using the native base classes but start from a first layer of your own classes, which then can have specific or general behavior added to them.

Your only other choice is BINDSEVENTS. I won't try out for you to have a central form load that binds to all object destroy event and trigger the form release by this binding. It would be tedfious, it would perhaps only work, when there is code in the destroy events, some event bindings have such a requirement. Check it out for yourself, but I don't really see the point of having that behaviour. Handle the form release at the form level, not within any single object of it.
 
Last edited:
Besides that, no control is removed from a form, unless you explicitly RETURN .F. from it, and that requires code, your code, to do so, if you do so you can also at the same time release the whole form, if you want to, can't you? The process is fully under your control in the code you program into some controls that you remove that way.

This means, you also don't have a general need for this behavior, it only arises for any object/control you program in such a way, anyway.
 
Code:
Public oform1

oform1=Newobject("form1")

If Vartype(oForm1) = "O"
    oform1.Show
EndIf

Return

*
Define Class form1 As Form
    DoCreate = .T.
    Caption = "Form1"
    lload = .F.
    Name = "Form1"

    Add Object command1 As myCommand With ;
        Top = 24, ;
        Left = 12, ;
        Height = 27, ;
        Width = 84, ;
        Caption = "Command1", ;
        Name = "Command1"

    Procedure Init
        Return Thisform.lload
    Endproc
Enddefine

Define Class myCommand As CommandButton
    Procedure Init
        *!* Changing the value of a custom property prevents the form Init
        Thisform.lload = .T.
    Endproc
Enddefine
 
AFAIR form.init runs after objects.init. Maybe you can have a form property lProblem which you set to .t. in .init of controls before return .f.
Then in form.init or whatever you can kill the form if its set to .t. by one or more controls

Regards
tom
 
I still don't see the use case. If you want to protect your form composition from not being manipulated, you have not much available in VFP. You can't define a class to be final, i.e. nothing can inherit from it.

If that's the goal, even having a form init that checks whether all controls in the SCX of the form are actually there and none was removed by RETURN .F. can be avoided, as the form init can be overwritten within a subclass of the form, whoever has your code and your classes can subclass and change things that way.

If you're after a sensible way to check health of your EXE, use a checksum check, better yet verify a cryptographic signature that can testify there was no manipulation.
 
Last edited:
AFAIR form.init runs after objects.init. Maybe you can have a form property lProblem which you set to .t. in .init of controls before return .f.
Then in form.init or whatever you can kill the form if its set to .t. by one or more controls
Like in an old thread (2005 or like) i've read before i started this one inside any object./form.init-method it does not work to insert something like ThisForm.Release()
I still don't see the use case.
not to protect my form and neither my exe-file.

in case the user has any form already running, this form will recognize and change and applies (OS-DiscEvent fires a event in the form) any changes (new data) within the used fileset. but if a update with locked (exclusive used) is in progress a new form is not allowed to run.

I could check this before I start the form, but I would need a lot of different program code for each different form and this is difficult to maintain. What makes it even more difficult is that a certain form can be started from several places, with different parameters. So I would have to insert this check routine everywhere.

I therefore carry out this check in the object, placed in the form, wich is responsible for checking and accessing the required files and do not allow this form in the event of an error. This means that I only need to make program adjustments in one place.
 
Despite the IF VARTYPE(This.DontRunForm) == "C" that should better be IF NOT EMPTY(This.DontRunForm) your code example shows you have understood a few things and you're not after what was upfront the idea to let any objects init failure close the form.

Just for a littel bit more realization of what does not happen here: If you use CREATEOBJECT("formclass") and the formclass Load() or Init() returns .F. that doesn't return .F. instead of the form object, it "returns" .NULL,. That means the init does not return to a caller, the RETURN mechanism of VFP to mean something is internal in this case. The controls on a form also don't return a value by RETURN to anything, not to a caller, not to the forms init.

You came to a conclusion to set a property to a reason to display, when the form should not start that's only concerning any object participating in the setting of that property. That works, but there are some details on top of the wrong choice of VARTYPE:

In forms inheriting from such a form, their Init will have the LPARAMETERS line shown, the event is still considered empty, if you don't progra anything into it and the base form class return value becomes inherited, without needing a DODEFAULT() call.

If you want to explicitly program something in a subclass form, then it would be like this:
Code:
LPARAMETERS p1,p2

Local llContinue
* This form subclass considerations to run or quit
* setting llContinue to .F. or .T.

RETURN DODEFAULT(p1,p2) AND llContinue

And you should consider letting all controls involved adding reasons to the DontRunForm property, not overwrite it. otherwise you only see the last controls reason to let the form quit.
 
And you should consider letting all controls involved adding reasons to the DontRunForm property, not overwrite it
1st) all forms have the same init-behavior and thei own init-procedere where i do initialize formspecific properties.
one of them is, and this works fine now
Code:
LPARAMETERS P1, P2, P3, P4, P5, P6

WITH This
    IF .UseStatusBar
        SET CLASSLIB TO (.F_BALLOON.ClassLibrary) ADDITIVE
        .AddObject( "F_StatBar", "ctl32_StatusBar")
    ENDIF
    .Form_HideTime_Count = .Form_HideTime_Value
    .Par1 = P1
    .Par2 = P2
    .Par3 = P3
    .Par4 = P4
    .Par5 = P5
    .Par6 = P6
    BINDEVENT(.F_SYSTICK,"SecTimer",This,"TimerEvent")
    BINDEVENT(.HWnd, WM_POWERBROADCAST, This , "Form_WinMsgs", 4)

    IF VARTYPE(.DoNotLoad) == "C" ;
    .and. !EMPTY(.DoNotLoad)
        =MESSAGEBOX(.DoNotLoad, 16, "Fenster ["+.Comment+"] nicht verfügbar", 10000)
    ENDIF

    IF !EMPTY(.DoNotLoad)
        RETURN .f.
    ENDIF

&& here follows  the rest of the INIT
The first object that activates this "do not load" property takes precedence.
All other following objects where this case might occur have following code as the first statement in the init method.
Code:
&& MyObject.INIT
LOCAL Lc_Reor

IF TYPE("ThisForm") == "O" ;
.and. !EMPTY(ThisForm.DoNotLoad)
    RETURN .f.
ENDIF

&& do my init here...

&& here the part, where any error might happen
    IF .SrvExist
        .UseSrvFile(.SrvAlias)
        Lc_Reor = !.Data_Test(.SrvAlias)
        USE IN SELECT(.SrvAlias)
    ENDIF
    IF !Lc_Reor ;
    .and. .LocExist
        .UseLocFile(.SrvAlias)
        Lc_Reor = !.Data_Test(.SrvAlias)
        USE IN SELECT(.SrvAlias)
    ENDIF

    IF Lc_Reor && error present
        IF MESSAGEBOX("File for ["+.Comment+"] needs to be updated.", 16+4, "System update required", 10000) == 6 && JA
            .Data_Reorg()
        ELSE
            IF TYPE("ThisForm") == "O"
                ThisForm.DoNotLoad = "Reorganisieren von "+.Comment+" verweigert"
            ENDIF
            RETURN
        ENDIF
    ENDIF
With this mechanism (and a few external properties) in my File_IO_class, it is not at all important to me WHICH file(s) I need, where they are stored and how I get to them.
It is only important to me that I can get to them.
multiple installations, each file has its own physical name.
(e.g. phone book in one installation = BOB_V011_Tel.DBF, in the other BILL_V011_Tel.DBF)
but within my application I always use the File_IO_Object.Name = "TELDATA".
This way I saved hundreds of lines of code.
 
TYPE("ThisForm") == "O"is lways true, even if the form init did not yet decide whether the form actually starts or quits. Likewise VARTYPE(.DoNotLoad) == "C" is always true, unless you initialize it with .F., but even then checking whether it is NOT EMPTY is enough, because .F. is empty or in other words EMPTY(.F.) is .T.

Then you also want to RETURN .F. and otherwise not, so you can shorten this...

Code:
WITH THIS
...
    IF VARTYPE(.DoNotLoad) == "C" ;
    .and. !EMPTY(.DoNotLoad)
        =MESSAGEBOX(.DoNotLoad, 16, "Fenster ["+.Comment+"] nicht verfügbar", 10000)
    ENDIF

    IF !EMPTY(.DoNotLoad)
        RETURN .f.
    ENDIF
...
ENDWITH

...to this

Code:
WITH THIS
...
   IF !EMPTY(.DoNotLoad)
      =MESSAGEBOX(.DoNotLoad, 16, "Fenster ["+.Comment+"] nicht verfügbar", 10000)
      RETURN .F.
   ENDIF
...
ENDWITH
 
Last edited:
All other following objects where this case might occur have following code as the first statement in the init method.
Code:
IF TYPE("ThisForm") == "O" ;
.and. !EMPTY(ThisForm.DoNotLoad)
    RETURN .f.
ENDIF
again, shorten it to
Code:
IF !EMPTY(ThisForm.DoNotLoad)
    RETURN .f.
ENDIF

It's your choice to let only one object set DoNotLoad and spare to initialize further objects, then. I recommend to give all reasons, because from the users perspective you get shown one reason why the form does not start, may easily fix this and restart, just to find out there is another reason, fix that, and restart and... well..., get angry, perhaps?! "Why don't you show me all reasons to fix at once?"
 
Last edited:
Some of this IO-class i also use for certain reason outside of an form. So i need to check, if it's in or out of a form. Therefore the TYPE("ThisForm") up there.

One of the main reason is to set and check some public variables like my own adress, the environment-settings. This i'll do just ONCE and i dnt have to code the access
To set an Object with my adress, my mail-addy and sum other stuff it's the simplest way to keep those infos in my adress-table and/or phone-table. I do load this in the StartUp-programm, where i use the same IO_Class i do use in the forms. managing the own adress is the same as managing customers adress.

Another feature... the user can also work the most functions in offline-mode. a relic because of the corona aftermath and the home-office-duty. the user can also do his job while fooling around with the kids on the beach 😁 all new data he's creating are saved local and marked with a "dirty-data-flag". As soon he reconnects to the server, those data will be syncronized
 
Therefore the TYPE("ThisForm") up there.
I see, It seems an unelegant solution to me, but I can't think of a general more elegant way from the top of my hat.
You could work with This.Parent in both cases, perhaps, when these IO checking classes always are put at the top level of forms (not inside some page of a pageframe or inside a container.

But if they have no parent outside of forms, i.e. are standalone objects, that'd still not be generally usable. You could work with PEMSTATUS, perhaps, checking whether a property exists or not. And you could still work with

Code:
TRY
   IF !EMPTY(ThisForm.DoNotLoad)
      RETURN .f.
   ENDIF
CATCH
ENDTRY
And, yes, that's also not more elegant, I agree.
 
Last edited:
What you could do instead of modifying your IO-classes to feed and verify a form property is let form load or init create your IO-class objects and if they don't initialize call it a day and quit.

That means, for example in form load:
Code:
Localk loIO
loIO = CreateObject("yourioclass") && which has RETURN .F. in init, if requirements are not met
IF ISNULL(loIO)
   Return .f.
ENDIF
*...continue with loading....
Instead of just puttin the object on the form.

I understand that changes the process of adding requirements checked by a form to having such code instead of just dropping a class on a form by drag&drop, perhaps.

Well, you could have a form property telling which IOclasses should be used for checking requirements and process that list in form load.

PS: by not creating the object the loIO variable actually becomes .F., not .NULL., it doesn't change the principle.
 
Last edited:
And, yes, that's also not more elegant, I agre
i agree 😁

Do you know the saying "All roads lead to Rome"? There are also many different methods to solve a problem here.

I actually only use this TRY-ENDTRY mechanism permanently in 2 parts of the program. Otherwise only temporarily, while I create and test a new function somewhere. But this will be removed again because I hope that the new function will be error-free after repeated testing. Most of the time these are typos or a forgotten LOCAL declaration.
The fixed TRYs are
a) around the body of the main program to record crashes. CATCH to Lc_ErrObj is logged using all available means.
b) when saving several records on the server. Normally this doesn't fire, but if the server is connected via VPN and the connection breaks down, errors can occur. Actually, it never happens, only when the router is forced to disconnect (24 hours) and a new Internet IP is assigned. Unfortunately, this can lead to interruptions because VPN sometimes takes too long to stabilize. (I've only had this happen twice in the last 5 years, but I had it)
Instead of just puttin the object on the form.
But in this case it needs some kind of coding within the form.load and/or form.init. And i have this class with different properties/functions up to 10 times within one form.
in the mid of 80'ies we used to say. Each saved line of code (non written) saves you to get hit by two errors.

one pair of my IO-Class are placed within a container, so not anymore as top-level object. One is responsible for customers-adresses and the 2nt for the memberlist and their phonenumbers.
i have ONE form with 2-pages-PageFrame for manage the view for this datasets.
1st page main-grid for adresses and a slave-grid for corresponding members and their phonenumber.
2nd page main-grid for members and their phonenumber. and a slave-grid for corresponding adresses (if exist)
exept the few lines do initiate the grid-layout i have only few lines of code within this Grid.
The INIT-Procedere for the GRID has 3 Steps.
1) the Pre-Init (native GRID.INIT) Sets all required basic-properties/environment, and a BIND-EVENT to the form.activate-event for Post-Init
no data acces here, because i dont care here if the IO_Class allready known and passed the INIT
2) form.INIT, if it passes, i'll do the GRID.Data_Init for creating connections to the IO-classes and create the required cursors
wich also might fail therefore here no access to the real data-sources
3) post-init, fires on the FORM.Activate if FORM.INIT did not fail. This Binding will be released here (UNBIND) and data will acessed for display.
in step 3 there actually only the code to set the relation from Main-Grid into slave-grid like this
rudimentary representation of the post-init code
Code:
DODEFAULT() && unbind and sum more
with This && Grid-PostInit
    set order to (ThisForm.SlaveGrid.RelOrder) in (ThisForm.SlaveGrid.RelAlias)
    select(.DisAlias)            && it's a cursor, not the real table
    set relation to (.RelOrder) into (ThisForm.SlaveGrid.RelAlias) ADDITIVE
endwith
every else needed stuff in here is already done in the IO_Class and/or native INIT and GRID.Data_Init
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top