윈도우 시스템 프로그래밍 강의 – 널널한 개발자 TV
https://www.youtube.com/watch?v=PQ5gqh0p2-c&list=PLXvgR_grOs1ANK0gLpkt6L9v_1xH32caM&index=2
- 프로세스 생성
- WaitForSingleObject 함수에 대해서
- Event 객체를 이용한 프로세스 동기화
- CreateThread() 함수와 자동 업데이트 구현
1. 프로세스 생성
https://docs.microsoft.com/en-us/windows/win32/procthread/creating-processes
# 버튼 1개 만들기
ID : IDC_BUTTON_CreateProcess
캡션 : CreateProcess()
# GetLastError() 에서 반환한 오류 번호에 대한 오류 메시지 보기
도구 – 오류 조회 – 오류 번호 입력
void CProcessSample01Dlg::OnBnClickedButtonCreateprocess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// 또는 _T("notepad.exe")
// 경로를 안적어도 환경 변수를 뒤져서 실행된다.
TCHAR szBuffer[MAX_PATH + _MAX_FNAME] = { L"notepad.exe" };
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
szBuffer, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
CString strTmp;
strTmp.Format(L"CreateProcess failed (%d).", GetLastError());
AfxMessageBox(strTmp);
return;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
# [Win32/MFC] “”, L””, TEXT(“”), _T(“”) 차이점
http://x108zero.blogspot.com/2013/12/text-t-l.html
멀티바이트(MBCS) : 영어는 1바이트, 그 외 문자는 2바이트
유니코드(WBCS) : 모든 문자를 2바이트로 처리
멀티바이트 : “aaaaa”
유니코드 : L”aaaaa”
TEXT(“aaaaa”) 또는 _T(“aaaaa”) 를 쓰면
멀티바이트면 “aaaaa”
유니코드면 L”aaaaa”
으로 자동 변환됨
MFC는 _T(“aaaaa”) 를 써라.
# TCHAR, char, wchar_t
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=dkdaf&logNo=90156336556
TCHAR 매크로는
멀티바이트면 char (1바이트)
유니코드면 wchar_t (2바이트)
로 변환해주는 매크로
2. WaitForSingleObject 함수에 대해서
WaitForSingleObject function
DWORD WaitForSingleObject([in] HANDLE hHandle, [in] DWORD dwMilliseconds);
– HANDLE
1. void * (거의 void * 이다.)
2. 값
– dwMilliseconds
밀리초. 예) 3000 (3초)
– HANDLE (포인터) 가 가리키는 주소의 변화를 감지하는 함수
– non-signaled 상태에서 signaled 상태가 될 때까지 wait 한다. 밀리세컨드만큼
– 함수를 호출하는 순간 호출한 쓰레드는 wait 상태가 된다. 응답없음 상태
void CProcessSample01Dlg::OnBnClickedButtonCreateprocess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
TCHAR szBuffer[MAX_PATH + _MAX_FNAME] = { L"notepad.exe" }; // 또는 _T("notepad.exe") // 경로를 안적어도 환경 변수를 뒤져서 실행된다.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
szBuffer, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
CString strTmp;
strTmp.Format(L"CreateProcess failed (%d).", GetLastError());
AfxMessageBox(strTmp);
return;
}
// Wait until child process exits.
DWORD dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
if (dwResult == WAIT_OBJECT_0) {
AfxMessageBox(L"메모장이 종료됐습니다.");
}
else if (dwResult == WAIT_TIMEOUT) {
AfxMessageBox(L"타임아웃 발생!");
}
else {
AfxMessageBox(L"ERROR: 심각한 오류가 발생했습니다!");
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
3. Event 객체를 이용한 프로세스 동기화
// linux unix : signal
// windows : Event (set, reset)
# 버튼 4개 만들기
ID : IDC_BUTTON_CreateProcess // 1편 강의에서 만든 것
캡션 : CreateProcess()
IDC_BUTTON_CreateEvent
캡션 : CreateEvent()
IDC_BUTTON_WaitEvent
캡션 : Wait Event
IDC_BUTTON_SetEvent
캡션 : Set Event
# CloseHandle(핸들)
- null 체크 안해도 그냥 넘어간다. 널 체크 안해도됨.
- 프로세스가 종료되면 자동으로 핸들을 닫으므로 굳이 안해줘도 된다.
- 그래도 원칙상 CloseHandle(핸들)을 해준다.
// ProcessSample01Dlg.h
protected:
HANDLE m_hEvent = NULL;
// ProcessSample01Dlg.cpp
void CProcessSample01Dlg::OnBnClickedButtonCreateprocess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
TCHAR szBuffer[MAX_PATH + _MAX_FNAME] = { L"notepad.exe" }; // 또는 _T("notepad.exe") // 경로를 안적어도 환경 변수를 뒤져서 실행된다.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
szBuffer, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
CString strTmp;
strTmp.Format(L"CreateProcess failed (%d).", GetLastError());
AfxMessageBox(strTmp);
return;
}
// Wait until child process exits.
DWORD dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
if (dwResult == WAIT_OBJECT_0) {
AfxMessageBox(L"메모장이 종료됐습니다.");
}
else if (dwResult == WAIT_TIMEOUT) {
AfxMessageBox(L"타임아웃 발생!");
}
else {
AfxMessageBox(L"ERROR: 심각한 오류가 발생했습니다!");
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
void CProcessSample01Dlg::OnBnClickedButtonCreateevent()
{
::CloseHandle(m_hEvent); // 널체크 안해도 됨. null 이면 자동으로 넘어감.
m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, L"PROCESS_SYNC_TEST_EVENT"); // 매뉴얼 리셋
if (m_hEvent == NULL)
{
//AfxMessageBox(L"ERROR: Failed to create event object!");
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
m_hEvent = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, L"PROCESS_SYNC_TEST_EVENT");
if (m_hEvent == NULL)
{
AfxMessageBox(L"ERROR: Failed to open event object!");
return;
}
}
return;
}
}
void CProcessSample01Dlg::OnBnClickedButtonWaitevent()
{
if (::WaitForSingleObject(m_hEvent, INFINITE) == WAIT_OBJECT_0)
AfxMessageBox(L"Event가 세트 됐습니다.");
}
void CProcessSample01Dlg::OnBnClickedButtonSetevent()
{
::SetEvent(m_hEvent);
// 어떠한 이유로 한 프로세스가 리셋이벤트를 못받는 경우를 대비. Sleep()은 해결책이 아님
// Event를 하나 더 만들거나 카운터를 만들어서 해결.
::Sleep(300);
::ResetEvent(m_hEvent);
}
4. CreateThread() 함수와 자동 업데이트 구현
# 버튼 3개 만들기
# IDC_BUTTON_CreateUpdateEvent
캡션 : Create Update Event
# IDC_BUTTON_UpdateEventSet
캡션 : Set Update Event
# IDC_BUTTON_WaitUpdateEvent
캡션 : Wait Update Event
# CreateThread 로 쓰레드를 만들고 return 하면 자동으로 종료됨
굳이 Terminate 하지 말고 return 하도록 유도해라.
# 버튼으로 이벤트 생성, Set, Wait 하기
// CProcessSample01Dlg.h
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
HANDLE m_hEventUpdate = NULL;
// CProcessSample01Dlg.cpp
void CProcessSample01Dlg::OnBnClickedButtonCreateupdateevent()
{
::CloseHandle(m_hEventUpdate); // 널체크 안해도 됨. null 이면 자동으로 넘어감.
m_hEventUpdate = ::CreateEvent(NULL, TRUE, FALSE, L"PROCESS_UPDATE_EVENT"); // 매뉴얼 리셋
if (m_hEventUpdate == NULL)
{
//AfxMessageBox(L"ERROR: Failed to create event object!");
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
m_hEventUpdate = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, L"PROCESS_UPDATE_EVENT");
if (m_hEventUpdate == NULL)
{
AfxMessageBox(L"ERROR: Failed to open update event object!");
return;
}
}
return;
}
}
void CProcessSample01Dlg::OnBnClickedButtonUpdateeventset()
{
::SetEvent(m_hEventUpdate);
}
DWORD WINAPI ThreadWaitForUpdate(LPVOID pParam)
{
HANDLE hEvent = pParam;
if (::WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
{
//AfxMessageBox(L"UPDATE EVENT!");
theApp.m_pMainWnd->PostMessage(WM_CLOSE);
}
//AfxMessageBox(L"Thread return");
return 0;
}
void CProcessSample01Dlg::OnBnClickedButtonWaitupdateevent()
{
::CreateThread(NULL, 0, ThreadWaitForUpdate, m_hEventUpdate, 0, NULL);
}
# 자동으로 시작하는 업데이트 구현
DWORD WINAPI ThreadWaitForUpdate(LPVOID pParam)
{
HANDLE hEvent = pParam;
if (::WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
{
//AfxMessageBox(L"UPDATE EVENT!");
theApp.m_pMainWnd->PostMessage(WM_CLOSE);
}
//AfxMessageBox(L"Thread return");
return 0;
}
// 프로그램 시작시 자동 업데이트 실행
BOOL CProcessSample01Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
::CloseHandle(m_hEventUpdate); // 널체크 안해도 됨. null 이면 자동으로 넘어감.
m_hEventUpdate = ::CreateEvent(NULL, TRUE, FALSE, L"PROCESS_UPDATE_EVENT"); // 매뉴얼 리셋
if (m_hEventUpdate == NULL)
{
//AfxMessageBox(L"ERROR: Failed to create event object!");
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
m_hEventUpdate = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, L"PROCESS_UPDATE_EVENT");
if (m_hEventUpdate == NULL)
{
AfxMessageBox(L"ERROR: Failed to open update event object!");
return FALSE;
}
}
return FALSE;
}
::CreateThread(NULL, 0, ThreadWaitForUpdate, m_hEventUpdate, 0, NULL);
return TRUE; // return TRUE unless you set the focus to a control
}