API or MFC - 03 : File Dialog

[프로그래밍 팁/윈도우 프로그래밍]

우리는 윈도우 프로그래밍을 할 때, 엄청난 빈도로 파일을 열거나 저장한다.
일반적인 워드프로세서나 포토샵, 뮤직플레이어 등을 볼 때,
오히려 파일을 열고 닫는 작업이 없는 프로그램을 찾는 것이 더 힘들 정도이다.

일반적으로 파일을 열 경우 우리는 아래와 같은 다이얼로그를 접하게 된다.


이 다이얼로그를 여는 방법에서 당연히 MFC와 API는 차이가 있다.
아래는 MFC 코드이다.

CFileDialog dlg(TRUE, NULL, NULL, OFN_EXPLORER | OFN_FILEMUSTEXIST, _T("All Files (*.*)|*.*||"), this);
if (dlg.DoModal() == IDOK)
  ::SetDlgItemText(*this, IDC_EDIT, (LPCSTR)dlg.GetPathName());

아래는 API 코드이다.

TCHAR path[MAX_PATH] = _T("");
OPENFILENAME ofn = {0,};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = *this;
ofn.hInstance = ::AfxGetInstanceHandle();
ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0\0");;
ofn.lpstrFile = path;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
if (::GetSaveFileName(&ofn))
    ::SetDlgItemText(*this, IDC_EDIT, path);

MFC는 3줄인데 반해 API는 11줄이나 된다.
두 코드는 정확이 똑같은 모양과 특성의 다이얼로그를 만든다.

물론 그렇다고 위의 상황으로 MFC가 무조건 우월하다는 것은 아니다.
개인적으로 프로젝트 상에서는 후자인 API를 자주 쓴다.
그 이유는 본인의 주 업무가 ATL/COM 관련 프로그래밍이기 때문에
MFC를 쓸 기회가 매우 적기 때문이다.

그러나 MFC를 이용해 테스트 코드를 짜는 빈도는 엄청나게 많다.
새로운 API나 클래스를 쓰거나 라이브러리의 기능 중 의심나는 부분이 있으면
테스트를 위해 어김없이 콘솔 프로젝트 또는 MFC 다이얼로그 프로젝트를 생성한다고 보면 된다.
이 때, 단순 테스트를 위해 후자와 같이 긴 코드를 만드는 것은 상당한 낭비라고 생각한다.
그래서 요즘에는 전자인 MFC 다이얼로그 코드를 많이 쓰게 되었다.

결국 MFC와 API는 우월성 관계 보다는
필요와 효율의 문제라는 것을 알 수 있다.
2007/01/08 00:42 2007/01/08 00:42
TAG. , ,

API or MFC - 02 : 윈도우 Error 처리 (Runtime Error)

[프로그래밍 팁/윈도우 프로그래밍]

앞서 보인 Compile 문제 이외에
런타임 에러 처리에서도 차이를 볼 수 있다.
이것은 유효하지 않은 윈도우 핸들에 대한 런타임 처리에 관한
API와 MFC의 차이 점을 보여 준다.

아래는 MFC 코드이다.

test_wnd_.Invalidate();

아래 코드는 위 함수의 내부 구현이다.

_AFXWIN_INLINE void CWnd::Invalidate(BOOL bErase)
{ ASSERT(::IsWindow(m_hWnd)); ::InvalidateRect(m_hWnd, NULL, bErase); }

윈도우 핸들을 'ASSERT()'로 검사를 하고 있다.
이는 윈도우 핸들이 유효하지 않으면 디버깅 시에 프로그램을 강제 중단 하며,
릴리즈 모드에서는 아무런 검사 없이
API 함수인 'InvalidateRect()'를 호출한다는 것을 의미한다.

여기서 판단해야 할 것은
위 지점에서 반드시 유효한 윈도우 핸들이 존재해야만 하는가에 있다.
위의 코드에서는 선택권이 없으며,
디버깅에서 운 나쁘게 유효하지 않은 'ASSERT()'에 걸린 적 없이 릴리즈가 배포된다면,
사용자는 심각한 시각적 문제를 보게될 수 있다.

그렇다면 우리는 어떤 성택을 해야 할까?
API에서는 두 가지 선택을 할 수 있다.

첫째, 핸들이 유효할 경우에만 'InvalidateRect()'를 호출한려 한다면,

if (::IsWindow(test_wnd_))
test_wnd_.Invalidate(test_wnd_, NULL, FALSE);


