이번 예제는 아주 간단한 프로그램으로,
단순하게 + 버튼을 누르면 숫자가 증가하고, - 버튼을 누르면 숫자가 감소하는 프로그램입니다.
본 예제는 학습을 위하여 SubClassDlgItem과 Timer를 활용하여 구현되었습니다.
// SampleDlg.h
class CSampleDlg : public CDialogEx
{
// 중략..
afx_msg BOOL OnCommand(WPARAM wParam, LPARAM lParam);
afx_msg void OnTimer(UINT_PTR nIDEvent);
public:
int m_nCount;
BOOL m_threadFlagPlus;
BOOL m_threadFlagMinus;
CWinThread * m_Thread;
void UpdateCount();
BOOL DestroyThread(void);
protected:
CButtonSubClass m_ButtonPlus;
CButtonSubClass m_ButtonMinus;
};
UINT ThreadProc(LPVOID lpParam);
void ThreadDelay(DWORD secTime);
헤더 파일을 살펴보면 일단 숫자 증가를 위한 int 변수가 하나 보이고,
Thread 관련 함수 및 변수들, Timer 관련 함수(OnTimer)가 보입니다.
마지막으로 CButtonSubClass라는 클래스 객체가 보입니다.
다음은 SampleDlg에 대한 소스코드 파일 일부 입니다.
// SampleDlg.cpp
BOOL CSampleDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// CButtonSubClass 객체를 연결
m_ButtonPlus.SubclassDlgItem(IDC_BTN_PLUS, this);
m_ButtonMinus.SubclassDlgItem(IDC_BTN_MINUS, this);
return TRUE;
}
이전 CButtonSubClass 객체로 생성된 변수 m_ButtonPlus, m_ButtonMinus에 SubclassDlgItem이란 함수가 사용되었습니다.
일반적으로 특정 리소스를 가져오기 위해 우리는 GetDlgItem을 사용하는데, SubclassDlgItem은 무엇인지 아래 ButtonSubClass 소스 코드를 보며 설명드리겠습니다.
// ButtonSubClass.cpp
#define DEF_BUTTON_DOWN_EVENT_ID (10000)
#define DEF_BUTTON_UP_EVENT_ID (10001)
BEGIN_MESSAGE_MAP(CButtonSubClass, CButton)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
void CButtonSubClass::OnLButtonDown(UINT nFlags, CPoint point)
{
GetParent()->PostMessage(WM_COMMAND, GetDlgCtrlID() + DEF_BUTTON_DOWN_EVENT_ID, (LPARAM)m_hWnd);
CButton::OnLButtonDown(nFlags, point);
}
void CButtonSubClass::OnLButtonUp(UINT nFlags, CPoint point)
{
GetParent()->PostMessage(WM_COMMAND, GetDlgCtrlID() + DEF_BUTTON_UP_EVENT_ID, (LPARAM)m_hWnd);
CButton::OnLButtonUp(nFlags, point);
}
함수는 마우스를 눌렀을 경우, 그리고 누른 마우스를 뗄을 경우에 대한 구현입니다.
이전 코드에서 CButtonSubClass 객체 m_ButtonPlus에서 SubClassDlgItem 함수를 사용했습니다. 그리고 그 인자로 IDC_BTN_PLUS와 this(SampleDlg 클래스) 가 사용되었습니다.
이는 쉽게 말해 IDC_BTN_PLUS 리소스에 대한 메세지는 설정된 외부 클래스(CButtonSubClass)에서 처리하겠다는 의미입니다.
따라서 해당 버튼 이벤트는 ButtonSubCLass에서 구현된 것이죠.
이로서 우리는 CButtonSubClass 객체를 만들어, 플러스 버튼 및 마이너스 버튼에서 사용이 가능합니다.
그리고 그 이벤트는 WM_COMMAND로, 이는 SampleDlg.cpp의 OnCommand 함수를 호출하게 됩니다.
그러나 OnCommand 함수에선 실제로 이 명령이 마우스를 누른 것인지, 또는 뗀 것인지를 모르게 때문에 버튼 이벤트 ID를 설정하였고, 또한 이 것이 마이너스 버튼인지 또는 플러스 버튼인지 구분을 위해 GetDlgCtrlID() 함수를 사용했습니다.
GetDlgCtrlID 함수는 위 코드에서 이전 SubClassDlgItem을 통해 등록하였던 IDC_BTN_PLUS 리소스 값을 반환합니다.
다음으로 마우스 이벤트에 대한 처리부 입니다.
마우스를 클릭했을 때 숫자가 지속적으로 증가하여야 하는데, 사실 본문에선 타이머와 스레드 사용이 굳이 필요하진 않습니다.
그러나 일반적인 상황에서 마우스 클릭 이벤트 처리만을 위한 프로그램은 거의 없기 때문에, 학습 목적으로 사용했습니다.
// SampleDlg.cpp
BOOL CSampleDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (wParam == (IDC_BTN_MINUS + DEF_BUTTON_DOWN_EVENT_ID))
{
SetTimer(ID_TIMER_COUNT_UPDATE, UDPATE_TIMER_INTERVAL, NULL);
m_threadFlagMinus = TRUE;
m_Thread = AfxBeginThread(ThreadProc, this);
}
if (wParam == (IDC_BTN_MINUS + DEF_BUTTON_UP_EVENT_ID))
{
m_threadFlagMinus = FALSE;
DestroyThread();
KillTimer(ID_TIMER_COUNT_UPDATE);
}
if (wParam == (IDC_BTN_PLUS + DEF_BUTTON_DOWN_EVENT_ID))
{
SetTimer(ID_TIMER_COUNT_UPDATE, UDPATE_TIMER_INTERVAL, NULL);
m_threadFlagPlus = TRUE;
m_Thread = AfxBeginThread(ThreadProc, this);
}
if (wParam == (IDC_BTN_PLUS + DEF_BUTTON_UP_EVENT_ID))
{
m_threadFlagPlus = FALSE;
DestroyThread();
KillTimer(ID_TIMER_COUNT_UPDATE);
}
return CDialogEx::OnCommand(wParam, lParam);
}
UINT ThreadProc(LPVOID lpParam)
{
CSampleDlg * pThis = (CSampleDlg*)lpParam;
while (pThis->m_threadFlagPlus || pThis->m_threadFlagMinus)
{
ThreadDelay(THREAD_DELAY_INTERVAL);
if (pThis->m_threadFlagPlus)
{
pThis->m_nCount++;
}
else if (pThis->m_threadFlagMinus)
{
pThis->m_nCount--;
}
}
return 0;
}
void ThreadDelay(DWORD secTime)
{
MSG msg;
DWORD dwStart;
dwStart = GetTickCount();
while (GetTickCount() - dwStart < secTime)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
BOOL CSampleDlg::DestroyThread(void)
{
if (m_Thread != NULL)
{
DWORD dwResult = ::WaitForSingleObject(m_Thread->m_hThread, 60);
if (dwResult == WAIT_TIMEOUT)
{
return FALSE;
}
else if (dwResult == WAIT_OBJECT_0)
{
TerminateThread(m_Thread, dwResult);
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
플러스 또는 마이너스 버튼 마우스 클릭 이벤트가 전달되면, 스레드를 생성합니다.
스레드 내부에 BOOL 변수를 활용하여 해당 스레드 동작이 마이너스로 동작되어야 하는지, 또는 플러스로 동작되어야 하는지를 구분합니다.
당연히 플러스일 경우 Count 값을 증가시키며, 마이너스는 감소시킵니다.
이후 마우스가 떼여진 상황이면 동작 중인 스레드를 종료시키는 단순한 코드입니다.
참고로,
ThreadDelay 함수를 구현한 부분은 일반적으로 윈도우 프로그래밍에서 Sleep보다 자주 사용하는 Delay 함수입니다.
기본적으로 결과는 유사하지만, MFC 또는 윈도우 프로그래밍은 내부적으로 다양한 메세지 처리가 이루어지는데, Sleep 함수는 해당 메세지 처리까지 중단시키기 때문에 위와 같은 코드를 애용합니다.
마지막으로 Timer를 통해 카운팅을 갱신해주는데 Timer 코드는 다음과 같으며, Timer 관련 예제는 별도 포스팅 하겠습니다.
BOOL CSampleDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (wParam == (IDC_BTN_MINUS + DEF_BUTTON_DOWN_EVENT_ID))
{
SetTimer(ID_TIMER_COUNT_UPDATE, UDPATE_TIMER_INTERVAL, NULL);
// 중략..
}
if (wParam == (IDC_BTN_PLUS + DEF_BUTTON_DOWN_EVENT_ID))
{
SetTimer(ID_TIMER_COUNT_UPDATE, UDPATE_TIMER_INTERVAL, NULL);
// 중략..
}
}
void CSampleDlg::OnTimer(UINT_PTR nIDEvent)
{
switch (nIDEvent)
{
case ID_TIMER_COUNT_UPDATE:
UpdateData(FALSE);
break;
}
CDialogEx::OnTimer(nIDEvent);
}
void CSampleDlg::UpdateCount()
{
SetDlgItemInt(IDC_EDIT_COUNT, m_nCount);
}
Visual Studio 2015 (VC 14.0) MFC 기반 프로젝트
'Application' 카테고리의 다른 글
윈도우 필터키 적용 프로그램 (for. 메이플스토리) (0) | 2022.03.08 |
---|---|
파이썬 화면 녹화 프로그램 (0) | 2021.08.30 |