본문 바로가기

3.구현/VC++

Dialog 기반 프로그램에서 OnIdle (WM_IDLE) 구현하기 (WM_KICKIDLE사용)

Dialog 기반 프로그램에서 OnIdle (WM_IDLE) 구현하기 (WM_KICKIDLE사용)

기본적으로 Dialog로 작성된 프로그램은 일반 응용 프로그램의 메시지 루프 처리에 차이로 인해서 WM_IDLE 사용에 문제가 발생한다.

CWnd::RunModalLoop는 CWinThread::PumpMessages(win32의 모달 대화상자 작동형태를 흉내낸 것)를 호출하게 된다. 이는 모달 대화상자가 실제로는 MFC에의해 모달리스 처럼 다뤄지지만 나타나는 형태는 모달로서 작동하게 된다.
이로 인해 일반 WM_IDLE을 사용할 수 없고 WM_ENTERIDLE를 사용하게 된다. 그러나 이는 약간 속임수로 우리가 원하는 IDLE에서 처리를 할 수 없다.
WM_ENTERIDLE은 대화상자가 동작했을 때 작동되며, 대화상자가 종료되면 작동이 중단된다. 그래서 대화상자에 의해서 선택된 내용이 적용되고 싶으나 다음 대화상자를 작동할 때까지 아무 작업도 하지 않은다.

우리가 원하는 IDLE에서 수행하는 메커니즘을 제공하는 것이 WM_KICKIDLE 메시지이다. 이 메시지 핸들러를 작성하고 OnIdle 핸들러를 호출하는 것은 어리석은 짓이며 제대로 작동하지 않을 것이다.

이런 것은 앞에서 WM_ENTERIDLE 메시지를 WindowProc에서 다음과 같이 메시지 큐에 메시지가 없으면 강제적으로 OnKickIdle 핸들러를 호출하게 했으나 메시지 맵 오류가 발생하게 된다.

if(HIWORD(GetQueueStatus(QS_ALLEVENTS)) == 0) { 
  PostMessage(WM_ENTERIDLE, wParam + 1, 0);
}

그럼 WM_KICKIDLE를 추가해보자.

Step 1) 필요한 헤더 파일을 추가하자.

#include <afxpriv.h> // for WM_KICKIDLE declaration

afxpriv.h에 WM_KICKIDLE 메시지에 대한 처리가 들어 있다. 반드시 추가해줘야 한다.

Step 2) 클래스에 메시지 핸들러를 정의 한다.

class CTestDlg : public CDialog
{
  ...
  afx_msg LRESULT OnKickIdle(WPARAM wParam, LPARAM lParam); // 핸들러 추가
}

Step 3) 메시지 맵에 WM_KICKIDLE를 추가한다.

BEGIN_MESSAGE_MAP(CTestDlg , CDialog)
  ...
  ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()

Step 4) 메시지 핸들러를 정의 한다. 그리고 그 안에 내가 갱신하고 싶은 내용을 넣는다.

LRESULT CTestDlg ::OnKickIdle(WPARAM wParam, LPARAM lParam)
{
  switch(lParam) {
  case 2:
     // IDLE에서 처리할 내용
     break;
  case 4:
     // IDLE에서 처리할 내용
     break;
  }

  return (lParam <= 10);
}

여기서 lParam <= 10은 카운터 범위가 0 - 9까지를 말한다. 즉 lParam이 증가가 0 - 9까지 되며 계속 순환하는 형태로 되어 있다. 즉 return이 TRUE이면 계속 증가되고 FALSE이면 다시 0에서 증가하게 된다.

0
1
2 ==> case 2 호출
3
4 ==> case 4 호출
5
6
7
8
9
0 ==> 순환
1
2

if문을 사용해서 짝수만 호출하거나 원하는 순번에 호출할 수 있게 하면된다.
이는 CWinApp의 OnIdle을 호출하는 것이 아니고 CTestDlg의 OnKickIdle을 호출한다.
그렇기에 갱신되는 모든 처리는 CTestDlg에서 이뤄지게 된다.

참조

[1] http://www.codeguru.com/forum/archive/index.php/t-260189.html

반응형