2010. 7. 20. 09:06

Full Design CMainFrame의 구현

제목 정하기가 참 어려운데,
Full Design CMainFrame의 구현으로 정하였습니다.

본 포스트는 다음 내용을 포함하고 있습니다.
  • CMainFrame을 Full로 Design하고 싶다. (이는 Caption Bar, NC 영역등이 없다는 뜻)
  • Resize, System Menu를 지원하고 싶다.
  • 기타 등등등...

위와 같은 상황을 개발할때의 주요 이슈는 다음과 같습니다.

  • Caption Bar를 제거하고 싶다.
    • WS_CAPTION을 Off하면 된다.
    • (발생하는 문제점) : Caption Bar의 System Menu를 처리해줘야 한다. (필요시)
    • (발생하는 문제점) : 전체화면(SW_MAXIMIZE/SC_MAXIMIZE)시에 작업표시줄을 덮어버림
  • OS마다 두깨가 다른 못생긴 Resize NC 영역을 제거하고 싶다.
    • WS_THICKFRAME을 Off하면 된다.
    • (발생하는 문제점) : CMainFrame의 Resize를 지원하지 못한다.

WS_CAPTION을 Off하면 다음과 같은 Thick Frame Border의 윈도우가 생성됩니다.

XP (WS_CAPTION 제외)

XP (WS_CAPTION 제외)

             
VISTA (WS_CAPTION 제외)

VISTA (WS_CAPTION 제외)


위 부분의 경계선(Resizible Border)은 CMainFrame의 ClientRect가 아닌 NC 영역입니다.
(즉, GetClientRect(...)가 아닌 GetWindowRect(...))
그리고, 이때 작업표시줄의 오른쪽 클릭하여 System Menu를 띄우고, "전체 화면"을 실행하면,
작업표시줄을 덮어 버리는 현상이 발생합니다.
나름, WS_CAPTION에서 "전체 화면"의 Special한 코드가 있지 않을까?... 반문해 보기도 합니다.
(그 Special한 코드란,... 뭐... 작업표시줄 덮지 않기, 듀얼 모니터에 어느쪽으로 Max할지 결정,... 입니다)

위 무식한 경계선 제거를 위해 WS_THICKFRAME을 제거하면 되지만,
순수 백색의 ClientRect만 존재하는 Resize가 지원되지 않는 CMainFrame이 만들어 집니다.

보통 일반적인 경우,
이러한 문제를 해결하기 위해, WS_CAPTION만 제거하고,
OnNcPaint()에서 Thick Frame Border를 Repaint하는것을 추천하고 있습니다.
그러면, OnNcPaint()와 OnPaint() 두개를 구현해야 하는 문제가 발생합니다.
즉, NC영역과 Client영역의 경계를 넘나드는 Bitmap을 Draw해야 할때는,... 참으로 난감한 상황이 발생합니다.
그리고, Vista와 XP의 Thick Frame의 두께가 달라서, 구현이 힘든 경우도 발생합니다.

따라서, WS_CAPTION, WS_THICKFRAME을 제거하고 문제점을 쉽게 해결하는 방법을 공유합니다.
뭐... 막코드가 될 수 있으니, 최적하는 필요할지는 모르겠습니다.

제가 알려주는 해결 방안은 대략 다음과 같습니다.
  • Resize의 Full 구현
  • System Menu의 Display & 처리
  • Maximize 될때 작업표시줄을 덮는 현상을 막기 위해,
    다음을 구현하는데, 이때 잠시 WS_CAPTION이 발생하는데, 자연스러움을 위히 NcPaint()를
    Ignore 하는 전략입니다. 이렇게 하는 이유는 듀얼 모니터등 복잡한 Maximize의 구현이 힘들기
    때문입니다. 즉, OS에서 구현한 것을 제가 하는데 무리가 있는 것이죠. 그래서, WS_CAPTION을 넣어서
    OS에서 처리하도록 하고, 다시 WS_CAPTION을 제거하는 것이죠.
    • Maximize 호출 받았을때, WS_CAPTION을 강제 부여
    • Maximize 처리함
    • Maximize 종료되었을때 WS_CAPTION을 강제 제거

