Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392? 
Author Message
 Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392?

Has anyone else ever seen this, ANY clue as to what's causing this would
be GREATLY appreciated!

ABSTRACT:
=========
There is a hard-to-reproduce bug which causes an MFC ASSERT in
wincore.cpp at line 392.  This appears to be caused by a more
fundamental real-time bug in Windows 95.

REPRODUCABILITY:
================
This appears to happen very rarely on *any* Win95 machine, but on
certain machines it will happen consistently and reproducably for
several days, then gradually become less frequent.

On days when it is consistent and reproducable, it will only occur when
the App is NOT being run under the VisualStudio de{*filter*}!

The bug has never been observed on Windows NT.

I have not observed this bug in applications other than our own, and I
have no explanation for that;  I can't completely rule out the
possiblity that my app is somehow corrupting Win95's internal data
structures.

DETAILS:
========
This is actually a combination of bugs:  Bugs in the MFC library in
wincore.cpp which  normally lie dormant until they are triggered by a
deeper bug somewhere within Windows 95 USER32.DLL.

I understand what's going wrong in the MFC library, but I don't
understand what's happening below that in USER32.

I am quite certain of the following details, which have been painfully
discovered by several days work with various debugging tools.

THE WIN32 PART:
===============
Under some (unknown) circumstances, a call to SetWindowLong (...,
GWL_WNDPROC, ....) fails when it should succeed.

According to the API documentation, the only time this could fail would
be if the window belonged to a different process, which is certainly not
the case here.  The application has previously done SetWindowLong () on
the same window, successfully, in order to subclass the the window, but
the call fails during un-subclassing.

I presume the API returns NULL on the failing call, as documented, but,
because of the difficulties in reproducing this problem, I haven't yet
been able to examine this return code under a de{*filter*}.

Because the failure is intermittent, I presume it is some sort of
real-time bug.

Because this bug has appeared on multiple machines, and because the
application always fails at the same spot (if it fails), and because the
bug never appears when the app is being run under the VS de{*filter*}, I
believe it is not a hardware fault.

THE MFC PART:
=============
What's going on in wincore.cpp is that MFC is subclassing any popup
windows that are owned by the App -- in this case Open File common
dialogs.  The MFC code sets the window procedure to
_AfxActivationWndProc(), saving the address of the previous window proc
in a Windows Property.  The property is deleted when the window is
un-subclassed, during WM_NCDESTROY:

