2009년 1월 23일 금요일

[VS 2008] 마지막 에러 코드값 확인


마지막 에러 코드값 확인



 윈도우 함수(이하 API 함수) 중에 ::GetLastError() 함수가 있다.
API 함수가 실패하게 되면 내부적으로 함수를 호출한 스레드의 스레드 지역 저장소에 적절한 에러 코드를 저장해
둔다. 여러 개의 스레드가 동시에 수행될 경우라도 상호간에 영향을 미치지 않고 각 스레드별로 에러 코드를 유지
할 수 있게 된다. 이때 에러 코드를 확인 하는 함수가 ::GetLastError() 함수 이다.

::GetLastError() 함수를 소스 코드 속에 작성하여 확인하는 방법 말고 Visual Studio 2008 도구 자체를 이용해서
확인 할 수도 있다.

1. Visual Studio 내에 포함된 Watch(조사식) 창을 통해 확인


Watch 창을 통해 특정 행을 선택하고 "$err,hr" 을 입력하면 된다.



2. Visual Studio 가 제공하는 Error Lookup 유틸리티 이용








cf) 제프리 리처의 WINDOWS VIA C/C++  - 한빛미디어











2009년 1월 21일 수요일

사용자 정의 메시지 처리기 사용하기

사용자 정의 메시지 처리기 사용하기







사용자가 정의한 메시지를 수신할 수 있는 윈도우는 크게 2가지가 있다.

1.     CWnd 의 파생 클래스

2.     CWinThread의 파생 클래스 : 사용자 인터페이스 클래스(User Interface Class)

 


 



, 윈도우를 출력하는 클래스가 사용자 정의 메시지를 받을 수 있다.

MFC의 계층 구조도 모르고 Document 에서 사용자 정의 메시지 처리기를 만들면 아마 다음과

같은 에러 메시지를 볼 수 있을 것이다.



LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)'()로 변환할 수 없습니다



인터넷을 뒤지면 아마 사용자 정의 메시지 처리기의 리턴값을 LRESULT 로 바꾸면 해결 된다고

하는 글이 가장 많은데  아마 그렇게 변경해 주어도 안되면 CWnd CWinThread의 파생 클래스

가 아닌 곳에서 구현하려 해서 에러가 나는 경우일 것이다.



CMainFrame CView의 파생 클래스 에서 사용자 정의 메시지 처리기를 만들어 주자 



위에서 언급한 사용자 정의 메시지 처리기 함수 리턴값에 대한 에러는

VC6.0 에서 .Net 이상의 컴파일러 사용시 나타나는 문제이다.

.Net 이상부터 MFC 에서는 메시지 처리기의 반환 형식 및 매개변수 형식을 보다 엄격하게

검사한다. 이로써 안전하지 않은 메시지 처리기를 오류 메시지로 플래그를 지정하여 발생할 수

있는 문제를 개발자에게 알린다.

MFC 에서 ON_MESSAGE, ON_REGISTERED_MESSAGE, ON_THREAD_MESSAGE,

ON_REGISTERED_THREAD_MESSAGE 에 대해 정적 캐시팅을 사용한다.



VC6.0 에서 ON_MESSAGE 또는 ON_REGISTERED_MESSAGE 에 대해 메시지 처리기 함수가

void 값을 리턴하였을 경우 .Net 이상에서는 컴파일 에러를 일으킨다.

, 메시지 처리기는 LRESULT 를 리턴해야 한다.







#  CWnd 파생 클래스 에서 사용자 정의 메시지 처리기 사용



1. 사용자 정의 메시지 정의

    const UINT UM_READ_RESULT = WM_USER + 1; 



2. 메시지 맵에 메시지 함수 Overriding

   - 구현파일 (*.cpp)



BEGIN_MESSAGE_MAP(CReadProgressThread, CWinThread)

    ON_MESSAGE(UM_PROGRESS_SET, OnSetProgress)

END_MESSAGE_MAP()


LRESULT CProgressThread::OnSetProgress(WPARAM _wParam, LPARAM _lParam)

{

       ...

}



      

    UM_PROGRESS_SET 사용자 정의 메시지를  OnSetProgress 함수와 연결

    수신 프로그램에 메시지가  도착 하므로 해당하는 메시지 핸들러가 수행 되도록

    메시지 핸들러를 오버라이딩

      

     ※ Message Map

        - 메시지와 메시지 핸들러(함수) 를 연결하는 역할



3. 사용자 정의 메시지 함수 작성

   - 헤더파일 (*.h)        

protected:

    afx_msg LRESULT OnSetProgress(WPARAM _wParam, LPARAM _lParam);

    DECLARE_MESSAGE_MAP()







# CWinThread 파생 클래스 에서 사용자 정의 메시지 처리기 사용



- 사용자 인터페이스 스레드 즉, CWinThread의 파생클래스가 사용자 정의 메시지를 수신 하는

    경우는 Message Map을 연결하는 부분과 메시지 핸들러 원형만 다르다.



1. 사용자 정의 메시지 정의

    const UINT UM_READ_RESULT = WM_USER + 1; 



2. 메시지 맵에 메시지 함수 Overriding

   - 구현파일 (*.cpp)



BEGIN_MESSAGE_MAP(CReadProgressThread, CWinThread)

    ON_THREAD_MESSAGE(UM_PROGRESS_SET, OnSetProgress)

END_MESSAGE_MAP()



