본문 바로가기

Programming/Win32&MFC&COM

SDI(or MDI)에서 View를 포함하는 Dialog 띄울 때 문제점

반응형

SDI 형식에서 View를 포함하는 Dialog를 생성하면 문제점이 발생합니다.

먼저 메뉴를 클릭하면 다이얼로그가 DoModal()을 통해서 생성이 됩니다.

DoModal()로 생성된 다이얼로그 내부에서 CView나 CView를 상속한 뷰를 생성해서 포함시킵니다.

다이얼로그 내부에서 뷰를 컨트롤처럼 추가하는 것입니다.

이렇게 했을 때 다이얼로그의 내부 뷰 영역을 마우스로 클릭하면 문제가 발생합니다.

ASSERT(pParentFrame == pDesktopWnd || pDesktopWnd->IsChild(pParentFrame));

바로 위와같은 ASSERT문에 걸리게 됩니다.

문제의 원인은 다음과 같습니다.

SDI 형태는 MainFrame이 있고 그 안에 View가 들어갑니다.

MainFrame에 바로 붙는 View는 문제가 되지 않는데 새로 띄운 Dialog에 View가 추가되면 문제가 생깁니다.

문제가 생기는 부분은 다음과 같습니다.

int CView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
	int nResult = CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
	if (nResult == MA_NOACTIVATE || nResult == MA_NOACTIVATEANDEAT)
		return nResult;   // frame does not want to activate

	CFrameWnd* pParentFrame = GetParentFrame();
	if (pParentFrame != NULL)
	{
		// eat it if this will cause activation
		ASSERT(pParentFrame == pDesktopWnd || pDesktopWnd->IsChild(pParentFrame));

		// either re-activate the current view, or set this view to be active
		CView* pView = pParentFrame->GetActiveView();
		HWND hWndFocus = ::GetFocus();
		if (pView == this &&
			m_hWnd != hWndFocus && !::IsChild(m_hWnd, hWndFocus))
		{
			// re-activate this view
			OnActivateView(TRUE, this, this);
		}
		else
		{
			// activate this view
			pParentFrame->SetActiveView(this);
		}
	}
	return nResult;
}

MFC에서 제공되는 viewcore.cpp 파일의 일부입니다.

중간 정도에 pParentFrame == pDesktopWnd를 비교하는 ASSERT문이 보입니다.

여기에서 pParentFrame과 pDesktopWnd가 달라서 문제가 발생하게 됩니다.

CMainFrame을 생성할 때의 CMainFrame의 hWnd값은 다음과 같습니다.

실행할 때마다 값은 달라지므로 매번 해당 값을 기억하면 됩니다.

0x000a0a1a라는 값이 표시가 됩니다.

다음으로 메뉴를 클릭했을 때 DoModal()을 통해서 생성되는 다이얼로그의 hWnd값입니다.

위와 같이 0x000e0944라는 값이 출력이 됩니다.

이제 ASSERT가 걸리게 하기 위해서 뷰 영역을 클릭합니다.

ASSERT 경고창이 발생하게 되고 다시 시도(R)을 클릭하면 코드 부분으로 진입합니다.

Watch 창을 통해서 pParentFrame과 pDesktopWnd의 값을 확인하면 다음과 같이 표시됩니다.

결국 pParentFrame은 CMainFrame을, pDesktopWnd는 Dialog인 것을 확인할 수 있습니다.

두 값이 달라서 발생하는 문제로 해결방법은 다음과 같습니다.

DoModal()로 생성되는 Dialog 내부의 View에 WM_MOUSEACTIVATE 메시지 핸들러를 추가합니다.

아래와 같은 코드가 추가됩니다.

int CInnerDlg::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
	// TODO: Add your message handler code here and/or call default

	return CFormView::OnMouseActivate(pDesktopWnd, nHitTest, message);
}

CFormView를 상속받았기 때문에 CFormView::OnMouseActivate()를 호출하고 있습니다.

이 부분의 CFormView를 CWnd로 변경하면 ASSERT가 발생하지 않습니다.

전체 소스 코드는 아래 링크를 참조하시면 됩니다.

SDIViewDialog.zip


반응형