Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Err Object Anomaly Bug in VB6+SP5 and How to Reproduce

87 views
Skip to first unread message

Someone

unread,
Oct 13, 2005, 12:58:11 AM10/13/05
to
I have a strange problem with my VB6 Standard EXE program. The program is a
TCP/IP Client/Server application that utilizes the Winsock API(using CSocket
class). It creates a window that handles Winsock events. The project has few
dependencies and I plan to convert it to a service. I am using VB6 with SP5,
and Windows XP Pro with SP2 for development. The problem is reproducible in
other operating systems such as Windows 2000.

The problem is that if any runtime error occurs, regardless of when and
where and what error number, my VB6 program becomes unstable and crashes
instead of showing an error message and terminates normally. If an error
handler was added, the problem goes away, but this merely hides the error
object bug, not fix it. I have already added error handling throughout my
project, but I don't like the instability of the Err object. I need to
expand my program substantially and I don't want to worry about what
problems the Err object may present to me later. I am not the first to
experience this problem, but maybe the first to provide sample code to
reproduce it. The sample code is below, and doesn't utilize TCP/IP at all.

The following is what happens when any error occurs without an error
handler:

- Public variables are set to 0.
- Error 429: "ActiveX component can't create object".

Debugging with Visual C++ shows that the program ends with the following
exception:

INFO: Visual Basic Uses Exception Code 0xC000008F as Internal Exception Code
http://support.microsoft.com/default.aspx?scid=kb;en-us;232829

I could not pinpoint what API call fails, however, the line that the
exception occurs at is in the following line in WindowProc() function:

Form1.Label1.Caption = "ABC" ' This line is where the exception occurs

While commenting the line above makes these anomalies disappear, it hides
the problem rather than fixing it. The above is just a simple example.
Replacing it with 'Form1.Print 123' or 'Form1.Caption = "ABC"' for instance
also causes this problem.

Steps to reproduce the problem:

- Start a new Standard EXE Project, Form1 is created by default.
- Add a Label to Form1, leave the name at the default Label1.
- 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

CreateMyWindow
End Sub

Private Sub Form_Click()
Dim a As Integer

a = 1000 / 0 ' Generate a division by zero runtime error
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 Const WS_EX_WINDOWEDGE As Long = &H100&
Private Const WS_EX_CLIENTEDGE As Long = &H200&
Private Const WS_EX_OVERLAPPEDWINDOW As Long = WS_EX_WINDOWEDGE Or _
WS_EX_CLIENTEDGE
Private Const WS_OVERLAPPED = &H0&
Private Const WS_MAXIMIZEBOX = &H10000
Private Const WS_MINIMIZEBOX = &H20000
Private Const WS_SYSMENU = &H80000
Private Const WS_THICKFRAME = &H40000
Private Const WS_CAPTION = &HC00000 ' WS_BORDER Or WS_DLGFRAME
Private Const WS_OVERLAPPEDWINDOW = ( _
WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_THICKFRAME _
Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX)
Private Const CW_USEDEFAULT = &H80000000
Private Declare Function CreateWindowEx Lib "user32" Alias _
"CreateWindowExA" (ByVal dwExStyle As Long, _
ByVal lpClassName As String, ByVal lpWindowName As String, _
ByVal dwStyle As Long, ByVal x As Long, _
ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, _
ByVal hWndParent As Long, ByVal hMenu As Long, _
ByVal hInstance As Long, lpParam As Any) 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 Sub CreateMyWindow()
m_hWnd = CreateWindowEx(0&, _
"STATIC", _
"TESTWINDOW", _
0&, _
0&, _
0&, _
0&, _
0&, _
0&, _
0&, _
App.hInstance, _
ByVal 0&)

If m_hWnd <> 0 Then ' Success
m_OldProc = SetWindowLong(m_hWnd, GWL_WNDPROC, _
AddressOf WindowProc)
End If
End Sub

Private Function WindowProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

WindowProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
'WindowProc = CallWindowProc(m_OldProc, hwnd, uMsg, wParam, lParam)

Form1.Label1.Caption = "ABC" ' This line is where the exception occurs
'Form1.Print 123

End Function

Public Sub Main()
MsgBox "Main called"
Form1.Show
End Sub

- 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, where Form_Click contain a line that causes a division by
zero error. There is no error handler in Form_Click.
- Instead of showing "Error 11: Division by zero" and terminating the
application, Form1 disappears, and the following message appears:

Form_Load called, Form1 Load Counter = 1

Note that the counter variable used is of Public type Long and it's not
forced to zero anywhere.

