CreateEvent() 사용하기

MFC 2012. 2. 2. 10:59

공부는 역시 상황에 쫓겨서 하는게 가장 효과적이라는.. (..응? 좀 미리 미리 해놓지!!!!)

CreateEvent
(Microsoft Windows CE 3.0)

The CreateEvent function creates a named or an unnamed event object. : 이벤트를 만드는 함수랍니다.

HANDLE CreateEvent( 
  LPSECURITY_ATTRIBUTES lpEventAttributes, 
  BOOL bManualReset, 
  BOOL bInitialState, 
  LPTSTR lpName); 

Parameters

lpEventAttributes : Ignored. Must be NULL. 무시하라는데여.
bManualReset : a manual-reset 이냐 auto-reset event object 이냐를 결정한답니다.
bInitialState : 이벤트 obj의 초기 상태를 결정. 그니까 FALSE를 해야 만들었다가 나중에 set signal 하죠.
lpName : 이벤트 이름
Return 값 : 핸들값이 나옵니다.

그럼 이 함수의 용도와 용법을 알아보면..

CreateEvent(), SetEvnet(), ResetEvent()

1. 함수의 용도

WaitForSingleObject(), WaitForMultipleObjects() 등을 위한 Event Object를 만들고, 그것을 signal 상태 혹은 nonsignal 상태로 만들기 위해 사용됩니다.

2. 기본 용법

1) Event Object 를 생성한다.

HANDLE gv_hEvent;

int gv_operationType;

h = CreateEvent(NULL, TRUE, FALSE, NULL);

2) Thread Function을 만든다.

DWORD WINAPI MyThread(LPVOID lParam)

{

DWORD ret;

while (TRUE)

{

//gv_hEvent 가 set될 때까지 무한정 기다린다.

ret = WaiForSingleObject(gv_hEvent, INFINITE);

if(ret = WAIT_FAIL)

return 0;

}

else if ( ret = WAIT_ABANDONED) {

ResetEvent(gv_hEvent);

continue;

}

else if ( ret = WAIT_TIMEOUT) {

continue;

}

else {

if(gv_operationType==0)

return 0;

//필요한 작업을 하세요

ResetEvent(gv_hEvent);

}

}

}

3) UI 코드에서 Thread 가 작업을 하게 하고 싶다면

gv_operationType = 1;

SetEvent(gv_hEvent);

// --> 이 함수로 인해 gv_hEvent 가 signal 상태로 바뀌고, Thread의 WaitForSingleObject 가 return 된다.

** 참고로,

CreateEvent() 요놈은 네개의 인자를 가지는데,
첫번째는 생성된 핸들을 자식 프로세스가 상속받도록 하겠느냐
두번째는 이벤트를 자동으로 리셋시킬것이냐 아니냐
세번째는 초기값이 시그널이냐 아니냐
네번째는 이벤트의 이름을 스트링으로 주는것
WaitForSingleObject () 에서 이벤트를 기다리다가,
이벤트가 시그널되면 요놈이 리턴할텐데
그러고 나서 이벤트가 자동으로 리셋되기도하고, 변화가 없기도 하다.
그걸 결정하는것이 이벤트를 만들때 CreateEvent() 의 두번째 인자이다.
TRUE 면, 변화가 없고,
FALSE면, 자동으로 Reset된다.
모르고 쓰다가 낭패당하기 쉽상 ^^
예)
HANDLE h = CreateEvent(0,FALSE, TRUE,0); // 1번 요렇게 해보고
HANDLE h = CreateEvent(0,TRUE, TRUE,0); // 2번 요렇게도 해봐라
for(int i = 0; i<5; i++){
WaitForSingleObject(h, 1000);
printf("%d\n",i);
}
결과는 FALSE 인 1번의 경우 자동으로 리셋되기땜시롱 매번 1초씩 있다가 출력.
TRUE 인 2번의 경우 리셋되지 않기때문에 항상 시그널 상태이고, 기다릴것없이 두루룩~~
0,1,2,3,4 를 출력할것이다.
Posted by pkss
,

파일 선택을 위한 클래스가 CFileDialog 이다.

한개의 파일만 선택할 경우

char szFilter[] = "Image (*.BMP, *.GIF, *.JPG) | *.BMP;*.GIF;*.JPG | All Files(*.*)|*.*||";

CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter);

if(IDOK == dlg.DoModal()) {

CString strPathName = dlg.GetPathName();

}

여러개의 파일을 복수 선택할 경우

dwFlagOFN_ALLOWMULTISELECT 설정한다.

선택된 여러 파일 리스트가 저장될 버퍼가 제공되야 한다.

만일 기본 버퍼 이상의 파일을 선택해 확인 버튼을 누른다면,

DoModal을 실행시켰을 때, IDCANCEL이 리턴되고, 파일 리스트가 넘어오지 않을 것이다.

char szFilter[] = "All Files(*.*) | *.* ||";
CFileDialog dlg(TRUE, NULL, NULL, OFN_ALLOWMULTISELECT, szFiilter);

dlg.m_ofn.lpstrInitialDir = _T("D:/"); // 오픈할때 초기 경로 지정
// 여기서 버퍼 크기 늘려줘야 함.

char strFile[4096] = { 0, }; CString fileName;
dlg.m_ofn.lpstrFile = strFile; dlg.m_ofn.lpstrFile = fileName.GetBuffer( 4096 );
dlg.m_ofn.nMaxFile = sizeof(strFile); dlg.m_ofn.nMaxFile = 4096

if (IDOK == dlg.DoModal()) {
for (POSITION pos=dlg.GetStartPosition(); pos != NULL;)
CString
strPathName =
dlg.GetNextPathName(pos);
}

파일 선택 필터

static TCHAR BASED_CODE szFilter[] = 
_T("Chart Files (*.xlc)|*.xlc|") _T("Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|")
_T("*.xlc; *.xls|All Files (*.*)|*.*||");

예제

void CMyClass::OnFileOpen() 
{
// szFilters is a text string that includes two file name filters:
// "*.my" for "MyType Files" and "*.*' for "All Files."
TCHAR szFilters[]= _T("MyType Files (*.my)|*.my|All Files (*.*)|*.*||");

// Create an Open dialog; the default file name extension is ".my".
CFileDialog fileDlg(TRUE, _T("my"), _T("*.my"),
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);

// Display the file dialog. When user clicks OK,

fileDlg.DoModal()

// returns IDOK.
if (fileDlg.DoModal() == IDOK)
{
CString pathName = fileDlg.GetPathName();

// Implement opening and reading file in here.
// Change the window's title to the opened file's title.

CString fileName = fileDlg.GetFileTitle();
SetWindowText(fileName);
}
}

============================================================================

CFileDialog dlgFile(TRUE);


CString fileName;
const int c_cMaxFiles = 100;
const int c_cbBuffSize = (c_cMaxFiles * (MAX_PATH + 1)) + 1;


dlgFile.GetOFN().lpstrFile = fileName.GetBuffer(c_cbBuffSize);
dlgFile.GetOFN().nMaxFile = c_cMaxFiles;

dlgFile.DoModal();


fileName.ReleaseBuffer();

============================================================================

CFileDialog::CFileDialog

explicit CFileDialog( BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = TRUE );

BOOL bOpenFileDialog
생성하는 다이얼로그 박스 타입을 지정해 주는 매개변수
TRUE => 파일열기 다이얼로그 박스
FALSE => 파일 저장 다이얼로그 박스

LPCTSTR lpszDefExt
기본 파일 확장자명(저장할 경우 사용)
만약 유저가 파일이름 상자 안에 확장자를 포함하지 않는 경우
lpszDefExt의 파일 확장자명을 자동으로 지정 함
NULL => 확장자명을 추가하지 않음

lpszFileName
시작할 때 파일이름 상자에 나타낼 이름 지정
NULL => 시작할 때 파일이름을 나타내지 않음


dwFlags
하나이상의 플래그를 사용하여 사용자가 원하는 다이얼로그 박스로 조합

많이 사용되는 플래그
OFN_ALLOWMULTISELECT 한번에 여러개의 파일들을 선택 가능하게 함
OFN_CREATEPROMPT 존재하지 않는 파일명을 입력했을 경우 새로 생성하겠냐는 대화상자 표시
OFN_EXPLOPER 열기나 저장하기를 윈도우 탐색기 스타일로 출력
OFN_FILEMUSTEXIST 기존에 존재하는 파일 이름만 입력할 수 있도록 함
OFN_HIDEREADONLY 읽기전용 파일은 출력하지 않음
OFN_LONGNAMES 긴 파일 이름을 사용할 수 있도록 함
OFN_OVERWRITEPROMPT 저장할려고 하는 파일명이 존재할 경우 덮어쓰겠냐는 대화 상자 표시
OFN_PATHMUSTEXIST 오직 유효한 경로나 파일명만을 입력(아님 경고 메세지 출력)

