'MFC'에 해당되는 글 5건
- 2010.04.08 [TIP] VC++ 6.0에서 스택(STACK)의 크기 설정 3
- 2010.04.04 WM_PAINT 메시지에서 처리되는 무효화의 이해 3
- 2010.04.03 윈도우 프로시저에서 메시지 박스를 띄울 때 런타임 에러가 발생하는 이유
- 2010.04.03 CMainFrame과 CView의 이벤트 드리븐 차이점 1
- 2010.04.03 다이얼로그 타이틀바의 클릭 & 드래그하여 이동하는 기능을 막는 방법
하지만, 툴에서 설정된 기본 스택의 크기는 1MB로 제한되어 있습니다.
이것은 컴파일러 옵션을 통해 스택의 크기를 설정해 줄 수 있습니다.
[Project]-[Project Settings]-[Link] 탭에서 설정하시면 됩니다.
위에 빨간색 동그라미 에디트 윈도우에 할당할 크기를 직접 주시면 됩니다.
Written by Simhyeon, Choe
예전에 데브피아에서 어떤 유저가 기고하였던 질문이 있었는데, 거기에 대한 저의 답변입니다.
-------------------------------------------------------------------------------------
(환경 : VS 2005 MFC)
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch( message )
{
case WM_PAINT:
// 페인트 메시지에서 메박을 띄웠습니다.
MessageBox(_T("이상하다"), _T("case WM_PAINT:"), MB_OK);
// 그런데 여기서 실제로 이 메박은 뜨지 않습니다. 쾅쾅거리는 소리만 요란하게 납니다.
// 0.1초 쯤 아주 조금의 시간이 지난 뒤에 실행창은 뜹니다
// 그런데 실행창이 투명처리가 되어 나타납니다.
// 그리고 컴터가 다운은 아니지만 다운 비슷함니다. 다른 메시지가 전혀 먹히지 않습니다.
return 0; // 이런 이유가 무엇인가요?
}
return CFrameWnd::WindowProc(message, wParam, lParam);
}
-------------------------------------------------------------------------------------
원인이 발생하는 가장 큰 이유는 무효화후에 유효화가 일어나지 않기 때문에
커널단에서 무한하게 WM_PAINT 메시지를 발생시키는 겁니다.
다시 설명을 드리자면, WM_PAINT 메시지는 무효화가 일어났을 때 즉,
다시 그려야 할 경우에 발생하는 메시지입니다.
여기서 다시 그려야 할 경우라면 바로 메시지 박스를 의미하겠지요.
메시지 박스를 다시 그려라고 WM_PAINT 메시지를 주었는데, 그곳에는
어디에도 메시지 박스를 다시 그리는 작업(유효화)하는 부분이 존재
하지 않습니다.
님께선 이런 의문을 가지시겠지요.
case WM_PAINT:
MessageBox(_T("이상하다"), _T("case WM_PAINT:"), MB_OK);
return 0;
위의 코드처럼 WM_PAINT에서 MessageBox()를 호출하게 하였는데 무슨 소리냐..고요.
중요한 것은 그리기 작업을 하기 위한 DC를 발급받지 않은 상태에서 어떤 호출은
커널상에서 무시가 된다는 것입니다.
그러니까 커널 내부적으로 무효화된 부분이 다시 그려졌는지 확인해보고
그려져 있지 않으니까 또다시 호출하고 이런 과정이 무수히 반복되는 것입니다.
위의 코드가 님이 의도하신대로 제대로 동작하려면 두가지 방법으로 처리해야 합니다.
CDC *pDC;
PAINTSTRUCT ps;
case WM_PAINT:
pDC = BeginPaint( &ps );
MessageBox(_T("이상하다"), _T("case WM_PAINT:"), MB_OK);
EndPaint( &ps );
return 0;
와 같이 처리하시면 무효화가 발생할 때마다 메시지 박스가 정상적으로 호출될 겁니다.
혹은 부모 클래스 CFrameWnd의 WindowProc()으로 디폴트로 처리해 주신다면
님의 의도대로 호출될 수 있습니다.
case WM_PAINT:
MessageBox(_T("이상하다"), _T("case WM_PAINT:"), MB_OK);
break;
부모 클래스 CFrameWnd의 WindowProc()를 호출해주면 거기에서
다시 DefWindowProc()을 호출하게 되는데요.
DefWindowProc()에서 BeginPaint()와 EndPaint()처리를 기본적으로 해주게 됩니다.
※ WM_PAINT메시지는 Queued Message지만, WM_PAINT 하나의 메시지만 큐에 저장됩니다.
Written By Sim-Hyeon, Choe
다음의 예제를 한번 살펴 봅시다.
--------------------------------------------------------------------------------
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
① MessageBox( _T("error"), _T("error"), MB_OK ); // 여기서는 런타임에러가 난다.
switch( message )
{
case WM_NCLBUTTONDOWN :
// 여기서는 에러없이 잘된다.
② MessageBox( _T("success"), _T("success"), MB_OK );
break;
.......
}
}
--------------------------------------------------------------------------------
1번과 2번은 어떤 차이가 있을까요?
WindowProc()이 호출될 시점을 생각해 봅시다. 당연히 모든 이벤트가 발생할 때마다
WindowProc()가 호출되겠지요. 그중에서도 최초 CMainFrame 클래스의 윈도우를 생성
중인 시점이라면 WM_CREATE 메시지가 발생합니다. WM_CREATE 메시지는 윈도우를 만들
고 있는중에 처리할 수 있는 메시지입니다. 문제의 핵심은 이것입니다. 윈도우가 아직
만들어진 상태가 아니라는 점입니다. 부모 윈도우인 CMainFrame 윈도우가 아직 만들어
지지 않았기 때문에 부모 윈도우(CMainFrame)의 핸들값이 유효하지 않습니다. 그 유효
하지 않은 핸들값을 참조하는 일종의 자식 윈도우인 MessageBox()가 부모 윈도우의 핸
들값을 참조하기 때문에 런타임에 에러가 발생하는 것입니다. (MFC의 MessageBox()는
내부적으로 부모 윈도우의 핸들을 참조합니다) switch()문 밖에 있는 MessageBox()와
특정한 메시지에 위치한 MessageBox()는 그러한 차이가 있습니다. 제가 이렇게 확신해서
말씀드릴 수 있는 이유가 만약에 우리가 원하는 의도대로 MessageBox()가 호출될 수 있
다면 정신없이 무수히 많은 메시지 박스가 팝업될텐데 WM_CREATE 메시지를 처리하기전에
그런 경우는 절대 발생할 수 없기 때문입니다.
Written By Sim-Hyeon, Choe
다음의 예제를 살펴 봅시다.
---------------------------------------------------------------------------------
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch( message )
{
case WM_LBUTTONDOWN:
// 실행창 아무 곳에서 마우스 왼쪽 버튼을 눌러도 아무 반응이 없다.
// 메시지 박스가 뜨지 않는다. 에러도 없다.
MessageBox(_T("LRESULT CMainFrame::WindowProc(msg, wParam, lParam)"),
_T("SUCCEEDED"), MB_OK);
break;
}
}
LRESULT CmyView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch( message )
{
case WM_LBUTTONDOWN:
// 이것은 에러없이 메시지 박스가 잘 뜬다.
MessageBox(_T("void CmyView::OnLButtonDown(UINT nFlags, CPoint point)"),
_T("SUCCEEDED"), MB_OK);
break;
}
return CView::WindowProc(message, wParam, lParam);
}
---------------------------------------------------------------------------------
똑같은 함수의 재정의인데 CMainFrame::WindowProc( ... )에서는 안되고
CmyView::WindowProc( ... )에서는 가능한 이유는 무엇일까요? 이것은 당연합니다.
CMainFrame은 오직 Non-Client 영역에 대한 메시지만 받을 수 있습니다.
Non-Client 영역이라고 함은 캡션바(타이틀)과 메뉴바에 대한 영역의 메시지만 받을 수 있습니다.
그러니까 CMainFrame에서는 WM_LBUTTONDOWN이 아니라, WM_NCLBUTTONDOWN이라고 해야 캡
션바나 메뉴바를 클릭했을 때 이벤트를 받을 수 있습니다. 클라이언트 영역을 아무리 클릭해봐야 실제로
CMainFrame에서 클라이언트 영역(작업 영역 : 흰색 윈도우 부분)을 포함하지만, 자신의 영역이 CMyView
클래스 윈도우 영역이 가려져 있기 때문에 이벤트가 발생하지 않는 겁니다. Z-ORDER가 CMyView 클래스
윈도우 영역이 더 높기 때문입니다. 그러니까 쉽게 말해서 CMainFrame의 클라이언트 영역이 존재하지만,
그 위에 CMyView 윈도우 영역으로 덮혀져 있기 때문에 메시지를 받을 수 없는 겁니다. 반면에 CMyView
클래스 윈도우 영역에서는 Non-Client 영역에 대한 메시지를 받을 수 없지요. 왜냐하면 그 크기가 흰색 영
역만을 포함하기 때문입니다.
Written By Sim-Hyeon, Choe
두개의 NON CLIENT MESSAGE를 함께 처리해야 하기 때문에 WindowProc 오버라이딩합니다.
[CLASS WIZARD] -> [Class Info] TAB -> 메시지 필터 (Chlid Window)로 선택하면
WindowProc 프로시저를 오버라이딩이 가능합니다.
LRESULT CCantMoveDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
static bFlag = FALSE;
switch( message )
{
case WM_NCLBUTTONDOWN: // 넌 클라이언트 영역(캡션바)을 클릭했다면
bFlag = TRUE;
case WM_NCMOUSEMOVE: // 넌 클라이언트 영역을 클릭하고 있는 상태에서 이동한다면
if( bFlag )
{
bFlag = FALSE;
return TRUE; // 디폴트 윈도우 프로시저로 넘기지 않고 정상적인 처리로 리턴
}
return 0;
}
return CDialog::WindowProc(message, wParam, lParam);
}
Written By Sim-Hyeon, Choe