Link to home
Start Free TrialLog in
Avatar of AmeriSciences
AmeriSciencesFlag for United States of America

asked on

vb6 byref withevents not working

I am having an issue with by ref in vb6.

I have a private form with events object declared in a form.  If I pass the private form to a function as by ref, the object is not returned to the parent routine.  here is a code and debug snippet.

Public WithEvents o_frmBusy As frmVBBusy

Private Sub Command9_Click()
    Debug.Print o_frmBusy Is Nothing
   
    rtnSetForm o_frmBusy
   
    Debug.Print o_frmBusy Is Nothing
End Sub

Public Sub rtnSetForm(ByRef r_frmBusy As frmVBBusy)
    If r_frmBusy Is Nothing Then
        Debug.Print r_frmBusy Is Nothing
   
        Set r_frmBusy = New frmVBBusy
       
        Debug.Print r_frmBusy Is Nothing
    End If
End Sub

Debug is
True
True
False
True

if I remove the withevents it works.  any thoughts
ASKER CERTIFIED SOLUTION
Avatar of nffvrxqgrcfqvvc
nffvrxqgrcfqvvc

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Test To Validate WithEvents Influence on Fnc(ByRef O as AnyObject)
I am ver surprised ,    I think it is a bug ???

Private WithEvents x As Class1
Private z As Class1

Private Sub Form_Load()
   f x
   f z
   Debug.Print (x Is Nothing) 'Return True
   Debug.Print (z Is Nothing) 'Return false
   
   Dim y As Class1
   Set y = x
   f y
   Set x = y
   'Set x = y
   Debug.Print (x Is Nothing) 'Return True
End Sub
Sub f(ByRef a As Class1)
  If a Is Nothing Then Set a = New Class1
End Sub



Avatar of AmeriSciences

ASKER

If it is a bug MS is not going to fix it at this point.  lol
egl1044

I have been thinking how I would even do this in the app that I have written.  This is a set of common forms and modules that indicate to the user that the app is busy.  The withevents part is to allow the user the ability to tell the app to cancel what it is doing.

In the example I posted, I have the o_frmBusy object as public, but it is really a private in the app.  I was trying the Public type to see if that would fix the problem.  

I played around with it a little and it looks like I am going to have to create the form object in the parent form and then pass it into the routine to show it and close it.

Thanks for your help.
Avatar of nffvrxqgrcfqvvc
nffvrxqgrcfqvvc

I don't see a bug :( you just need to explicitley reference the same object name when you use WithEvents keyword.
BTW: I understand where your concern is for this but if you use the WithEvents keyword it expects the object to be referenced specifically so it can do stuff internally to wire the events. When you don't use the WithEvents keyword it doesn't expect that you want to wire any events so it works in that case.
How would I pass the private variable instance to the public routine to use in the new instanciation?

Or in the set statement to force the object that was created in the routine to be passed back.  

I guess I could have the routine return the new object....

any other thoughts
If you could provide a "real time" example of what your application is doing it would help with a better solution. Can you post example source code directly from your application?
Here it is

The Form Code.

Private o_colIsDirty As Collection  'This collection holds the names of the objects that are on this object that are dirty

Private WithEvents o_frmBusy As frmVBBusy


Private Sub Form_Unload(Cancel As Integer)
    Const cstRoutineName = "Form_Unload"

    If prcGetRegionValue("TrapOnErrorGoTo") Then On Error GoTo RoutineError

   'here the o_frmBusy is nothing

    Cancel = basVBBusy.rtnShowBusyForm(o_colIsBusy, Me, o_frmBusy)

   'here the o_frmBusy is nothing

    If Cancel = 0 Then
        If o_blnIsDirty Then
            If MsgBox("Are you sure you want to cancel?  All unsaved work will be lost.", vbYesNo + vbDefaultButton2) = vbNo Then
                Cancel = -1
            End If
        End If

        If Cancel = 0 Then
            Call basVBForm.rtnSaveFormSetting(Me)
        End If
    End If

    Call basVBForm.rtnSaveFormSetting(Me)
   
    If Cancel = 0 Then
        End
    End If

RoutineExit:
    Exit Sub

RoutineError:
    Call gsubError(Err.Number, Err.Description, o_cstObjectName, cstRoutineName, "", True)
    Resume RoutineExit
End Sub

in basVBBusy