추가 플래그 참조 :
http://msdn.microsoft.com/ko-kr/library/ms646839(en-us,VS.85).aspx


lpszFilter
사용할 파일들이 걸러지도록 파일명들을 연속으로 나열 함

pParentWnd
부모나 소유자 윈도우의 파일 다이얼로그 박스의 포인터

dwSize
OPENFILENAME 구조체의 크기

Posted by pkss
,

오랫만에 정말 친절한 설명을 찾았다..
그동안 개념이 어리버리했는데 확실히 개념을 잡았다.

^^

출처 :

Threads and Thread synchronization
Threads
MFC는 2종류의 쓰레드로 구분할 수 있다.


1. user interface threads
메시지 루프가 존재한다. 윈도우를 만들고 이들 윈도우로 보내진 메시지들을 처리한다. 어플리케이션안에 또하나의 어플리케이션(ui-threads)을 만드는것과 비슷하다.일반적으로 별개로 움직이는 다중 윈도우를 만들때 많이 사용되어 진다.


2. worker threads
직접적으로 메시지를 받지 않고 백그라운드에서 동작되기 때문에 윈도우나 메시지루프들이 필요가 없다.

%이 둘간의실질적인 차이는 아직 잘모르겠다. 좀 더 학습하도록


-Creating a Worker Thread
AfxBeginThread함수는 ui-thread,worker thread 둘다 쓰인다. MFC프로그램에서 Win32::CreateThread함수를 사용하지 말아라.

ex)
CWinThread* pThread = AfxBeginThread (ThreadFunc, &threadInfo);

UINT ThreadFunc (LPVOID pParam)
{
UINT nIterations = (UINT) pParam;
for (UINT i=0; i<nIterations; i++);
return 0;
}


CWinThread* AfxBeginThread (AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL)

nPriority : 쓰레드 우선순위. 쓰레드들 중에서만 상대적인 우선순위를 정할때 사용한다.
nStackSize : 쓰레드의 스택사이즈 라는데 정확히 뭔지..몰라.
dwCreateFlags : 0 일 경우, 이 함수 호출후 바로 쓰레드가 시작되는 것이고,
CREATE_SUSPENDED일 경우, ResumeThread()가 호출되어야지만 시작되는것이다.
lpSecurityAttrs : 몰라도 되.


--Thread Function prototype
콜백함수이기에 static class 멤버 함수이거나 클래스 밖에 선언된 전역함수이어야 한다.
UINT ThreadFunc( LPVOIDpParam )
pParam : 32비트값. AfxBeginThread 함수의 파라미터. 이것은 scalar,handle,객체포인터로도 사용되어 질수 있다.


-Creating a UI Thread
CWinThread를 상속받은 클래스를 사용한다.

ex)
class CMyThread : public CWinThread

CMyThread * pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread),
THREAD_PRIORITY_NORMAL,
0, // stack size
CREATE_SUSPENDED);
pThread->초기값 설정.
pThread->ResumeThread();


또는

CMyThread * pThread = new CMyThread();
pThread->초기값 설정.
pThread->CreateThread();


-Suspending and Resuming Threads
SuspendThread를 호출하면 쓰레드는 멈추고, 내부적으로 Suspend count 가 1 증가 한다. 그리고 ResumeThread를 호출하면 Suspend count는 1 줄고 쓰레드는 다시 움직인다.
ResumeThread는 자신이 호출 할 수 없다. 그리고 이들 함수의 리턴값은 쓰레드의 이전 Suspend Count이다.