void CProgressThread::OnSetProgress(WPARAM _wParam, LPARAM _lParam)

{

       ...

}   





3. 사용자 정의 메시지 함수 작성

   - 헤더파일 (*.h)         

protected:

    afx_msg void OnSetProgress(WPARAM _wParam, LPARAM _lParam);

    DECLARE_MESSAGE_MAP()





 



Update 09.01.22





오늘 사용자 정의 메시지를 사용할 일이 있어서 구현 하는데 잘 안되더라...
정리한 것을 다시 읽고 구현했는데 또 안되더라 -_-;;;
가장 앞줄에 써 있는 "CWnd 의 파생 클래스" 를 그냥 읽고 지나쳐 버려서....

Visual Studio 의 경고, 에러 메시지 역시 자세히 봐야 겠다는 것을 느꼈다.
뭐 하나 그냥 지나칠게 없다는것을 느꼈다.

아무튼! 문제에 대한 접근방법!!  기억하자.

2009년 1월 15일 목요일

[ComboBox] 콤보박스에 문자열 넣는 시점 - AddString()

콤보박스에 문자열 넣는 시점 - AddString()



 콤보박스를 가지고 있는 다이얼로그를 동적으로 생성했다.
다이얼로그를 DoModal()로 띄우기 전에 콤보박스에 문자열을 넣으려고 함수를 호출하여
콤보박스의 컨트롤 변수를 이용하여 문자열을 넣었다.
그랬더니 에러, 메모리 릭 주루루룩~



m_pSetEvtFilterDlg = new CSetEvtFilterDlg;
if (NULL == m_pSetEvtFilterDlg)
{
        MessageBox(_T("대화상자 열기 실패"), _T("오류"), MB_OK | MB_ICONSTOP);
        return;
}

m_pSetEvtFilterDlg->SetEvtFilterDataList(m_pDoc->GetEvtLogNameList(), m_pDoc->GetEvtFilterData());

m_pSetEvtFilterDlg->DoModal();







 원인은 컨트롤 변수의 AddString() 함수를 이용하여 문자열을 넣는 시점이 DoModal() 이전 이여서 그랬었다.
즉, 다이얼로그가 띄어질때 OnInitUpdate() 함수에서 해주어야 한다.

ComboBox 컨트롤 변수를 사용하여 데이터를 다룰 때
ComboBox 컨트롤이 보이는 상태, 즉 다이얼 로그가 출력 중인 상태에서 다루어야 한다.


BOOL CSetEvtFilterDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    INT nLogNameListCount = m_sLogNameList.GetCount();
    POSITION pos = m_sLogNameList.GetHeadPosition();
    for (INT nIndex = 0; nIndex < nLogNameListCount; nIndex++)
    {
        CString sLogName = m_sLogNameList.GetNext(pos);
        m_ctrlLogName.AddString(sLogName);
    }   
    m_ctrlLogName.SetCurSel(0);

    return TRUE;
}





cf )  SetCurSel(처음에 보여줄 아이템의 인덱스);

2009년 1월 14일 수요일

CString::Format %d %u / DWORD UINT

CString::Format %d %u / DWORD  UINT




CString의 Format() 이라는 멤버 함수가 있다.
CString 객체의 문자열 값을 sprintf 를 이용하듯이 C-style 서식 지정을 이용하여 문자열 값을 설정한다.


의심의 여지 없이 아래와 같이 코드를 짰다.

CString  sCount = _T("");

DWORD  dwItem = MAXDWORD;

sCount.Format(_T("아이템 개수 : %d"),  dwItem);



sCount값을 출력 시켰는데  "아이템 개수 : -1" 이라고 찍히더라.
printf()를 사용해본지 하도 오래되어서 무심코 %d를 사용했는데
서식지정자 역시 변환하는 데이터 형에 맞게 넣어 주어야 한다.

%d : signed interger (4Byte  +,- 21억) 10진수 출력
%u : unsigned integer (4Byte  +42억) 10진수 출력







UINT 와 DWORD 의 차이는 뭘까??
MSDN을 봐도 둘다 unsigned의 4바이트 값의 범위를 갖는다.
과연??



UINT
 - Windows Version 3.0 과 3.1 에서는 16bit unsigned integer 로 해석된다.
 - Win32 Version 에서는 32bit unsigned integer 로 해석 된다.

즉, Win32 윈도우 프로그래밍에 있어서 UINT와 DWORD는 동일한 값을 표현 한다고 볼 수 있다.

예전엔 int는 2byte 여서 +,- 3만2천,  long 은 4byte 여서 +,- 21억  의 값의 범위를 갖는다고 배웠었는데..
추억이 되었구나  ^^

2009년 1월 5일 월요일

[ListCtrl] Virtual ListCtrl 새로운 아이템 설정 후 갱신 -SetItemCountEx()


Virtual ListCtrl - 새로운 아이템 설정 후 갱신




: 기존 ListView의 모든 아이템을 삭제하고 새로운 아이템으로 출력하고자 할 때

- SetItemCountEx() 함수 이용


- 예


CListCtrl &ListCtrl = GetListCtrl();

ListCtrl.DeleteAllItems();
ListCtrl.SetRedraw(FALSE);
// 새로운 아이템 데이터 설정
ListCtrl.SetItemCountEx(출력할 아이템의 개수);
ListCtrl.SetRedraw();






SetItemCountEx  참조 : http://six605.tistory.com/215

#