Public Function rtnShowBusyForm(ByRef r_colIsBusy As Collection, ByRef r_frm As Form, ByRef r_frmBusy As frmVBBusy) As Integer
    Const cstRoutineName = "rtnShowBusyForm"

    If prcGetRegionValue("TrapOnErrorGoTo") Then On Error GoTo RoutineError
   
    Dim intReturn As Integer
   
    If rtnCheckIsBusy(r_colIsBusy) Then
        'GWS 4/23/2009: can not do a msgbox here, because it will stop the running process.
       
        If r_frmBusy Is Nothing Then
            Set r_frmBusy = New frmVBBusy
        End If
       
        Call r_frmBusy.rtnShowForm(r_frm)
       
        r_frm.Enabled = False
       
        intReturn = -1
    Else
        intReturn = 0
    End If

RoutineExit:
    rtnShowBusyForm = intReturn
    Exit Function

RoutineError:
    Call gsubError(Err.Number, Err.Description, o_cstObjectName, cstRoutineName, "", True)
    Resume RoutineExit
End Function

Public Function rtnCheckIsBusy(ByRef r_colIsBusy As Collection) As Boolean
    Const cstRoutineName = "rtnCheckIsBusy"

    If prcGetRegionValue("TrapOnErrorGoTo") Then On Error GoTo RoutineError

    Dim blnReturn As Boolean

    blnReturn = (r_colIsBusy.Count <> 0)

RoutineExit:
    rtnCheckIsBusy = blnReturn
    Exit Function

RoutineError:
    Call gsubError(Err.Number, Err.Description, o_cstObjectName, cstRoutineName, "", True)
    Resume RoutineExit
End Function


let me know if i missed something.
Well that helps to see what your doing but it's unclear if you want to open a new instance of frmVBBusy every call or just want to load it once and only show when your main application is still busy. In that case don't unload the form just Hide and Show until your ready to Unload the form. I don't know how your calling this line calling ( Private WithEvents o_frmBusy As frmVBBusy ) unless frmVBBusy is actually a class and not a form. Can you be more specific.. I am little confused yet, I have an idea what your doing but yet I think what you really need can be accomplished without doing all this extra work.
For example in your code your calling ( Set r_frmBusy = New frmVBBusy ) if that is a form a NEW form will be loaded each time. If it's a class a new reference will be created to the class but it's not associated with the "real" o_frmVBBusy you have declared using withevents. You need to make additional call to reference that object. Now if frmVBBusy is a Class then you need to expose the reference to the form from the class using a property. I don't know exactly what your looking for so you will need to explain in more detail...

 


        

Open in new window

These items are in the declarations section of the form.  (Sorry I had a typeo in the first example.  o_colIsDirty = o_colIsBusy)

Private o_colIsBusy As Collection   'This collection holds the names of the objects that are on this object that are busy

Private WithEvents o_frmBusy As frmVBBusy


frmVBBusy is a Form

If there is an easier way I would love to here it.  The goal here is the keep the user from ending the program when it is still busy, and also keep them from moving to another section of the program while it is still busy.

When an action starts it adds a value to the o_colIsBusy Collection and when it is removed when the action is done.  When the collection is empty the program is no longer busy.  I know that is a bit combersome, but it was what I could do with what I had.  I knew of no easier way to know if the app was busy in vb6.  

I hope this helps, if not please ask and I will try to answer additional questions.  You have given me a few new ideas.  Thanks for that.
Can you start a new project and keep Form1 by default and add one more form called frmVBBusy. Then paste this code below into Form1. Run project and try to close Form1... It won't close until you set the collection to no items. I see what your issue is where you doing this from a module... You might want to consider using a Public variable in a form or creating a custom class.
Option Explicit

Private WithEvents o_frmBusy  As Form
Private col As Collection

Private Sub Form_Load()

  Set o_frmBusy = frmVBBusy '// wire events to frmVBBusy
  Set col = New Collection
  col.Add "Test" ' 1 item
  
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
  
  If col.Count <> 0 Then
    o_frmBusy.Show
    Cancel = True
  Else
    Unload frmVBBusy
  End If

End Sub

Private Sub o_frmBusy_QueryUnload(Cancel As Integer, UnloadMode As Integer)

  o_frmBusy.Hide
  Cancel = True
  
End Sub

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well that is a very simple and elegant approach.  I usually try to stear clear of public variables, but in this case it may be the best solution.

Thanks