-Putting Threads to sleep
::Sleep(0) (스레드 양보)
현재쓰레드를 중지하고 스케쥴러가 동등한 혹은 높은 우선순위를 갖는 다른 쓰레드를 움직이도록 허락해준다. 만약, 다른 동등한 혹은 높은 우선순위의 쓰레드가 wait 상태가 아니라면, 이 함수는 바로 리턴해서 현재쓰레드를 재시작 한다. (::SwitchToThread.(in NT 4.0 ), ::Sleep(0) (in win32 ))
Sleep함수의 시간은 정확하지 않을 수 있다. 이는 여러환경에 지배받기때문이다. 윈도우즈에서는 쓰레드의 suspend 시간을 보장하는 함수는 존재하지 않는다.



-Terminating a Thread

--Worker Thread
call AfxEndThread.
쓰레드 함수내부의 리턴으로 종료.


--UI Thread
call AfxEndThread.
post WM_QUIT.( ::PostQuitMessage )

(쓰레드 종료함수 사용시 주의할것은 쓰레드 내부에 메모리를 동적할당(new,malloc)해놓고 delete를 안해서 메모리 릭이 날 염려가 있다. 그래서 가급적이면 쓰레드 함수 리턴으로 종료하는 것이 낫다.)

위의 함수들을 호출한 후 한번 제대로 종료됬는지 확인해보라
DWORD dwExitCode;
::GetExitCodeThread (pThread->m_hThread, &dwExitCode);

만약 여전히 살아 있다면 dwExitCode = STILL_ACTIVE(0x103) 으로 되어 있을테다.



- CWinThread 개체 자동 삭제하기

AfxBeginThread 로 스레드 생성할 경우,


CWinThread *pThread = AfxBeginThread( , ,, );


위와 같이 CWinThread 개체 포인터를 반환값으로 받는다. 스레드 종료시, 이 개체는 어떻게 되는가?

MFC는 사용자가 따로 delete pThread; 할 필요없게 자동으로 삭제해 준다.

그런데, 여기서 문제가 있다.

스레드가 종료되지 않았다면, 아래구문은 잘못된 곳이 없다.


::GetExitCodeThread( pThread->m_hThread, &dwExitCode );

( 스레드의 현재 상태값을 반환하는 함수 )


하지만, 종료되어 pThread 개체 포인터 역시 자동으로 삭제되었다면, 위의 구문은 에러를 발생할 것인다.

pThread 는 아무것도 가리 키지 않기 때문에..


해결책)

1. m_bAutoDelete = FALSE; 설정.

-- 이는 곧 사용자가 CWinThread 개체를 delete 해주어야 함을 의미한다.

-- 스레드가 중지가 된 상태에서 m_bAutoDelete = FALSE; 를 설정해주어야 한다.


2. Win32::DuplicateHandle 함수를 호출하여 스레드 핸들의 사본을 생성하여 사용.

--해당핸들의 참조카운트가 1 -> 2로 증가되어 CloseHandle() 호출시에 다시 2 -> 1 로 감소될뿐 개체는 죽지 않고 남아있다. 물론, 사용자가 명시적으로 CloseHandle() 을 호출해야 한다.


- 다른 스레드 종료하기

1.

//Thread A

nContinue = 1;

CWinThread *pThread = AfxBeginThread( ThreadFunc, &nContinue );

...

...

nContinue = 0 ; // 스레드 B 를 종료해라.



//Thread B

UINT ThreadFunc( LPVOID pParam )

{

int* pContinue = (int*) pParam;

while( *pContinue )

{

....

}

return 0;

}



2. 스레드 A는 스레드 B가 죽을때 까지 무한 기다리도록 하고 싶을 경우,


//Thread A

nContinue = 1;

CWinThread *pThread = AfxBeginThread( ThreadFunc, &nContinue );

HANDLE hThread = pThread->m_hThread; //사본 생성. 스레드B종료시 pThread 없을 경우 대비.

...

...

nContinue = 0 ; // 스레드 B 를 종료해라.

::WaitForSingleObject( hThread, INFINITE );



//Thread B

//1.의 에제와 같음



::WaitForSingleObject 은 hThread 스레드가 종료될때 까지 무한정(INFINITE) 기다리는 함수이다. 반환 값은 아래와 같다.

-- WAIT_OBJECT_0 : 그 개체가 신호를 받았다. 즉, 스레드가 종료되었다.

-- WAIT_TIMEOUT : 스레드는 살아있지만, 시간 만료로 기다리지 않고 반환되었다.


두번째 매개 변수를 0 으로 설정하고 아래와 같이 사용하는 것이 좋다.