그리고, 당신이 처리해야 하는 부분은 다음과 같습니다.

  • CMainFrame의 Full Design (즉, OnPaint()/OnSize()에서 직접 Boder를 그려야 함)
  • CMainFrame의 Caption Bar (즉, OnPaint()/OnSize()에서 직접 Caption Bar를 그려야 함)

우선, 다음의 멤버 변수와 #define을 추가하세요.

#define	GAP_RESIZE_PT								5
#define GAP_RESIZE_PT_BOTTOMRIGHT					10

typedef	INT	TYPE_RESIZE_POS;
#define		TYPE_RESIZE_POS_UNKNOWN					0
#define		TYPE_RESIZE_POS_LEFTTOP					1
#define		TYPE_RESIZE_POS_RIGHTTOP				2
#define		TYPE_RESIZE_POS_RIGHTBOTTOM				3
#define		TYPE_RESIZE_POS_LEFTBOTTOM				4
#define		TYPE_RESIZE_POS_LEFT					11
#define		TYPE_RESIZE_POS_RIGHT					12
#define		TYPE_RESIZE_POS_TOP						13
#define		TYPE_RESIZE_POS_BOTTOM					14

그리고, 다음과 같이 CMainFrame의 멤버 변수를 추가하고 초기화 하세요.
class CMainFrame : public CFrameWnd
{
...
private:
	INT						m_nCountResize;
	BOOL					m_bSizeMouseCapture;
	CRect					m_cRectMaximize;
	BOOL					m_bRecalcMaximizeDone;
	BOOL					m_bInMaximizeSize;
	LPTSTR					m_lpszCursorType;
	LPTSTR					m_lpszTmp;
	TYPE_RESIZE_POS			m_nResizePos;
	BOOL					m_bTitleMouseDown;
	CPoint					m_ptMouseDown;
	CSize					m_cSizeTmp;
	CRect					m_cRectTmp;
	CRect					m_cRectTmp2;
	CRect					m_cRectTmp3;
	BOOL					m_bInMaximize;

CMainFrame::CMainFrame()
{
...
	m_bSizeMouseCapture				= FALSE;
	m_bTitleMouseDown				= FALSE;
	m_ptMouseDown.x					= 0;
	m_ptMouseDown.y					= 0;
	m_bInMaximize					= FALSE;
	m_cRectMaximize.left			= 0;
	m_cRectMaximize.right			= 0;
	m_cRectMaximize.top				= 0;
	m_cRectMaximize.bottom			= 0;
	m_bInMaximizeSize				= FALSE;
	m_nCountResize					= 0;
	m_lpszCursorType				= NULL;
	m_nResizePos					= TYPE_RESIZE_POS_UNKNOWN;

그다음, WS_CAPTION / WS_THICKFRAME 속성을 제거합니다.
물론, CAppUtil::IsCustomCaptionBar() 함수는 설정을 위해 존재하며, TRUE를 리턴하는 Static 함수 입니다.
그리고, CAppUtil::GetHeightCustomCaptionBar() 함수는 Custom할 Caption Bar의 높이(px)를 리턴하는 Static 함수 입니다.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
...
	if (TRUE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom...
		// 다음 Window 속성을 제거한다.
		ModifyStyle(WS_CAPTION, 0, SWP_FRAMECHANGED);
		ModifyStyle(WS_THICKFRAME, 0, SWP_FRAMECHANGED);
	}

OnSize를 수정하여 Maximize 등의 처리를 하십시요.
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
	CFrameWnd::OnSize(nType, cx, cy);
...
	if (TRUE == CAppUtil::IsCustomCaptionBar())
	{
		// 창 크기 상태를 저장한다.
		if (SIZE_MAXIMIZED == nType)
		{
			m_bInMaximize = TRUE;
		}
		else if (SIZE_RESTORED == nType)
		{
			m_bInMaximize = FALSE;
		}
	}
...
	// memdc 이용하여 GetDC()의 Background의 정보를 멤버 Bitmap에 저장
	// 해당 Background Bitmap은 OnPaint()에서 그리고,
	// 내부의 CWnd(ex, CEdit)가 있다면, Resize의 Flicker(깜박임)의
	// 제거를 위해, 해당 CWnd의 OnEraseBkgrnd()를 TRUE 리턴하고,
	// DrawItem() 혹은 OnPaint()에 해당 Background 정보를 통해 Client Rect를 Draw하면,
	// CMainFrame의 내부 CWnd를 Flicker없이 손쉽게 구현할 수 있다.
...
	if (TRUE == CAppUtil::IsCustomCaptionBar())
	{
		if (SIZE_MAXIMIZED == nType)
		{
 			ModifyStyle(WS_CAPTION, 0, SWP_FRAMECHANGED);

			GetWindowRect(&m_cRectMaximize);
			if (FALSE == m_bRecalcMaximizeDone)
			{
				m_cRectMaximize.left += (GetSystemMetrics(SM_CXDLGFRAME));
				m_cRectMaximize.right -= (GetSystemMetrics(SM_CXDLGFRAME));
				m_cRectMaximize.top += (GetSystemMetrics(SM_CYDLGFRAME));
				m_cRectMaximize.bottom -= (GetSystemMetrics(SM_CYDLGFRAME));
				m_bRecalcMaximizeDone = TRUE;
			}

			MoveWindow(m_cRectMaximize.left, 
					   m_cRectMaximize.top, 
					   m_cRectMaximize.Width(), 
					   m_cRectMaximize.Height(),
					   TRUE);
		}
	}

다음과 같이 Message Handler를 추가합니다.
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom할때만 아래 코드 활성화~!!!
		goto FINAL;
	}

