Preventing VB runtime error 401

drleewood used Ask the Experts™
Is it possible that repeatedly minimizing and normalizing a modal window can cause a "runtime error 401 - cannot show non-modal form when modal form is displayed" ?  There is no non-modal form displayed.  Code to display and minimize window is:

Form.windowstate = vbMinimized


Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
It sounds like you might be unintensionally be creating multiple instances of your Modal Form.

Can you post the basic code that is loading and showing your form?


The form Terminal is initially loaded in Sub Timer2.  It is then accessed,  normalized and minimized again in Sub Timer1.  Both timers are on the the main form which is modal.
Private Sub Timer2_Timer()

    Timer2.Enabled = False
    SkynetBridge.Show 1

    '-- Show the screen
    pause 2
    Dim Start!
    Start! = Timer
        pause 1
        Terminal.TERM1.TypeKeys "[ClrHome]"
        Call WaitforCursor("1:1", 3, waitcode)
        Terminal.TERM1.TypeKeys "Waiting for request..."
    Loop Until TextIsOnScreen("Waiting") Or (Timer - Start!) > 10
    If Timer - Start! > 10 Then
        If Len(Dir$(LocalDir$ & "Ready.txt")) = 0 Then
            f = FreeFile
            Open LocalDir$ & "Ready.txt" For Output As #f
            Close #f
        End If
    End If
    pause 2
    Terminal.WindowState = vbMinimized

End Sub

Private Sub Timer1_Timer()

    On Error GoTo MainTimer1_TimerErr
    'check for input file
    Terminal.Caption = UCase(logtype) & " folder" & Right(App.Path, 1) & " ...idle"
    If Len(Dir$(InputFile$)) <> 0 And Len(Dir$(LocalDir$ & "Busy.txt")) = 0 And Len(Dir$(LocalDir$ & "Ready.txt")) <> 0 Then
        Timer1.Enabled = False
        Terminal.WindowState = vbNormal
        If Len(Dir$(LocalDir$ & "WPIN_*.*")) <> 0 Then
            Kill (LocalDir$ & "WPIN_*.*")
        End If
        f = FreeFile
        Open LocalDir$ & "Busy.txt" For Output As #f
        Close #f
        StartTime2 = Timer
        f = FreeFile
        Open LocalDir$ & "StartTime2.txt" For Output As #f
        Print #f, Trim(Str(StartTime2))
        Close #f
        If Len(Dir$(LocalDir$ & "Ready.txt")) <> 0 Then
            Kill (LocalDir$ & "Ready.txt")
        End If
        pause 1
        Terminal.WindowState = vbMinimized
    End If

Exit Sub
    Call ErrorHandling("MainTimer1_Timer", Err.Number, Err.Description)

End Sub

Open in new window

So you have three form...
1. The main form (which is modal),
2. SkyNetBridge (which is modal in Timer2)
3. Terminal (which is loaded and show in Timer2 after SkyNetBridge closes).

There's your problem... you already have the Main form shown modal, then in Timer2 you show SkyNetBridge modal (that's ok, one modal form can superseed another modal form, when SkyNetBridge is dismissed, the main modal form will regain the focus).  But then you try to show Terminal modeless.  That's what 401 is saying you can not do.  You can't show Terminal modeless when you've got Main shown Modal.

The solution mght be as simple as NOT showing the main form Modal.  Instead, show it modeless, and when you do show Terminal, show it modeless with the main form as the parent.

Since you say you show the main form modal, I assum that you have a Sub Main that declares the Main form, loads it and shows it Modal.  When Main closes, Sub Main finishes and the application terminates.