- After you click on the message box above, another message box shows up:

Error 429: ActiveX component can't create object

- After you click OK, the application crashes.

Does anyone know how to "stabilize" the error object without removing
"Form1.Label1.Caption" from WindowProc in the sample above? I tried changing
CreateWindowEx parameters and using RegisterClassEx to no avail. Using
CallWindowProc instead of DefWindowProc works better in some cases, but the
problem still appears. In the sample above, using either one doesn't make a
difference.

Sean Myers

unread,
Oct 13, 2005, 10:25:28 AM10/13/05
to
More of a guess than a genuine solution...(experts feel free to correct me
:-) )

I would guess that the form instance (Form1) crashes, but the O/S is still
calling into that memory space, and the WindowProc attempts to access the
instance Form1 which is crashed causing the second dialog with a messed up
count. The program instability is likely caused by not unhooking your
WindowProc properly on application shutdown. (Won't happen if you just let
the application crash!)

Several things you may wish to consider:
1) Use an explicit instance of Form1.
in module:
Public gfForm1 as Form1

Sub Main()
Set gfForm1=New Form1
gfForm1.Show
End Sub

Check in your WindowProc to see if gfForm1 is nothing

2) Unhook your WindowProc on program termination.
3) Handle that error properly!
I'm not sure what you mean by Err object instability. I have never
experienced any problems with the Err object.

HTH,
Sean


Ken Halter

unread,
Oct 13, 2005, 10:36:28 AM10/13/05
to
"Someone" <nob...@cox.net> wrote in message
news:OuBE1K7...@TK2MSFTNGP09.phx.gbl...

>I have a strange problem with my VB6 Standard EXE program. The program is a
>TCP/IP Client/Server application that utilizes the Winsock API(using
>CSocket class). It creates a window that handles Winsock events. The
>project has few dependencies and I plan to convert it to a service. I am
>using VB6 with SP5, and Windows XP Pro with SP2 for development. The
>problem is reproducible in other operating systems such as Windows 2000.

Try unhooking the window before showing a MsgBox. Look ma, no crash! (here
anyway)
'==================Form changes
Private Sub Form_Click()
On Error GoTo ErrorTrap
Dim a As Integer

a = 1000 / 0 ' Generate a division by zero runtime error

Terminate:
Exit Sub

ErrorTrap:
SafeMsgBox "Error " & Err.Number & vbCrLf & Err.Description
Resume Terminate
End Sub

Private Sub Form_Unload(Cancel As Integer)
Call UnHookMyWindow
End Sub
'==================Module changes
Private mlMyWindowHandle As Long

Public Sub CreateMyWindow()
m_hWnd = CreateWindowEx(0&, "STATIC", "TESTWINDOW" _
, 0&, 0&, 0&, 0&, 0&, 0&, 0&, App.hInstance, ByVal 0&)


If m_hWnd <> 0 Then ' Success

mlMyWindowHandle = m_hWnd

Call HookMyWindow

End If

End Sub

Public Sub HookMyWindow()

If m_OldProc = 0 Then
m_OldProc = SetWindowLong(mlMyWindowHandle, GWL_WNDPROC _
, AddressOf WindowProc)
End If

End Sub

Public Sub UnHookMyWindow()

If m_OldProc <> 0 Then ' Success

Call SetWindowLong(mlMyWindowHandle, GWL_WNDPROC, m_OldProc)

m_OldProc = 0

End If

End Sub