	if (point.y < CAppUtil::GetHeightCustomCaptionBar())
	{
		// 이동~
		m_bTitleMouseDown = TRUE;
		SetCapture();
		m_ptMouseDown = point;
	}

	if (TRUE == m_bInMaximize)
	{
		// 최대화 상태인 경우, 사이즈 조정관련인 아래 코드 실행하지 말자~
		goto FINAL;
	}

	GetClientRect(m_cRectTmp);
	m_lpszCursorType = CAppUtil::GetResizeMouseCursor(&m_cRectTmp, &point, &m_nResizePos);
	if (NULL != m_lpszCursorType)
	{
		// NULL이 아니라고 한다면, 일단 모퉁이에 있다라는 뜻~

		// Mouse 추적~
		SetCapture();

		// Capture되었음
		m_bSizeMouseCapture = TRUE;

		// 마우스 Cursor를 변경한다.
		SetCursor(AfxGetApp()->LoadStandardCursor(m_lpszCursorType));
	}

FINAL:
	CFrameWnd::OnLButtonDown(nFlags, point);
}

void CMainFrame::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom할때만 아래 코드 활성화~!!!
		goto FINAL;
	}

	// 마우스를 떼면...
	// 눌렸을때, 설정했던 내용들을 초기화~~~
	m_lpszCursorType	= NULL;
	m_nResizePos		= TYPE_RESIZE_POS_UNKNOWN;
	m_bTitleMouseDown	= FALSE;
	m_bSizeMouseCapture = FALSE;
	ReleaseCapture();

FINAL:
	CFrameWnd::OnLButtonUp(nFlags, point);
}