If so, try something like this...
In the .BAS file where you have Sub Main(), define a global variable of type MainForm (what ever it's name is).  Then in Sub Main, set the global variable to a NEW MainForm, Load it, show it  (no modal).  
Dim g_MainForm as MainForm

If you want Terminal to close the MainForm when it closes, you can add a subroutine in the .BAS where Sub Main() is defined and call it something like "MainEnd()".  In the form unload of Terminal, you can call MainEnd().  The code in MainEnd() would then unload the MainForm.

.  Now you are allowed to show a modal form with a modal form... that's sort of like calling a subroutine within a subroutin
Ensure you’re charging the right price for your IT

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!


Please let me clarify a few points that may help you.

1. This application is successfully running.  The terminal window is loaded and is minimized when not needed,  then displayed when needed,  then minimized again waiting,  It is never unloaded except when the application is closed once a day.  There are fifteen similar apps running simultaneously on the the server.  Between the fifteen apps there are approximately 2000-3000 accesses to the terminal windows each day. The runtime error shows up once or twice a day which stops those particular apps.  We need to find out why.

2. The main form is loaded using the form_load event which goes to completion. As far as I know,  the main form is modal.  A timer on the main form is used once only to load the connection form called "SkynetBridge".  This form is non-modal and is unloaded before the Terminal form is loaded.  The terminal form is modal.  The "SkynetBridge" form in never loaded again.  The terminal form never gets unloaded nor does the main form.  Both do get minimized.  Then the terminal form gets normalized/minimized per above description in point 1.

3. I am unable to duplicate this runtime error in testing.  Only during a lot of user activity do we see it.  I don't want to add the form code that you suggested until we are pretty sure that it would help this situation.
The form.Show takes 2 arguments:  The 1st is whether or not the form is modal (0=modeless, 1=modal, from what I can find online for vb6 documentation) and the 2nd is 'Parent'.  The 1st parameter defaults to modeless, while the 2nd defaults to NULL (the desktop).

If I have that correct, then your comments "SkyNetBridge. This form is non-modal" contradicts the code above: SkyNetBridge.Show 1

How is Timer and SkyNetBridge defined?  I know VB has this sort of hidden global variable, one for each form defined in the project, and you access it by the form name.  I also know that odd or unexpected things can happen when you use these "hidden" global variables, or a variable dimmed with "as New ...".

The only way I have found that you can truely control the life cycle of an object (form or class) is to Dim a variable as type the form or class desired, then doing a new (and set = Nothing) to really control when the form actually does things.

So while I understand the idea of wanting to simply fix a program that is "almost" working, I've also learned the hard way from years of VB coding, that for reliable operations, you have to do things the "hard' way.

So if you do indeed have a form in your project named "Terminal", I would NEVER use it directly.  I would instead always have code something like:

Dim X as Terminal        'Declare a variable of type Terminal Form
Set X = new Terminal  'Create an instance of a Terminal Form object
Load X                         'Load the actual window for the Terminal Form object
Unload X                     'Destroy the window for the Terminal Form object
Set X = Nothing          'Destroy the Terminal Form object

(Until Set X = Nothing is called, all modual level variables within X will maintain their values because only the window will be destroyed on unload, while 'Set X = Nothing' will destroy the object.

I've done enough VB coding to know that VB can do some weird things at times.  For example, the following two lines of code SHOULD be identical:
SubName VariableName
Call SubName( VariableName )
I had a situation like this where the variable was passed by reference, and the SubName subroutine was supposed to modify the value of VariableName.  The 1st syntax failed to keep the value of VariableName as set by the subroutine (as if the value was passed by Ref) and the 2nd syntax worked as expected.


I am sorry,  but I mixed my definitions of modal and non- modal.  The modal form SkynetBridge is unloaded before the non-modal form Terminal is loaded.  I can try using the example you gave above to unload SkynetBridge and see if that makes a difference.  I will let you know.  Thank you.
I don't think that will make any difference.  The only thing my example will do to SkynetBridge is make sure the entire object is destroyed before Terminal becomes active.

Am I correct in that you have a form named "Terminal" and that you are therefore using the global "Terminal" object I talked about?  If so, it should be relatively trivial to replace "Terminal" with a
Global g_Terminal as Terminal.  Then in the start up code of your project, do

Set g_Terminal = New Terminal
Load g_Terminal  

In your shutdown code, add
Unload g_Terminal
Set g_Terminal = Nothing

Then just replace every instance of the "Terminal" object in your code with the g_Terminal object.

It should be trival to do the same with the Main form.  That way you at least stop using the "hidden" MainForm and Terminal forms (if you are indeed doing that) and only have to do a search and replace on the main form and modiy your startup and shutdown code to create/destory the objects as I've shown.


Am I correct that the 401 error would be most likely caused by the modal form SkynetBridge being not completely unloaded from memory and then the modeless form Terminal might cause that error when it is minimized/maximized?  If this is the case,  we would only modify the SkynetBridge form to make sure it is completely unloaded and out of memory.
Because 401 seem to indicate that a NON-modal form is being displayed within a MODAL form, I would suspect Terminal to be the problem rather than SkynetBridge because you show SkynetBridge modal and terminal non-modal.

Of course It won't hurt to change any of the forms to the new/load/unload/nothing form over using the global "hidden" objects.  Obviously I'm not seeing all the code either (so I didn't realize SkynetBridge is used more than once, since I don't see any code to reenable Timer1 after it executes).


SkynetBridge is used only once,  which is why I was thinking it was the culprit here by not being fully unloaded.  Terminal is loaded only once and is merely cycled between minimized and normal.  I will give it all a try.  I want to keep the question open in case I need to ask you about some syntax since I haven't used this method to load and unload forms.  Thanks for your patience.
The syntax is fairly simple...

Declare a variable as you would any other variable, except the variable type is the name for the form:
Dim m_Terminal as Terminal    

Create an instance of the Form and assign it to the variable
Set m_Terminal as New Terminal

The object exists, and module variables of m_Terminal can now be modified... but it does not exist as a window yet.  To create the window, use the load comment
Load m_Terminal

Then to destroy the window when you are done with it, unload it
Unload m_Terminal

You can still access non-window related modual level variable of m_Terminal, because the object still exists at this point.  To destory the object, set the variable to nothing...
Set m_Terminal = Nothing

About the only thing I've left out is the fact that if needed, you could actually create multiple Terminal windows and create multiple instances of it...
Dim m_Terminal1 as Terminal
Dim m_Terminal2 as Terminal
Set m_Terminal1 = New Terminal
Set m_Terminal2 = New Terminal
Load m_Terminal1
Load m_Terminal2

The other thing is that you can pass the m_Terminal variable around like any other variable or you can assign it to other variables...

Dim m_Terminal3 as Terminal
Set m_Terminal3 = m_Terminal1

Now m_Terminal3 and m_Terminal1 reference the same Terminal object.  If this is ever done, the object doesn't get destoryed until ALL references to the object are set to nothing...

Set m_Terminal3 = Nothing

Now m_Terminal3 doesn't reference any object, and the Terminal object that was referenced still exists because m_Terminal1 still references it.

Set m_Terminal1 = Nothing

Now the Terminal object is destoryed.

This syntax (Set X = New Object, Set X = Nothing) works with any variable that is an object.  In the case of a Form, you have the additional commands of Load and Unload to Create or Destroy the Window associated with the object (i.e. you can still have the object existing without the window it defines loaded yet).


Thank you. I will give it a try.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial