[WINAPI] Terminate Process Nicely(?) └ C/C++

<일단, 이 글은 사용자가 직접 새로운 프로세스를 생성/실행 시켜 해당 프로세스의 pid 등을 알고 있다는 전제하에 작성>

유닉스/리눅스 계열에서는 kill() 함수에 의해서 프로그램을 강제 종료 시킬 수 있다.

윈도우즈는 어떨까?

만일 프로그램으로 하여금 일반적으로 윈도우창의 X버튼을 눌렀을 때와 같이 종료를 시키고자 한다면,
해당 프로그램에게 WM_CLOSE 메시지를 날리는 방법이 가장 안전할 것이다.

하면 어떻게 WM_CLOSE메시지를 날릴 수 있을까?

EnumWindows() 함수를 이용해서 현재 실행중인 윈도우즈를 조회하는 방법이 있다.

아래는 간단한 샘플 코드이다.

BOOL CALLBACK EnumWindowProc( HWND hWnd, LPARAM lParam )
{	
	if( GetParent( hWnd ) == NULL ) {		
		DWORD pid = 0;		
		GetWindowThreadProcessId( hWnd, &pid );		
		if( pid == (DWORD)lParam )	{			
			PostMessage( hWnd, WM_CLOSE, 0, 0 );			
			  //PostMessage( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );		
		}	
	}	
       return TRUE;
}

void KillProcess( PROCESS_INFORMATION* pInfo ){	
	EnumWindows( EnumWindowProc, pInfo->dwProcessId );
	if( WaitForSingleObject( pInfo->hProcess, 2000 ) != WAIT_OBJECT_0 )	{
		///> 2sec timeout
TerminateProcess ( pInfo->hProcess, 0 );
	}
}

윈도우들을 조회하면서 동일한 PID를 갖는 모든 hWnd에 WM_CLOSE 메시지를 보내는 것이다.

일단, 왠만한 상황에서는 이 코드가 정상적으로 동작하는 것을 확인했다.

EnumWindows() 밑에 부분은 2초동안 프로세스 종료를 기다려보고, 

그래도 종료가 안되었다면 응답없음으로 판단하고 강제 종료 시키는 부분이다.

단, TerminateProcess의 경우 DLL에 의해 관리되는 전역 데이터 상태가 손상될 수 있고, 

DLL들에게 detaching notify가 안날라가는 등, 추후 문제의 소지가 될 수 있다.


*** 추가적으로 위 코드가  TerminateProcess를 타는 몇가지 경우가 있었는데...

가장 치명적이었던 경우는 바로  VLC로 영상을 재생 중에 종료를 시도할 때였다

재생 중이 아니라면 정상 종료되었으나 재생 중에는 위 방법을 사용했다가, VLC가 Hang이 걸리는 문제가 발생했다.

보통 하나의 프로세스 내에 여러 개의 hWnd가 존재할 수 있는데 그 중 하나가 WM_CLOSE 메시지를 받고

그대로 Blocking 상태가 되어버린 것으로 추측하고 있다.

하드 코딩으로 여러개의 hWnd 중 주 윈도우 핸들 ( 실질적으로 윈도우 메시지를 처리하는 중심 윈도우 ? ) 에게만

WM_CLOSE 메시지를 보내줄 때는 정상적으로 종료가 됨을 확인했다.

문제는 그 주 Window 핸들을 특정지을만한 방법이 우리에게는 없다는 것이다.

몇가지 편법으로 IsWindowVisible() 이나, GetWindow( hWnd, GW_CHILD ) 함수를 사용해서

화면에 보이고 있거나, Child Window가 존재하는 녀석이 주 윈도우라고 판단하고 WM_CLOSE 메시지를 보내도록 해보기도 했다.

일단 VLC의 경우에 한해서는 성공적이었으나, 이게 모든 case를 커버할 것이라고는 생각하지 않는다.


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


그 다음으로 찾은 방법은 WindowsXP Pro 이상에서만 가능한 방법인데

taskkill 유틸리티를 이용하는 방법이다.

taskkill 유틸리티는 유닉스/리눅스 계열의 kill 커멘드와 유사하다고 보면된다.

다만, 직접적으로 함수로 있는게 아닌 cmd 유틸리티이므로

CreateProcess() 나 ShellExecute() 계열의 함수를 이용해서 실행해 주면 된다.

커멘드는 : [ taskkill /im 프로그램이름 ] 과 같이 사용하면 된다.

가령, vlc를 종료한다고 하면, "taskkill /im vlc.exe" 가 된다.

한가지 단점은, 화면에 떠있는 모든 vlc 로 종료 시그널이 날라가기 때문에

특정 vlc만 죽이고 싶을 때는 좋은 방법이 아니다.




덧글

댓글 입력 영역