위와 같은 검사를 해야할 것이다.
반면에 절대로 핸들은 유효해야하며,
디버깅시 반드시 윈도우 핸들이 유효하지 않은 상황을 모두 제거해야 한다면,

assert(::IsWindow(test_wnd_));
test_wnd_.Invalidate(test_wnd_, NULL, FALSE);

위와 같은 MFC와 유사한 구현을 해야할 것이다.

물론, 확실히 안전한 코드를 위해서라면 둘을 혼합할 수도 있다.
2006/12/07 10:52 2006/12/07 10:52
TAG. , ,

API or MFC - 01 : 윈도우 Error 처리 (Compile Error)

[프로그래밍 팁/윈도우 프로그래밍]

우리가 생성한 차일드 윈도우 또는 다이얼로그의 컨트롤들은
각각의 우리가 지정한 고유한 아이디를 갖는다.
그리고 이 아이디를 통해 우리는 윈도우를 얻을 수 있다.
이 때 존재하지 않는 ID에 대한 컴파일 에러에서 다음과 같은 차이를 보인다.

아래는 MFC 코드이며 'GetDlgItem()'은 "CWnd*'를 반환한다.

GetDlgItem(IDC_BTN_TEST)->SetWindowText(_T("MFC"));

이 때, 'IDC_BTN_TEST'는 존재하지 않는 식별자 이다.
컴파일 에러는 다음과 같다.

e:\Project\CompareMFCandAPI\Err01_NoID\Err01_NoIDDlg.cpp(98) : error C2065: 'IDC_BTN_TEST' : 선언되지 않은 식별자입니다.
e:\Project\CompareMFCandAPI\Err01_NoID\Err01_NoIDDlg.cpp(98) : error C2227: '->SetWindowTextA' 왼쪽은 클래스/구조체/공용 구조체를 가리켜야 합니다.

'IDC_BTN_TEST'가 존재하지 않음으로써 'GetDlgItem()'역시 유효하지 않은 식별자가 되고
연쇄 에러가 발생한 것이다.

아래는 API 코드이며 'GetDlgItem()'은 HWND를 반환한다.

::SetWindowText(::GetDlgItem(*this, IDC_BTN_TEST), _T("API"));

역시, 'IDC_BTN_TEST'는 존재하지 않는 식별자 이다.
컴파일 에러는 다음과 같다.

e:\Project\CompareMFCandAPI\Err01_NoID\Err01_NoIDDlg.cpp(99) : error C2065: 'IDC_BTN_TEST' : 선언되지 않은 식별자입니다.

위에서는 정확하게 문제점만을 가리키고 있다.

포인터에 대한 좀더 많은 참조라던지 같은 유형의 코드가 여러 곳에 산재해 있을 때,
쓸데 없이 많은 에러 메세지는 디버깅을 어렵게 한다는 점에서
윈도우 핸들을 다루어야 하는 동작에서는 API에 장점이 있다고 하겠다.
2006/12/07 10:39 2006/12/07 10:39
TAG. , ,

API or MFC - 00 : Intro

[프로그래밍 팁/윈도우 프로그래밍]
윈도우 프로그래밍을 하면
같은 윈도우 개발자들 사이에서도
MFC와 API 사이에 대해 의견이 분분하다.

물론 둘 모두를 골고루 사용하다 보면
둘 사이에 개발속도와 코딩량 등에 크게 차이가 없다는 것을 알게 될 것이다.
그리고 둘 사이에 균형을 이루어 빠르고 안전한 코드를 이루는 방법을 얻을 수 있을 것이다.

그러나 둘 사이에 차이점이 있다는 것은
C와 C++ 이라는 기본적인 차이만큼이나 명확하며,
어느 쪽을 사용하느냐에 따라 이점과 단점이 있다는 것도 사실이다.

개인적인 성향으로는 API를 분명히 선호한다.
그러나 나 개인의 기호와 선입견에 사로잡혀 아둔한 코드를 만들고 싶은 생각은 없다.
이전에도 API와 MFC의 장단점을 느낄 수 있는 경험이 많이 있었다.
머리가 나빠 그 상황을 기억하지는 못하지만,
그 경험이 지금의 균형감을 유지하는데 상당한 기여를 해 주었다.
뭐, 그래서 이러저러한 이유로
나의 기억을 환기시키고
API와 MFC의 중도를 찾기 위한 앞으로의 경험을
이 글을 보게 될 누군가와 공유하고자 한다.
2006/12/07 10:03 2006/12/07 10:03
TAG. , ,