void CMainFrame::OnMouseMove(UINT nFlags, CPoint point)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom할때만 아래 코드 활성화~!!!
		goto FINAL;
	}

	if (TRUE == m_bInMaximize)
	{
		// 최대화 상태인 경우, 사이즈 조정 없다.
		goto FINAL;
	}

	// 윈도우의 크기를 구한다.
	GetClientRect(m_cRectTmp);

	// 마우스 Cursor 설정
	if ((NULL == m_lpszCursorType) && (FALSE == m_bTitleMouseDown))
	{
		m_lpszTmp = CAppUtil::GetResizeMouseCursor(&m_cRectTmp, &point, NULL);
		if (NULL != m_lpszTmp)
		{
			// NULL이 아니라고 한다면, 일단 모퉁이에 있다라는 뜻~

			// 마우스 Cursor를 변경한다.
			SetCursor(AfxGetApp()->LoadStandardCursor(m_lpszTmp));
		}
	}

	if (TRUE == m_bSizeMouseCapture)
	{
		// Resizing 중~
		if (0 != (m_nCountResize++%3))
		{
			// Resizing 메시지 3번중 한번만 처리한다.
			// 너무 많이 처리하면, Resizing하는 동안의 UI가 굼뜬다.
			goto FINAL;
		}
		
		if (TYPE_RESIZE_POS_RIGHT == m_nResizePos)
		{
			// 사이즈 조정 : →
			SetWindowPos(NULL, m_cRectTmp.left, m_cRectTmp.top, point.x, m_cRectTmp.bottom, SWP_NOMOVE|SWP_NOZORDER);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_LEFT == m_nResizePos)
		{
			// WindowRect를 구해야만 MoveWindow 가능하다.
			GetWindowRect(m_cRectTmp2);

			// 크기 보정
			m_cRectTmp2.left += point.x;

			// 지원 크기보다 작아지면, 막는다.
			if (m_cRectTmp2.Width() < m_stLayoutInfo.nxMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.left = m_cRectTmp3.right - m_stLayoutInfo.nxMinWindowSizeCalc;
			}

			// 사이즈 조정 : ←
			MoveWindow(m_cRectTmp2.left, m_cRectTmp2.top, m_cRectTmp2.Width(), m_cRectTmp2.Height(), 1);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_BOTTOM == m_nResizePos)
		{
			// 사이즈 조정 : ↓
			SetWindowPos(NULL, m_cRectTmp.left, m_cRectTmp.top, m_cRectTmp.right, point.y, SWP_NOMOVE|SWP_NOZORDER);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_TOP == m_nResizePos)
		{
			// WindowRect를 구해야만 MoveWindow 가능하다.
			GetWindowRect(m_cRectTmp2);

			// 크기 보정
			m_cRectTmp2.top += point.y;

			// 지원 크기보다 작아지면, 막는다.
			if (m_cRectTmp2.Height() < m_stLayoutInfo.nyMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.top = m_cRectTmp3.bottom - m_stLayoutInfo.nyMinWindowSizeCalc;
			}

			// 사이즈 조정 : ←
			MoveWindow(m_cRectTmp2.left, m_cRectTmp2.top, m_cRectTmp2.Width(), m_cRectTmp2.Height(), 1);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_RIGHTBOTTOM == m_nResizePos)
		{
			// 사이즈 조정 : →↓
			SetWindowPos(NULL, m_cRectTmp.left, m_cRectTmp.top, point.x, point.y, SWP_NOMOVE|SWP_NOZORDER);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_RIGHTTOP == m_nResizePos)
		{
			// 사이즈 조정 : →↑
			// WindowRect를 구해야만 MoveWindow 가능하다.
			GetWindowRect(m_cRectTmp2);

			// 크기 보정
			m_cRectTmp2.right = m_cRectTmp2.left + point.x;
			m_cRectTmp2.top += point.y;

			// 지원 크기보다 작아지면, 막는다.
			if (m_cRectTmp2.Height() < m_stLayoutInfo.nyMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.top = m_cRectTmp3.bottom - m_stLayoutInfo.nyMinWindowSizeCalc;
			}

			MoveWindow(m_cRectTmp2.left, m_cRectTmp2.top, m_cRectTmp2.Width(), m_cRectTmp2.Height(), 1);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_LEFTTOP == m_nResizePos)
		{
			// 사이즈 조정 : ←↑
			// WindowRect를 구해야만 MoveWindow 가능하다.
			GetWindowRect(m_cRectTmp2);

			// 크기 보정
			m_cRectTmp2.left += point.x;
			m_cRectTmp2.top += point.y;

			if (m_cRectTmp2.Height() < m_stLayoutInfo.nyMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.top = m_cRectTmp3.bottom - m_stLayoutInfo.nyMinWindowSizeCalc;
			}

			if (m_cRectTmp2.Width() < m_stLayoutInfo.nxMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.left = m_cRectTmp3.right - m_stLayoutInfo.nxMinWindowSizeCalc;
			}

			// 사이즈 조정 : ←↑
			MoveWindow(m_cRectTmp2.left, m_cRectTmp2.top, m_cRectTmp2.Width(), m_cRectTmp2.Height(), 1);
			goto FINAL;
		}

		if (TYPE_RESIZE_POS_LEFTBOTTOM == m_nResizePos)
		{
			// 사이즈 조정 : ←↓
			// WindowRect를 구해야만 MoveWindow 가능하다.
			GetWindowRect(m_cRectTmp2);

			// 크기 보정
			m_cRectTmp2.left += point.x;
			m_cRectTmp2.bottom = m_cRectTmp2.top + point.y;

			// 지원 크기보다 작아지면, 막는다.
			if (m_cRectTmp2.Width() < m_stLayoutInfo.nxMinWindowSizeCalc)
			{
				// 보정값 이전으로 원복
				GetWindowRect(m_cRectTmp3);
				m_cRectTmp2.left = m_cRectTmp3.right - m_stLayoutInfo.nxMinWindowSizeCalc;
			}

			MoveWindow(m_cRectTmp2.left, m_cRectTmp2.top, m_cRectTmp2.Width(), m_cRectTmp2.Height(), 1);
			goto FINAL;

		}
	}

	// 창이동이 Resize보다 우선권이 낮다.
	if (TRUE == m_bTitleMouseDown)
	{
		// CpationBar Drag하여 이동중~
		m_cSizeTmp = m_ptMouseDown - point;
		GetWindowRect(&m_cRectTmp);
		m_cRectTmp = m_cSizeTmp - m_cRectTmp;
		SetWindowPos(NULL, m_cRectTmp.left, m_cRectTmp.top, m_cRectTmp.right, m_cRectTmp.bottom, SWP_NOSIZE|SWP_NOZORDER);
		goto FINAL;
	}

