Possibly a bug in MFC?
Dale Wilson -- dale@dra.com Friday, February 09, 1996 In MFC 4.0 VC 2.2 on NT3.51. Very near the beginning of COccManager::IsDialogMessage is the following code: CWnd* pWndFocus = CWnd::GetFocus(); HWND hWndFocus = pWndFocus->GetSafeHwnd(); near the end of this routine appears: if (::IsWindow(hWndFocus)) { UIDeactivateIfNecessary(pWndFocus, CWnd::GetFocus()); Eventually UIDeactivate triggers ASSERT(::IsWindow(m_hWnd)); where m_hWnd is pWndFocus->m_hWnd and m_hWnd is most often 0xfeeefeee [as in "...fieefiee foeefoee fummfumm", but that's another story] By breaking on (pWndFocus->m_hWnd != hWndFocus) I have discovered that the damage is done in a call UIActivateControl(CWnd::GetFocus()); appearing shortly before the second code snippet above. In other words, if the test was: if (::IsWindow(pWndFocus->m_hWnd)) then UIDeactivateIfNecessary would not be called. Apparently the CWnd pointed to by pWndFocus is being changed (probably being deleted!?). BTW, although OleControl Containment is enabled, the CFormView involved has NO OCX's. The triggering event is a mouse click on the form background (not on any control). It is very reproducable in my program, but I haven't been able to reproduce it in any other program--hence I suspect something I'm doing is wrong, or triggering a bug in MFC (or both). My program has grown a LONG way from the original Wizard generated MDI/CFormView/ OCX Enabled /OLE Container application. So the questions are: 1) is this a bug in MFC and/or 2) what have I done to trigger it (how's that for an easy question (grin)?)? I'm open to suggestions, help, abuse, anything!? dale@dra.com
Dale Wilson -- dale@dra.com Monday, February 12, 1996 The plot thins... Friday I wrote about code in COccManager::IsDialogMessage: CWnd* pWndFocus = CWnd::GetFocus(); HWND hWndFocus = pWndFocus->GetSafeHwnd();if (!bResult) { bResult = ::IsDialogMessage(pWndDlg->m_hWnd, lpMsg); if (bResult && (CWnd::GetFocus() != pWndFocus)) UIActivateControl(CWnd::GetFocus()); } if (::IsWindow(hWndFocus)) { UIDeactivateIfNecessary(pWndFocus, CWnd::GetFocus()); ... which caused (eventually) an ASSERT(::IsWindow(m_hWnd)); because the CWnd pointed to by pWndFocus was no longer valid. I reported that the damage was done during: UIActivateControl(CWnd::GetFocus()); Alas, I lied. The damage actually happens in: bResult = ::IsDialogMessage(pWndDlg->m_hWnd, lpMsg); (silly me, I assumed that if it didn't reference pWndFocus or the focused window it couldn't *possibly* be causing the problem, so I didn't break on that line ) The answers to my questions are: 1) Is this a bug in MFC? Answer: Yes! See the answer to question 2... 2) What have I done to trigger it? Answer: In my OnLButtonDblClk routine I instantiate a CRectTracker, then call TrackRubberBand. At some point (I am speculating here) during the handling of TrackRubberBand, the MFC idle time stuff gets called and "garbage collects" the temporary CWnd pointed to by pWndFocus. Upon return we have a pointer to nowhere and... Thank goodness (and the MFC developers) for asserts. Thanks, also, to Phil Shaw..a coworker of mine who made me put a breakpoint on EVERY line in the suspicious region. As a work-around, I plan to disable the "drag and select" feature of my program (sigh). Can anyone suggest a method to work around this problem short of rebuilding the MFC dll's? PS: Whiile tracking this down, I also uncovered a minor defect in the compiler itself (it should have generated a warning message for the following "typographic error" in my code CWnd *pChild = m_pChildControl; UINT ID = CWnd->GetDlgCtrlID(); // <- {I'm sure I didn't really type that, [blush]} which called GetDlgCtrlID for the CFormView rather than for pChild, but shouldn't have called anything at all! What's the best way to report such anomolies to the VC compiler development folks? A half duplex reporting channel is fine. I don't need a fix. I just thought they outta know. dale@dra.com
Brad Wilson -- bradw@netnet.net Wednesday, February 14, 1996 > As a work-around, I plan to disable the "drag and select" feature of my > program (sigh). Can anyone suggest a method to work around this problem > short of rebuilding the MFC dll's? Instead of doing the TrackRubberBand in response to the message, post yourself a message to tell yourself to do the work. > PS: Whiile tracking this down, I also uncovered a minor defect in the > compiler itself (it should have generated a warning message for the > following "typographic error" in my code > > CWnd *pChild = m_pChildControl; > UINT ID = CWnd->GetDlgCtrlID(); // <- {I'm sure I didn't really type > that, [blush]} > > which called GetDlgCtrlID for the CFormView rather than for pChild, but > shouldn't have called anything at all! There was a thread, long ago, about this. Mike B. told me in Email that he had reported the bug (it's a parser error, and you would see that the line there would have generated no code). Good luck! Brad -- class CBradWilson : public CWorldWatchProgrammingTeam { public: void GetInetAddr ( CString& s ) { s = "bradw@exptech.com"; } void GetE164Addr ( CString& s ) { s = "+1 (810) 620-9803"; } void GetURL ( CString& s ) { s = "http://www.exptech.com"; } void GetDisclaimer( CString& s ) { s = "All I say is fact :-p"; } }; // QOTW: "Music nowadays is merely the art of executing difficulties and in // the end that which is only difficult ceases to please."
Dale Wilson -- dale@dra.com Friday, February 23, 1996 A couple of weeks ago (my how time flies) I wrote about a bug in COccManager::IsDialogMessage which resulted in an ASSERT out of MFC. The problem appears when MFC mismanages a temporary CWind returned by GetFocus(). Brad (thanks, Brad) suggested this work around.... > Instead of doing the TrackRubberBand in response to the message, post > yourself a message to tell yourself to do the work. At first I thought that this wouldn't work because the posted message goes thru IsDialogMessage and would trigger the same problem. Upon further reflection I realized that by the time the posted message was processed, focus would have been given to the FormView for which MFC has a permanent CWnd, and therefore the work around would work. However, there is always the possibility that some other message could trigger the same symptom, so I wasn't very comfortable with this approach. Fortunately, I have developed both a reliable way to reproduce the problem, and a "solid" work around, so I no longer need to hold my breath while demoing this work-in-progress. [The reason two weeks have elapsed since my last message is we had a significant dog-and-pony show today [which went quite well, thank you] and I didn't have time to send this]. So, here is how you reproduce the problem, and my work-around... Recreating the problem: 1) Run appwizard with all defaults except: a) enable OLE Control containment and b) use CFormView 2) Convert the application to UNICODE (may not be necessary). 3) Edit the dialog template for the form view to add two controls: a) an edit control b) your choice of OLE Control (circle works fine) DO NOT ATTACH THE EDIT CONTROL TO THE FORM VIEW! If you do then there will be a permanent CWnd for the Edit control and GetFocus will return that rather than creating a temporary CWnd. Note that the OLE Control is necessary to force the trip through COccManager::IsDialogMessage. 4) Add a WM_LBUTTONDOWN handler containing the following code: void CJunqueView::OnLButtonDown(UINT nFlags, CPoint point) { CRect rBox(point,CSize(0,0)); CRectTracker Tracker(&rBox, CRectTracker::dottedLine); Tracker.TrackRubberBand(this, rBox.TopLeft()); } 5) Build and run 6) Click on the edit control to give it focus. 7) LButton down outside both controls. Drag the tracker box around. LButton up ----ASSERT HAPPENS---- The work around: Add a CWinApp::PreTranslateMessage override containing: BOOL CJunqueApp::PreTranslateMessage(MSG* pMsg) { // The following is a workaround for a bug in MFC 4.0 static BOOL BeenHere = FALSE; HWND hFocus = ::GetFocus(); CWnd* pFocus = NULL; if(hFocus != NULL) { pFocus = CWnd::FromHandlePermanent( hFocus ); if(pFocus == NULL) { // Make a permanent handle pFocus = new CWnd; pFocus->Attach(hFocus); if(! BeenHere) { TRACE(_T("WARNING: WORKAROUND FOR MFC 4.0 BUG IN PLACE\n")); TRACE(_T("IS THIS STILL NECESSARY?\n")); TRACE(_T("Attach permanent CWnd %lX to %lX\n"), (DWORD)(void*)pFocus, (DWORD)hFocus); } } else { // found a permanent CWnd. // forget it, all is well. pFocus = NULL; } } BOOL bResult = CWinApp::PreTranslateMessage(pMsg); if(pFocus != NULL) { // If we attached it... // detach it if(! BeenHere) { TRACE(_T(" Detach permanent CWnd %lX from %lX\n"), (DWORD)(void*)pFocus, (DWORD)pFocus->m_hWnd); BeenHere = TRUE; } pFocus->Detach(); delete pFocus; } return bResult; } ---------------------- dale@dra.com
| Вернуться в корень Архива |