<캡션바가 없는 윈도우의 이동>

캡션바가 없는 윈도우를 만들었습니다. 그런데 이 윈도우를 이동시켜야 하는데 방법을 잘 모르겠습니다. 다른 프로그램들은 캡션바의 위치가 아니더라도 이동이 가능하던데 이러한 프로그램들은 어떠한 방법으로 이동을 시키고 있는지 궁금합니다. 그리고 MFC에서 최상위 윈도우를 생성하는 방법도 알고 싶습니다.

윈도우의 캡션바에서 마우스 버튼이 눌러지면 메시지가 생성되는데, 이와 같은 원리를 이용하면 됩니다. 즉, 임의의 위치에서 마우스가 눌려지게 되면 이러한 메시지를 보내서 윈도우를 속이게 하는 것입니다. 임의의 윈도우 CMyWnd에서 다음과 같은 함수를 추가하면 됩니다.

void CMyWnd::OnLButtonDown(UINT nFlags, CPOint point)
{
     DefWindowProc(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}

또 이외에도 여러 가지 방법이 있는데 WM_NCHITTEST를 사용하는 방법과 WM_SYSCOMMAND를 이용하는 방법이 있습니다. 그리고 일반적인 윈도우의 생성은 Creare라는 함수를 사용하지만 최상위 윈도우의 생성은 CreateEx라는 함수를 사용합니다. 이 함수의 속성을 보면 WS_EX_TOPMOST라는 속성이 있습니다. 이것이 바로 최상위 윈도우로 작용할 수 있도록 하는 것입니다. 자신의 윈도우를 변화시키기 위해서는 PreCreateWindow에서 작업을 하면 됩니다.

BOOL CMyWnd::PreCreateWindow(CREATESTRUCT& cs)
{
     cs.dwExStyle = WS_EX_TOPMOST;
     return CWnd::PreCreateWindow(cs);
}

만일 직접 윈도우를 생성할려면 아래와 같이 하면 됩니다.

BOOL MyFunction()
{
     ...

     CMyWnd* pWnd = new CMyWnd;
     if (FALSE == pWnd->CreateMyWindow("My Top Window"))
     {
          return FALSE;
     }
     return TRUE;
}

BOOL CMyWnd::CreateMyWindow(LPCTSTR lpszWindowName)
{
     LPCTSTR lpszClassName;
     DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_THICKFRAME;

     lpszClassName = AfxRegisterWndClass (CS_HREDRAW | CS_VREDRAW,
          LoadCursor(NULL, IDC_ARROW), (HBRUSH)(::GetStockObject(LTGRAY_BRUSH)),
          NULL);

// 최상위 윈도우를 만들기 위해 Create가 아닌 CreateEx를 썼다.
return CWnd::CreateEx(WS_EX_TOPMOST, lpszClassName, lpszWindowName, dwStyle,
     0, 0, 0, 0, NULL, NULL, NULL);

 

<예외처리란?>

공부를 하다보니 예외 처리라는 생소한 개념이 나왔는데, 이에 대해 예제를 통한 자세한 설명을 부탁드립니다.

예외 처리란 프로그램의 진행중에 만나는 오류를 처리하는 것을 말합니다. 보통은 우리가 if문을 써서 그것들을 방지하곤 했습니다. 예를 들어 두 A, B를 입력받아 A/B한 값을 출력하려고 할 때 B의 값이 0이 되면 나눌 수 없는 오류가 발생합니다. 이것을 막기 위해서 if문으로 b가 0인지를 검사하게 되는 것이지요. 그런데 C++로 가면서 이러한 처리를 보다 편하게 해줄 수 있는 함수가 바로 try(), catch()라는 함수입니다. 예외를 처리하는 절차는 다음과 같습니다.

① 실패를 유발할만한 코드 부분에서 예외를 던진다(throw).
② 그 예외를 해당 예외 핸들러에서 받는다(catch).
③ 해당 예외에 대한 처리를 한다.

try
{
     if(B < 0) throw exception();
     else if(B >= m_nLength) throw exception();
     else if(check_lock(B) == LOCKED)
     throw "Array element is locked";
     else
     {
          // 기타 작업
     }
}

catch(const char* pszErr)
{
     cout << *pszErr << end1;
}

catch(exception e)
{
     cout << e.what() << end1;
}

throw를 할 때는 에러 메시지를 전달할 수도 있습니다. 당연히 catch에서는 인자의 형식에 맞는 catch가 실행됩니다. 예외가 던져지면 만들어진 모든 객체는 적절하게 파괴됩니다. 그럼으로써 프로그램에서 에러 상황이 되는 경우에도 계속 프로그램이 돌아갈 수 있는 것입니다. 그리고 객체를 만들때 예외 처리를 하고자 할 때는 다음과 같습니다.

try
{
     m_nData = new int[nArraySize];
     m_pLogger = new CLog;
}

catch(...)
{
     cout << "Error constructing CSafeArray" << end1;
}

 

 <다이얼로그에서 키값 메시지 처리>

MFC를 이용해 간단한 게임을 만들려고 합니다. 윈도우는 다이얼로그 베이스로 만들었습니다. 그런데 중요한 문제가 발생했습니다. 주인공 캐릭터를 움직여야 하는데 움직이지 않습니다. WM_KEY, WM_KEYUP, WM_KEYDOWN 등 모든 메시지를 걸어봐도 키값이 들어오지를 않습니다. 이럴 땐 어떻게 해야 하는지요. 다이얼로그 베이스 윈도우에서는 원래 키값이 들어오지를 않나요? 그렇다면 게임을 만들 수 없는지요.

물론 다이얼로그 베이스 윈도우로도 게임을 만들 수 있습니다. 윈도우의 다이얼로그박스는 기본적인 컨트롤 컨테이너 역할을 하기 위해서 키값을 사용자에게 넘겨주지 않게 되어 있습니다. 당연히 키값을 받지 않으므로 주인공이 움직이지 않는 것입니다. 다이얼로그 메시지 중 WM_GETDLGCODE라는 메시지가 있습니다. 이 메시지는 다이얼로그가 초기화될 때 프로그램에 메시지를 보내 리턴되는 값으로 키값 및 버튼들의 처리 결정을 설정하게 됩니다. 해당 메시지를 받아서 아래와 같은 설정값을 리턴하게 되면 그 해당하는 작업에 관한 값을 획득할 수 있습니다.

DLGC_BUTTON : 일반적 버튼의 눌림 처리
DLGC_DEFPUSHBUTTON : 기본 버튼 눌림 처리
DLGC_HASSETSEL : 에디트박스의 Set Select 처리. EM_SETSEL 메시지
DLGC_UNDEFPUSHBUTTON : 기본 버튼 이외의 버튼 눌림 처리
DLGC_RADIOBUTTON : 라디오 버튼의 처리
DLGC_STATIC : 정적 컨트롤의 처리
DLGC_WANTALLKEYS : 모든 키값의 반환 처리
DLGC_WANTARROWS : 방향 키값의 반환 처리
DLGC_WANTCHARS : WM_CHAR 메시지
DLGC_WANTMESSAGE : 모든 키 값의 처리. 컨트롤 키값 처리 무시
DLGC_WANTTAB : TAB 키

다이얼로그 베이스 프로그램 중에서 아래와 같이 하면 모든 키의 값을 획득할 수 있습니다.

CMyDlgWnd::OnGetDlgCode
{
     return DLGC_WANTALLKEYS;
}