FINAL: 
	CFrameWnd::OnMouseMove(nFlags, point);
}

void CMainFrame::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom할때만 아래 코드 활성화~!!!
		goto FINAL;
	}

	if (point.y < CAppUtil::GetHeightCustomCaptionBar())
	{
		if (FALSE == m_bInMaximize)
		{
			/*ShowWindow(SW_MAXIMIZE);*/
			PostMessage(WM_SYSCOMMAND, SC_MAXIMIZE);
		}
		else
		{
			PostMessage(WM_SYSCOMMAND, SC_RESTORE);
		}
	}

FINAL:
	CFrameWnd::OnLButtonDblClk(nFlags, point);
}

void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		goto FINAL;
	}

	if ((SC_MAXIMIZE == nID) ||
		(((SC_RESTORE == nID) && (TRUE == m_bInMaximize))))
	{
		// MAXIMIZE 명령기거나,
		// MAXIMIZE -> MINIMIZE -> RESTORE
		// 인 경우,...

		// 최대화 표시를 해야 하는데,
		// CustomCaptionBar인 상태(즉, WS_CAPTION, WS_THICKFRAME이 없음)
		// 에서는 작업 관리자까지 덮어 버린다.
		// 따라서, 최대화 표시를 할때,

		// WS_CAPTION 속성 강제 부여 ->
		// MAXIMIZE 처리 ->
		//					CFrameWnd::OnSysCommand() -> 
		//					CFrameWnd::OnSize()
		//					WS_CAPTION 강제 제거 ->
		//					BORDER 만큼 크기가 삐져 나오므로, BORDER 크기 조정후 MoveWindow(...) ->

		// 이렇게 하면, 윈도우의 기본 Maximize를 지원 받으면서, 처리할 수 있다.
		// 물론, WS_CAPTION을 부여한 동안 TitleBar 표시가 발생할 수 있으나,
		// OnNcPaint()에서 Nc영역(TitleBar 포함)을 Draw하지 않도록 했으므로,
		// 표시되지 않는다.
		m_bRecalcMaximizeDone = FALSE;
		m_bInMaximizeSize = TRUE;
		ModifyStyle(0, WS_CAPTION, SWP_FRAMECHANGED);
	}
	else
	{
		m_bInMaximizeSize = FALSE;
	}

FINAL:
	CFrameWnd::OnSysCommand(nID, lParam);
}