Private Function WindowProc(ByVal hWnd As Long, _


ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

WindowProc = DefWindowProc(hWnd, uMsg, wParam, lParam)


'WindowProc = CallWindowProc(m_OldProc, hwnd, uMsg, wParam, lParam)

Form1.Label1.Caption = "ABC" ' This line is where the exception occurs
'Form1.Print 123

End Function

Public Sub Main()
MsgBox "Main called"
Form1.Show
End Sub

Public Function SafeMsgBox(ByVal Prompt As String, _
Optional ByVal Buttons As VbMsgBoxStyle = vbOKOnly, _
Optional ByVal Title As String) As VbMsgBoxResult

Call UnHookMyWindow

If Len(Title) = 0 Then
Title = App.Title
End If

SafeMsgBox = MsgBox(Prompt, Buttons, Title)

Call HookMyWindow

End Function
'==================

--
Ken Halter - MS-MVP-VB - http://www.vbsight.com
DLL Hell problems? Try ComGuard - http://www.vbsight.com/ComGuard.htm
Please keep all discussions in the groups..


Someone

unread,
Oct 13, 2005, 1:08:38 PM10/13/05
to
> The program instability is likely caused by not unhooking your WindowProc
> properly on application shutdown. (Won't happen if you just let the
> application crash!)

In the sample I am not doing it because VB6 will never reach the line that
UnSubClass it. In my real App, I am doing it. I wanted to keep the sample as
small as possible.

> Public gfForm1 as Form1
>
> Sub Main()
> Set gfForm1=New Form1
> gfForm1.Show
> End Sub

I tried that. It shows the following messages then the CPU usage goes near
100%. gfForm1 is never shown. I replaced Form1 references in the code as
appropriate, i.e., changed 'Form1.Caption =' to 'gfForm1.Caption =':

Main called
Form_Load called, Form1 Load Counter = 1

I also used SubClass/UnSubClass code in the instance above. Also, I used a
Form2 instead of CreateWindowEx, then used gfForm1 instead of Form2. The
result above is when subclassing gfForm1. Here are my subclassing routines,
called in Form Load/Unload events:

Public Sub SubClassForm(ByVal h As Long)
m_hWnd = h
m_OldProc = SetWindowLong(h, GWL_WNDPROC, AddressOf FormWindowProc)
End Sub

Public Sub UnSubClassForm(ByVal h As Long)
If m_OldProc <> 0 Then
m_OldProc = SetWindowLong(h, GWL_WNDPROC, m_OldProc)
End If
End Sub


P.S.: I have been using VB since version 1.0, so I am not a beginner, not
that you thought I am...


"Sean Myers" <seanm@use_as_necessary_matrixcontrols.com> wrote in message
news:eEMv2HA0...@TK2MSFTNGP10.phx.gbl...

Someone

unread,
Oct 13, 2005, 1:31:19 PM10/13/05
to
> Try unhooking the window before showing a MsgBox. Look ma, no crash! (here
> anyway)

You have added an error handler which hides the problem. I am aware of that
solution. What I want to know is why referencing Form1 properties and
methods in the sample causes these anomalies if you don't have an error
handler. I know that I suppose to have error handlers everywhere, but
without it, the behavior is not what is expected.

In my real application, WindowProc processes Winsock messages, and depending
on which socket, it gets a reference to a class and calls a method that
raises events in that class. In event procedures that use that class, I am
doing all kinds of references to Form1 methods and properties. All of the
code in these events was designed carefully so it returns quickly so not to
drop any packets. If something takes a long time or need to show a message
box, I defer it to a Timer. With this design all Winsock events are
processed quickly, and with this design, I cannot remove references to
'Form1.Caption = "ABC"' to solve the problem, because I have too many of
them in event procedures.


"Ken Halter" <Ken_Halter@Use_Sparingly_Hotmail.com> wrote in message
news:%23raH$NA0FH...@TK2MSFTNGP12.phx.gbl...

Ken Halter

unread,
Oct 13, 2005, 3:31:22 PM10/13/05
to
"Someone" <nob...@cox.net> wrote in message
news:OykDqvB0...@TK2MSFTNGP15.phx.gbl...

>
> You have added an error handler which hides the problem. I am aware of
> that solution. What I want to know is why referencing Form1 properties and
> methods in the sample causes these anomalies if you don't have an error
> handler. I know that I suppose to have error handlers everywhere, but
> without it, the behavior is not what is expected.
>
> In my real application, WindowProc processes Winsock messages, and
> depending on which socket, it gets a reference to a class and calls a
> method that raises events in that class. In event procedures that use that
> class, I am doing all kinds of references to Form1 methods and properties.
> All of the code in these events was designed carefully so it returns
> quickly so not to drop any packets. If something takes a long time or need
> to show a message box, I defer it to a Timer. With this design all Winsock
> events are processed quickly, and with this design, I cannot remove
> references to 'Form1.Caption = "ABC"' to solve the problem, because I have
> too many of them in event procedures.

Do you have any code that prevents the CreateWindowEx from running more than
once? (but you said it doesn't matter if it's a VB form or a window you
created with CreateWindowEx)

You might want to create an instance if Form1 instead of using it directly.
At least, Load shouldn't fire twice if the object reference to the form is
invalid.... so, you'd get an error 91 instead of 429. If the object variable
is a "global", you should be able to search/replace all.

Maybe it's the act of loading a new GUI while processing the message loop
that's causing grief. If that's the case, a static flag in the Winproc to
prevent recursion might help.

Someone

unread,
Nov 2, 2005, 3:53:41 PM11/2/05
to
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.

0 new messages