if( ::WaitForSingleObject( hThread, 0 ) == WAIT_OBJECT_0 )

//스레드가 더이상 존재치 않는다.

else

//스레드가 여전히 실행중이다.



- ::TerminateThread( hThread, 0 );

다른 스레드를 직접삭제하는 방법은 위의 함수 딱 한가지 존재한다. 어쩔 수 없는 경우에만 사용하도록 .



MFC에서 스레드를 사용하기 전에 알아두어야 할 함수는 AfxBeginThread, AfxEndThread 이렇게 두 함수가 있다. 이 함수에 대한 인자값은 MSDN을 참고하기 바라며, 간단한 사용법을 알아보자.

AfxBeginThread(CalcIt, (LPVOID)val);

첫 번째 인자는 전역함수인데, 클래스에 포함시킬 경우 정적함수로 선언해야되고, 아니면 전역함수로 선언해서 써야한다. CalcIt 함수의 리턴값과 파라미터는 아래와 같다.

UINT CalcIt(LPVOID arg);

꼭 위의 규칙을 따라줘야 한다.

일반적으로는 위와 같이 스레드를 시작하면 된다. 그러면 스레드가 시작되고, CalcIt함수(코어 함수??)의 역할이 끝나면 자동으로 스레드를 종료한다.

하지만 스레드는 위와 같이 쓸 경우 바로 리턴해버린다. 리턴값이 0이면 정상적인 종료를 뜻하게 되는데, 사용자에게 의미있는 리턴값을 받기 위해서, 이렇게 하면 안될것이다.

그래서 사용자가 원하는 값을 리턴 받으려면 아래와 같은 방법을 쓴다.

CWinThread* pThread = AfxBeginThread(CalcIt, (LPVOID)val, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
우선 스레드를 시작하지만 정지상태로 놓는다는 뜻이다.(CREATE_SUSPENDED)

그 다음
pThread->m_bAutoDelete = FALSE;
위에서 말했듯이, 스레드가 시작되고 자기 할일이 끝나면 바로 스레드는 종료된다. 하지만 이 값을 FALSE로 해주면 종료되지 않는다. 대신 사용자가 직접 원하는 시점에 종료를 해줘야 한다.(AfxEndThread)

pThread->ResumeThread();
이제 스레드를 시작한다.

그럼 리턴값을 받아야한다.

::GetExitCodeThread(pThread->m_hThread, &returnvalue); //Get ExitCode from thread.

이 함수는 스레드로 부터 리턴값을 읽어온다. 여기에 포함되는 리턴값을 받기 위해선 위의 CalcIt 함수에 AfxEndThread를 써줘야 한다. AfxEndThread함수의 첫 번째 인자는 위 함수의 returnvalue에 들어갈 리턴값이다. 두번째 인자는 스레듣 객체를 메모리에서 제거할 것인지를 나타낸다. 기본값은 TRUE이고 메모리에서 삭제된다. FALSE이면 메모리에 남아있게 되어 스레드 객체를 재사용 할 수 있다.

그리고 마지막으로 알아둬야 할 것이, WaitForSingleObject 함수이다.

역할은 Sleep과 동일하지만, 이 함수의 첫 번째 인자에 있는 스레드가 사용할 수 있게 될 때까지 기다리는 것이다.

Sleep은 스레드를 멈추는 역할밖에 못하지만, WaitForSingleObject는 특정 시간동안 이벤트를 감지할 수 있습니다.

WaitForSingleObject 의 두 번째 인자에는 밀리세컨드 단위의 시간이 들어가는데, INFINITE가 들어가면 스레드가 끝날때까지 기다리게 됩니다.

만약 구현이 된다면 아래와 같은 구조가 될 것이다.

pThread = AfxBeginThread(ExportVVF, &arg1, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); //Make Thread with suspension
pThread->m_bAutoDelete = FALSE; //if this thread will be end, object will not destory this thread.
pThread->ResumeThread(); //Begin Thread
WaitForSingleObject(pThread->m_hThread, INFINITE);
::GetExitCodeThread(pThread->m_hThread, &returnvalue); //Get ExitCode from thread.


써놓고 보니 정신없네;;; 

 출처 : http://www.heart4u.co.kr/tblog/217

Posted by pkss
,