void CMainFrame::OnNcPaint()
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// CaptionBar Custom이 아닌 경우 (즉, WS_CAPTION이 있는 경우)
		// 에는 Nc 영역 Paint를 처리한다.
		Default();
	}
	else
	{
		// CaptionBar Custom인 경우 (즉, WS_CAPTION이 없는 경우)
		// 에는 Nc 영역 Paint를 처리하지 않는다.
		// do nothing,...
	}
}
void CMainFrame::OnRButtonUp(UINT nFlags, CPoint point)
{
	if (FALSE == CAppUtil::IsCustomCaptionBar())
	{
		// Caption Bar를 Custom할때만 아래 코드 활성화~!!!
		goto FINAL;
	}

	if (point.y < CAppUtil::GetHeightCustomCaptionBar())
	{
		// System 메뉴를 띄운다.
		CAppUtil::TrackPopupSystemMenu(this, NULL);
	}

FINAL:
	CFrameWnd::OnRButtonUp(nFlags, point);
}

그리고, 다음의 Utility 함수를 추가하세요.
class CAppUtil
{
...
public:
	static BOOL					IsCustomCaptionBar(VOID);
	static INT					GetHeightCustomCaptionBar(VOID);
	static LPTSTR				GetResizeMouseCursor(IN RECT* pstRect, IN LPPOINT ppt, OPTIONAL OUT TYPE_RESIZE_POS *pnPos);
	static VOID					TrackPopupSystemMenu(IN CWnd* pcWnd, OPTIONAL IN LPPOINT ppt);

// WS_CAPTION을 사용하지 않고, Custom 하는가?
BOOL CAppUtil::IsCustomCaptionBar(VOID)
{
	// 다음을 동적으로 구해올 수 있습니다...
	// 중간에 변경되는 경우, CMainFrame을 InvalidateRect()해주면 동적으로 변경됩니다.
	return TRUE;
}

// Caption, Resizing을 지원하는 경우일떄, Custom Caption Bar Height를 구한다.
INT CAppUtil::GetHeightCustomCaptionBar(VOID)
{
	return 25;
}

// NULL, IDC_SIZEWE, IDC_SIZENS, IDC_SIZENESW, IDC_SIZENWSE
LPTSTR CAppUtil::GetResizeMouseCursor(IN RECT* pstRect, IN LPPOINT ppt, OPTIONAL OUT TYPE_RESIZE_POS *pnPos)
{
	LPTSTR lpszRtnValue = NULL;

	if ((NULL == pstRect) || (NULL == ppt))
	{
		lpszRtnValue = NULL;
		goto FINAL;
	}

	if (NULL != pnPos)
	{
		*pnPos = TYPE_RESIZE_POS_UNKNOWN;
	}

	// 대각선 부터 먼저 검사~
	if (((pstRect->right - GAP_RESIZE_PT_BOTTOMRIGHT < ppt->x) && (ppt->x < pstRect->right)) &&
		((pstRect->bottom- GAP_RESIZE_PT_BOTTOMRIGHT < ppt->y) && (ppt->y < pstRect->bottom)))
	{
		lpszRtnValue = IDC_SIZENWSE;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_RIGHTBOTTOM;
		}
		goto FINAL;
	}

	if (((pstRect->right - GAP_RESIZE_PT < ppt->x) && (ppt->x < pstRect->right)) &&
		((pstRect->top					 < ppt->y) && (ppt->y < pstRect->top + GAP_RESIZE_PT)))
	{
		lpszRtnValue = IDC_SIZENESW;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_RIGHTTOP;
		}
		goto FINAL;
	}

	if (((pstRect->left					 < ppt->x) && (ppt->x < pstRect->left + GAP_RESIZE_PT)) &&
		((pstRect->top					 < ppt->y) && (ppt->y < pstRect->top + GAP_RESIZE_PT)))
	{
		lpszRtnValue = IDC_SIZENWSE;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_LEFTTOP;
		}
		goto FINAL;
	}

	if (((pstRect->left					 < ppt->x) && (ppt->x < pstRect->left + GAP_RESIZE_PT)) &&
		((pstRect->bottom- GAP_RESIZE_PT < ppt->y) && (ppt->y < pstRect->bottom)))
	{
		lpszRtnValue = IDC_SIZENESW;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_LEFTBOTTOM;
		}
		goto FINAL;
	}

	// 가로...
	if ((pstRect->right - GAP_RESIZE_PT < ppt->x) && (ppt->x < pstRect->right))
	{
		lpszRtnValue = IDC_SIZEWE;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_RIGHT;
		}
		goto FINAL;
	}

	if ((pstRect->left					 < ppt->x) && (ppt->x < pstRect->left + GAP_RESIZE_PT))
	{
		lpszRtnValue = IDC_SIZEWE;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_LEFT;
		}
		goto FINAL;
	}

	// 세로
	if ((pstRect->top					 < ppt->y) && (ppt->y < pstRect->top + GAP_RESIZE_PT))
	{
		lpszRtnValue = IDC_SIZENS;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_TOP;
		}
		goto FINAL;
	}

	if ((pstRect->bottom- GAP_RESIZE_PT < ppt->y) && (ppt->y < pstRect->bottom))
	{
		lpszRtnValue = IDC_SIZENS;
		if (NULL != pnPos)
		{
			*pnPos = TYPE_RESIZE_POS_BOTTOM;
		}
		goto FINAL;
	}