388:  LRESULT CALLBACK
389:  _AfxActivationWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM
lParam)
390:  {
391:    WNDPROC oldWndProc = (WNDPROC)::GetProp(hWnd, szAfxOldWndProc);
392:    ASSERT(oldWndProc != NULL);

422:            case WM_NCDESTROY:
423:                    SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
424:                    RemoveProp(hWnd, szAfxOldWndProc);
425:                    break;


Sometimes the App will put up the Assert message box during WM_DESTROY,
but more often the App crashes with an INT3.

[What usually happens is the attempt to launch the assert message box
will send a WM_ENABLE  message back to the App's frame window, notifying
it that it's loosing focus;   CFrameWnd::OnEnable() then calls
CFrameWnd::NotifyFloatingWindows(), which sends a WM_FLOATSTATUS message
to the subclassed window.  This reenters _AfxActivationWndProc,
triggering the assertion recursively.  If you're using SoftICE or
another tool to see TRACE messages, you'll see:
"Second Chance Assertion Failed: wincore.cpp, line 392".]

The first bug in _AfxActivationWndProc is that it doesn't check the
return value from SetWindowLong (line 423), to see if it failed! When
SetWindowLong fails to change the windproc address back to oldWndProc,
the property gets deleted anyway, and when the next message for that
window is delivered (WM_DESTROY), the App ASSERTs and dies.
The correct code should be something like:
        case WM_NCDESTROY:
                if ((LONG)(LPVOID) &_AfxActivationWndProc == SetWindowLong(hWnd,
GWL_WNDPROC, (DWORD)oldWndProc))
                        RemoveProp(hWnd, szAfxOldWndProc);
                // else memory leak, but nothing can be done about it.
                break;

Secondly, In My Humble Opinion, _AfxActivationWndProc should handle
being called after the property is destroyed more robustly.  I would
change line 392 to something like:
        ASSERT(oldWndProc != NULL);
        if (oldWndProc == NULL)
                return DefWindowProc (hwnd, nMsg, wParam, lParam);
My thinking here is that this situation also could occur under other
circumstances.  (See the next section for an example.)  It would be nice
if the release version of an MFC App would continue without an
unnecessary crash here.

NOT CAUSED BY MULTIPLE SUBCLASSING
==================================
For a while, I was guessing that someone else was subclassing the same
window after MFC, but the window wasn't being un-subclassed in the
correct (FIFO) sequence.  If that hypothetical other software was
restoring the GWL_WNDPROC pointer back to _AfxActivationWndProc *after*
_AfxActivationWndProc had done his un-subclassing, it would produce the
same symptoms.  However, I have proved that that is NOT what's happening
by using SoftICE to set a breakpoint that logged all SetWindowLong (...,
GWL_WNDPROC, ....) calls.

Q164166
=======
There is an Article in the Knowledge Base (Q164166 "PRB: Assert in
Wincore.cpp with MFC in an NT Service"), which describes the same
source-line ASSERT being thrown, but only when the MFC App is an NT
Service, which is NOT the case here.  This is not a service, and is only
failing under Win95.  The same application has never shown this bug
under NT.   Unfortunately, Q164166 doesn't provide an details as to
exactly why that assertion is thrown in that other case.



Tue, 31 Oct 2000 03:00:00 GMT  
 Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392?

Jim,

 > 392:      ASSERT(oldWndProc != NULL);

I think this is where I would salt the code to check the result from
GetProp(hWnd, "AfxOldWndProc") more often -- and remember, even const
strings like szAfxOldWndProc can be overwritten if someone tries hard
enough.

Cheers,
Felix.

--
If you post a reply, kindly refrain from emailing it, too.
I have killfiled hotmail.com and yahoo.com. If you are legit,
come forth and be recognized -- with a *real* email address.



Tue, 31 Oct 2000 03:00:00 GMT  
 Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392?

SetWindowLong will also fail the the HWND parameter refers to an already
destroyed window.  That is, it refers to something that is not a window.
Use IsWindow api to check.

--
---
Andrew Osman
ViewSoft, Inc.
http://www.*-*-*.com/
---

Quote:

>Has anyone else ever seen this, ANY clue as to what's causing this would
>be GREATLY appreciated!

>ABSTRACT:
>=========
>There is a hard-to-reproduce bug which causes an MFC ASSERT in
>wincore.cpp at line 392.  This appears to be caused by a more
>fundamental real-time bug in Windows 95.

>REPRODUCABILITY:
>================
>This appears to happen very rarely on *any* Win95 machine, but on
>certain machines it will happen consistently and reproducably for
>several days, then gradually become less frequent.

>On days when it is consistent and reproducable, it will only occur when
>the App is NOT being run under the VisualStudio de{*filter*}!

>The bug has never been observed on Windows NT.

>I have not observed this bug in applications other than our own, and I
>have no explanation for that;  I can't completely rule out the
>possiblity that my app is somehow corrupting Win95's internal data
>structures.

>DETAILS:
>========
>This is actually a combination of bugs:  Bugs in the MFC library in
>wincore.cpp which  normally lie dormant until they are triggered by a
>deeper bug somewhere within Windows 95 USER32.DLL.

>I understand what's going wrong in the MFC library, but I don't
>understand what's happening below that in USER32.

>I am quite certain of the following details, which have been painfully
>discovered by several days work with various debugging tools.

>THE WIN32 PART:
>===============
>Under some (unknown) circumstances, a call to SetWindowLong (...,
>GWL_WNDPROC, ....) fails when it should succeed.

>According to the API documentation, the only time this could fail would
>be if the window belonged to a different process, which is certainly not
>the case here.  The application has previously done SetWindowLong () on
>the same window, successfully, in order to subclass the the window, but
>the call fails during un-subclassing.

>I presume the API returns NULL on the failing call, as documented, but,
>because of the difficulties in reproducing this problem, I haven't yet
>been able to examine this return code under a de{*filter*}.

>Because the failure is intermittent, I presume it is some sort of
>real-time bug.

>Because this bug has appeared on multiple machines, and because the
>application always fails at the same spot (if it fails), and because the
>bug never appears when the app is being run under the VS de{*filter*}, I
>believe it is not a hardware fault.

>THE MFC PART:
>=============
>What's going on in wincore.cpp is that MFC is subclassing any popup
>windows that are owned by the App -- in this case Open File common
>dialogs.  The MFC code sets the window procedure to
>_AfxActivationWndProc(), saving the address of the previous window proc
>in a Windows Property.  The property is deleted when the window is
>un-subclassed, during WM_NCDESTROY:

>388:  LRESULT CALLBACK
>389:  _AfxActivationWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM
>lParam)
>390:  {
>391:  WNDPROC oldWndProc = (WNDPROC)::GetProp(hWnd, szAfxOldWndProc);
>392:  ASSERT(oldWndProc != NULL);

>422:  case WM_NCDESTROY:
>423:  SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
>424:  RemoveProp(hWnd, szAfxOldWndProc);
>425:  break;


>Sometimes the App will put up the Assert message box during WM_DESTROY,
>but more often the App crashes with an INT3.

>[What usually happens is the attempt to launch the assert message box
>will send a WM_ENABLE  message back to the App's frame window, notifying
>it that it's loosing focus;   CFrameWnd::OnEnable() then calls
>CFrameWnd::NotifyFloatingWindows(), which sends a WM_FLOATSTATUS message
>to the subclassed window.  This reenters _AfxActivationWndProc,
>triggering the assertion recursively.  If you're using SoftICE or
>another tool to see TRACE messages, you'll see:
>"Second Chance Assertion Failed: wincore.cpp, line 392".]

>The first bug in _AfxActivationWndProc is that it doesn't check the
>return value from SetWindowLong (line 423), to see if it failed! When
>SetWindowLong fails to change the windproc address back to oldWndProc,
>the property gets deleted anyway, and when the next message for that
>window is delivered (WM_DESTROY), the App ASSERTs and dies.
>The correct code should be something like:
> case WM_NCDESTROY:
> if ((LONG)(LPVOID) &_AfxActivationWndProc == SetWindowLong(hWnd,
>GWL_WNDPROC, (DWORD)oldWndProc))
> RemoveProp(hWnd, szAfxOldWndProc);
> // else memory leak, but nothing can be done about it.
> break;

>Secondly, In My Humble Opinion, _AfxActivationWndProc should handle
>being called after the property is destroyed more robustly.  I would
>change line 392 to something like:
>  ASSERT(oldWndProc != NULL);
> if (oldWndProc == NULL)
> return DefWindowProc (hwnd, nMsg, wParam, lParam);
>My thinking here is that this situation also could occur under other
>circumstances.  (See the next section for an example.)  It would be nice
>if the release version of an MFC App would continue without an
>unnecessary crash here.

>NOT CAUSED BY MULTIPLE SUBCLASSING
>==================================
>For a while, I was guessing that someone else was subclassing the same
>window after MFC, but the window wasn't being un-subclassed in the
>correct (FIFO) sequence.  If that hypothetical other software was
>restoring the GWL_WNDPROC pointer back to _AfxActivationWndProc *after*
>_AfxActivationWndProc had done his un-subclassing, it would produce the
>same symptoms.  However, I have proved that that is NOT what's happening
>by using SoftICE to set a breakpoint that logged all SetWindowLong (...,
>GWL_WNDPROC, ....) calls.

>Q164166
>=======
>There is an Article in the Knowledge Base (Q164166 "PRB: Assert in
>Wincore.cpp with MFC in an NT Service"), which describes the same
>source-line ASSERT being thrown, but only when the MFC App is an NT
>Service, which is NOT the case here.  This is not a service, and is only
>failing under Win95.  The same application has never shown this bug
>under NT.   Unfortunately, Q164166 doesn't provide an details as to
>exactly why that assertion is thrown in that other case.



Tue, 31 Oct 2000 03:00:00 GMT  
 Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392?

Andrew,

Thanks for the idea.  I hadn't thought of that possibility.
But if the HWND is "already destroyed", can it still receive
messages?

Quote:

> SetWindowLong will also fail the the HWND parameter refers to an already
> destroyed window.  That is, it refers to something that is not a window.
> Use IsWindow api to check.

> --
> ---
> Andrew Osman
> ViewSoft, Inc.
> http://www.viewsoft.com
> ---



Fri, 03 Nov 2000 03:00:00 GMT  
 Anyone know: SetWindowLong(GWL_WNDPROC) fails, causes ASSERT wincore.cpp line 392?

Raymond,

Thanks for the reply.

Yes, clearly the window IS getting WM_DESTROY (and WM_FLOATSTATUS)
messages after WM_NCDESTROY, as I discussed in my first posting.  
But that should work just fine; the problem is that those messages are
being delivered to the wrong windproc,
which can only occur if the SetWindowLong(GWL_WNDPROC) failed
to modify the windproc address.

What I don't understand is what can cause these messages to be
sent to _AfxActivationWndProc() *after* it has used
SeWindowLong(GWL_WNDPROC) to restore the previous windproc address??

Quote:

> The other possibility is that your window is getting a message
> after or during the WM_NCDESTROY.  WM_NCDESTROY destroys the
> property, and then you die on the recursive message.

> I believe there are weird compatibility scenarios where you can
> get messages after WM_NCDESTROY.
> --



Fri, 03 Nov 2000 03:00:00 GMT  
 
 [ 6 post ] 

 Relevant Pages 

1. Assert fails for wincore.cpp

2. Wincore.cpp line 980 ASSERT - why?

3. Assertion Failed wincore.cpp line 1241

4. HELP: WINCORE.CPP - AfxWndProc assert problem !

5. WINCORE.CPP - AfxWndProc assert problem !

6. WinCE MFC wizard project asserts in Wincore.cpp

7. Assertion Failed wincore.cpp

8. assertion in wincore.cpp @ line 986

9. wincore.cpp: line 369

10. wincore.cpp assertion (line 986)

11. Wincore.cpp line 311

12. Assertion in wincore.cpp, line 474

 

 
Powered by phpBB® Forum Software