AmeriSciences
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
If it is a bug MS is not going to fix it at this point. lol
ASKER
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.
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.
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.
ASKER
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
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?
ASKER
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("TrapOnE rrorGoTo") Then On Error GoTo RoutineError
'here the o_frmBusy is nothing
Cancel = basVBBusy.rtnShowBusyForm( o_colIsBus y, 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.rtnSaveFormSetti ng(Me)
End If
End If
Call basVBForm.rtnSaveFormSetti ng(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("TrapOnE rrorGoTo") 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_fr m)
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("TrapOnE rrorGoTo") 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.
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("TrapOnE
'here the o_frmBusy is nothing
Cancel = basVBBusy.rtnShowBusyForm(
'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.rtnSaveFormSetti
End If
End If
Call basVBForm.rtnSaveFormSetti
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("TrapOnE
Dim intReturn As Integer
If rtnCheckIsBusy(r_colIsBusy
'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_fr
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("TrapOnE
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...
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...
ASKER
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.
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
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
Thanks
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