FINAL:
	return lpszRtnValue;
}

VOID CAppUtil::TrackPopupSystemMenu(IN CWnd* pcWnd, OPTIONAL IN LPPOINT ppt)
{
	CMenu			*pcSystemMenu	= NULL;
	MENUITEMINFO	stInfo			= {0,};
	UINT			nID				= 0;
	CPoint			ptPoint;

	if ((NULL == pcWnd) || (NULL == pcWnd->GetSafeHwnd()))
	{
		return;
	}

	pcSystemMenu = pcWnd->GetSystemMenu(FALSE);
	if (NULL != pcSystemMenu)
	{
		// Windows Title을 사용할때에는
		// 자동으로 창의 최대화나 아닐때,
		// 시스템 메뉴 항목이 enable/disable이 자동으로 된다.

		// 수동으로 시스템 메뉴를 띄우려면,
		// 명시적으로 해줘야 한다.

		// 닫기를 Default로,...
		pcSystemMenu->SetDefaultItem(SC_CLOSE);

		if (TRUE == pcWnd->IsZoomed())
		{
			// 창이 최대화 되어 있을때에는,
			// MOVE,SIZE,MAXIMIZE를 dialble~
			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_MOVE, &stInfo);
			stInfo.fState = MFS_DISABLED;
			pcSystemMenu->SetMenuItemInfo(SC_MOVE, &stInfo);

			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_SIZE, &stInfo);
			stInfo.fState = MFS_DISABLED;
			pcSystemMenu->SetMenuItemInfo(SC_SIZE, &stInfo);

			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_MAXIMIZE, &stInfo);
			stInfo.fState = MFS_DISABLED;
			pcSystemMenu->SetMenuItemInfo(SC_MAXIMIZE, &stInfo);

			// Enable 할것
			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_RESTORE, &stInfo);
			stInfo.fState = MFS_ENABLED;
			pcSystemMenu->SetMenuItemInfo(SC_RESTORE, &stInfo);
		}
		else
		{
			// 창이 Maximize되지 않았을때에는,
			// RESTORE를 disable~ (minimize되었을때에는 창을 통해 조정 불가)
			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_RESTORE, &stInfo);
			stInfo.fState = MFS_DISABLED;
			pcSystemMenu->SetMenuItemInfo(SC_RESTORE, &stInfo);

			// Enable 할것
			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_MOVE, &stInfo);
			stInfo.fState = MFS_ENABLED;
			pcSystemMenu->SetMenuItemInfo(SC_MOVE, &stInfo);

			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_SIZE, &stInfo);
			stInfo.fState = MFS_ENABLED;
			pcSystemMenu->SetMenuItemInfo(SC_SIZE, &stInfo);

			stInfo.cbSize = sizeof(stInfo);
			stInfo.fMask = MIIM_STATE;
			pcSystemMenu->GetMenuItemInfo(SC_MAXIMIZE, &stInfo);
			stInfo.fState = MFS_ENABLED;
			pcSystemMenu->SetMenuItemInfo(SC_MAXIMIZE, &stInfo);
		}

		if (NULL == ppt)
		{
			GetCursorPos(&ptPoint);
		}
		else
		{
			ptPoint = (*ppt);
		}

		// 스스템 메뉴를 띄운다.
		nID = pcSystemMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, 
										   ptPoint.x, 
										   ptPoint.y, 
										   pcWnd);

		// 실행
		if (0 != nID)
		{
			pcWnd->PostMessage(WM_SYSCOMMAND, nID);
		}
	}
}