Re: Err Object Anomaly Bug in VB6+SP5 and How to Reproduce
- From: "Someone" <nobody@xxxxxxx>
- Date: Wed, 2 Nov 2005 15:53:41 -0500
Here is a shorter sample that does not use CreateWindow, but uses Form1 &
Form2. Form2 is subclassed. I call this sample, Sample #2.
For those who don't know what is this about, I repeat in short: If Form2 is
subclassed and has code that references any Form1 property or method, it
will crash the application if there was any runtime error anywhere in the
project in either EXE or IDE modes. It doesn't matter what, where, and when
the runtime error occurs, it will crash the application after displaying a
weird behavior. And by weird behavior I mean these things happen in the
following sequence:
- No message box displaying the runtime error that has just occurred.
- Public variables are reset to 0. I did not test string variables.
- Form1 is destroyed and reloaded. The new window has a different hWnd.
- If there was a Set statement to create objects in Form1's Form_Load event,
an error 429: "ActiveX component can't create object" shows up.
- Application crashes.
In the sample, I am using 'Form1.Caption = "ABC"' in the subclass procedure
to reproduce this problem, but the same thing happens if you reference any
of Form1 properties and methods. For example, 'x = Form1.hWnd' will cause
this problem too, despite its simplicity.
In the real world, the subclassed window receives Winsock messages, in which
it calls RaiseEvent which has code that accesses Form1 methods and
properties. I am not accessing any Form1 methods or properties in the
subclass procedure, but I am accessing them in event procedures called by
RaiseEvent.
My objective is to find a work around without removing 'Form1.Caption =
"ABC"', nor by deferring the code to a Timer, or for MS to fix this problem.
My environment: VB6 with SP5, Windows XP Pro with SP2. The problem is
reproducible in other OS's, such as Windows 2000.
Steps to reproduce the problem(Sample #2):
- Start a new Standard EXE Project, Form1 is created by default.
- Add a new Form, Form2 is added to the project.
- Add a new Module.
- Set the Startup Object in the project's properties to "Sub Main".
- Paste the following in the general section of Form1.
' Form1 Code ================================
Option Explicit
Private Sub Form_Load()
Form1LoadCounter = Form1LoadCounter + 1
MsgBox "Form_Load called, Form1 Load Counter = " & Form1LoadCounter
End Sub
Private Sub Form_Click()
Dim a As Integer
MsgBox "Form_Click called, RecursionCounter = " & RecursionCounter
a = 1000 / 0 ' Generate a division by zero runtime error and hangs the
app
MsgBox "Form1:Form_Click: After a = 1000 / 0" ' Never show up
End Sub
- Paste the following in the general section of Form2.
' Form2 Code ================================
Option Explicit
Private Sub Form_Load()
SubClassForm Me.hwnd
End Sub
Private Sub Form_Unload(Cancel As Integer)
UnSubClassForm
End Sub
- Paste the following in the general section of Module1.
' Module1 Code ================================
Option Explicit
Private Const GWL_WNDPROC = -4
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As
Long
Private Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc&, ByVal hwnd&, ByVal msg&, _
ByVal wParam&, ByVal lParam&) As Long
Private m_hWnd As Long
Private m_OldProc As Long
Public Form1LoadCounter As Long
Public RecursionCounter As Long ' For FormWindowProc
Public Sub SubClassForm(ByVal h As Long)
m_hWnd = h
m_OldProc = SetWindowLong(h, GWL_WNDPROC, AddressOf FormWindowProc)
End Sub
Public Sub UnSubClassForm()
If m_OldProc <> 0 Then
m_OldProc = SetWindowLong(m_hWnd, GWL_WNDPROC, m_OldProc)
End If
End Sub
Public Function FormWindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long)
As Long
RecursionCounter = RecursionCounter + 1 ' Entered the procedure
Form1.Caption = "ABC" ' This line is where the exception occurs
' FormWindowProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
FormWindowProc = CallWindowProc(m_OldProc, hwnd, uMsg, wParam, lParam)
RecursionCounter = RecursionCounter - 1 ' Left the procedure
End Function
Public Sub Main()
MsgBox "Main called"
Form1.Show
Form2.Show
End Sub
' End of code sample ================================
- Save and compile the project.
- Run the executable from Windows Explorer.
- The application will show the following messages:
Main called
Form_Load called, Form1 Load Counter = 1
- Click on Form1, a message box shows the following:
Form_Click called, RecursionCounter = 0
This line is before the line that causes the run time error.
RecursionCounter proofs that there is no recursion in the subclassing
procedure.
- Click OK
- Instead of showing "Error 11: Division by zero" and terminating the
application, the following unexpected message appears:
Form_Load called, Form1 Load Counter = 1
Public variables now are reset to 0. The counter variable used above is of
Public type Long and it's not forced to zero anywhere. Form_Load appears to
have been reexecuted. Main() function itself is not reexecuted.
- Click OK, and the application crashes.
Again, my objective is to find a work around without removing 'Form1.Caption
= "ABC"', nor by deferring the code to a Timer, or for MS to fix this
problem.
.
- Prev by Date: Re: Procedure won't exit, EXE hangs
- Next by Date: Re: Add a new function in server side,the the old client can not work?
- Previous by thread: Re: Procedure won't exit, EXE hangs
- Next by thread: Validation in combination with a message box
- Index(es):
Relevant Pages
|