출처 : http://webdizen.new21.net/blog/2954



1. 셧다운 관련 API 함수


윈도우즈를 종료하거나 현재 사용자를 로그오프 시키기 위해 다음과 같은 Win32 API 함수들이 사용된다.



시스템 셧다운 함수들

  1. BOOL ExitWindows( DWORD dwReserved, UINT uReserved );  
  2. // 2개의 파라메터 모두 예약된 값으로 '0'을 주어야 함.  
  3.  
  4. BOOL ExitWindowsEx( UINT uFlags, DWORD dwReserved );  
  5. // uFlags 파라메터는 셧다운 옵션상수.  
  6. // dwReserved 파라메터는 예약된 값으로 '0'을 주어야 함.  


ExitWindows는 새로운 사용자로 로그온 할 수 있도록 현재 사용자를 로그오프 시킨다.
이 과정이 진행되는 동안 실행중인 모든 응용 프로그램은 종료되며
다른 응용 프로그램을 실행시킬 수 없게된다.

ExitWindowsEx는 다양한 셧다운 작업을 지시할 수 있는데 첫번째 파라메터로 다음과 같은 값들이 사용된다.

EWX_LOGOFF: 현재 사용자를 로그오프시키고 새로운 사용자가 로그온 할 수 있도록 한다.

EWX_SHUTDOWN: 시스템을 종료시킨다.

EWX_REBOOT: 시스템을 종료한 후, 다시 부팅한다.

EWX_POWEROFF: 시스템을 종료한 후, 시스템이 전원을 끄는 기능을 가지고 있을경우 전원을 차단한다.
이상의 옵션들은 추가적으로 다음 상수들과 함께 사용될 수 있다.

EWX_FORCE: 현재 실행중인 응용 프로그램들을 강제로 종료시킨다.

EWX_FORCEIFHUNG: 윈도우즈 NT 5.0 이후 버전에서만 사용.
두 함수 모두 실행중인 모든 응용프로그램들에 대해 WM_QUERYENDSESSION 메시지를 보내게 된다.
주의할 점은 윈도우즈 NT의 경우엔 종료함수를 호출한 응용 프로그램 자신도
이 메시지를 받지만 윈도우즈 95는 그렇지 않다는 것이다.
따라서 윈도우즈 95라면 종료함수를 호출할때 자신의 프로그램은 스스로 종료시켜야 한다.
(어차피 종료되는 거라면 상관없지만 단지 로그오프만 시킬경우엔
로그온 대화창이 떴을 때까지 그대로 남아있게 된다.)

WM_QUERYENDSESSION 메시지를 받은 각 응용 프로그램들은 편집중인 데이터의 저장과 같은
적절한 조치를 취해야 하며 윈도우즈가 종료되어도 좋다면 'TRUE'를 반환하게 된다.
만약 어느 한 프로그램이라도 WM_QUERYENDSESSION 메시지에 대해 'FALSE'를 리턴했다면
윈도우즈는 종료되지 않는다.

반면 EWX_FORCE를 지정할 경우엔 WM_QUERYENDSESSION 메시지를 보내지 않는다.
즉, 실행중인 응용 프로그램에게 종료해도 되는가를 물어보지 않고 강제적으로 시스템을 종료하게 한다.
따라서 EWX_FORCE의 사용은 수정중이던 데이터를 잃어버리게 되므로 신중해야 한다.

참고: WM_QUERYENDSESSION 메시지의 처리
MFC에는 이 메시지를 처리하는 CWnd::OnQueryEndSession 핸들러 함수가 있다.
따라서 이 메시지 핸들러를 작성하고 프로그램이 종료되기 전에 반드시 해야할 일들을 정의해 주면 된다.
그러나 프로그래머의 특별한 코딩이 없더라도 수정중인 문서의 저장과 같은 작업은 이루어진다.
WM_QUERYENDSESSION 메시지를 받을때 MFC 프레임워크의 내부에서는
CFrameWnd::OnQueryEndSession 이 호출되고 거기서 CWinApp::SaveAllModified 함수를
호출하는 식으로 수정된 문서의 저장이 처리된다.
따라서 문서저장과 같은 작업을 코딩해야 한다면 WM_QUERYENDSESSION 메시지 핸들러 보다
CWinApp::SaveAllModified 함수를 오버라이드하여 처리하는게 좋을 것이다.


2. 사용 예제

다음은 시스템을 종료시키는 명령을 메인 프레임에서 처리하는 예이다.
강제로 종료할 것인가는 별도의 체크박스나 체크메뉴를 사용하여 입력받는 것으로 가정한다.


시스템을 종료시키는 코드의 예

  1. // m_bForce 변수는 강제로 종료시킬것인지, 아니면 모든 프로그램에게  
  2. // 시스템을 종료해도 되는가를 물어볼 것인지를 결정하기 위해 사용한다.  
  3.  
  4. // 로그오프 명령의 실행  
  5. void CMainFrame::OnLogoff()   
  6. {  
  7. UINT fuOptions = EWX_LOGOFF;  
  8. if ( m_bForce ) fuOptions |= EWX_FORCE;  
  9.  
  10. ExitWindowsEx( fuOptions, 0 ); // 시스템 종료명령  
  11. SendMessage( WM_CLOSE, 0, 0 ); // Win95라면 자신도 종료해야 함.  
  12. }  
  13.  
  14. // 시스템 종료의 실행  
  15. void CMainFrame::OnShutdown()   
  16. {  
  17. UINT fuOptions = EWX_SHUTDOWN;  
  18. if ( m_bForce ) fuOptions |= EWX_FORCE;  
  19.  
  20. ExitWindowsEx( fuOptions, 0 );  
  21. SendMessage( WM_CLOSE, 0, 0 );  
  22. }  
  23.  
  24. // 시스템 재부팅의 실행  
  25. void CMainFrame::OnReboot()   
  26. {  
  27. UINT fuOptions = EWX_REBOOT;  
  28. if ( m_bForce ) fuOptions |= EWX_FORCE;  
  29.  
  30. ExitWindowsEx( fuOptions, 0 );  
  31. SendMessage( WM_CLOSE, 0, 0 );  
  32. }  
  33.  
  34. // 윈도우즈 NT 라면 ExitWindowEx를 호출한 프로그램의 WM_QUERYENDSESSION 메시지 핸들러에서  
  35. // 다음처럼 한 번 더 실행을 확인할 수 있다.  
  36. BOOL CMainFrame::OnQueryEndSession()   
  37. {  
  38. if (!CFrameWnd::OnQueryEndSession()) return FALSE;  
  39.  
  40. if ( IDYES == MessageBox( "시스템을 종료해도 될까요?", "시스템 종료", MB_YESNO ))  
  41. return TRUE;  
  42. else 
  43. return FALSE; // 'FALSE'의 리턴은 WM_QUERYENDSESSION 메시지의 처리를 중지시킨다.  
  44. }  
  45.  
  46. // ExitWindows 함수의 이용  
  47. void CMainFrame::OnSimple()   
  48. {  
  49. ExitWindows( 0, 0 );  
  50.  
  51. // 이것은 다음과 동일하다.  
  52. // ExitWindowsEx( EWX_LOGOFF, 0 );  
  53. }  



EWX_FORCE 플래그를 테스트할 땐 간단히 메모장 프로그램을 이용하기 바란다.
뭔가 써놓고 저장하지 않은 상태에서 시스템을 종료해 보면 어떻게 다른지를 알 수 있다.


3. 윈도우즈 NT에서는...

시스템을 로그오프시키는 것 이외의 작업을 윈도우즈 NT에서 실행할 경우엔
ExitWindowsEx를 호출하는 프로그램이 SE_SHUTDOWN_NAME 권한을 가져야 한다.
이것에 대해서는 다음 기회로 설명을 미뤄야 하겠지만 (저도 아직 잘 몰라용~)
그 권한을 획득하려면 어떻게 해야 하는가는 알려줄 수 있다.
다음은 VC++ 온라인 도움말에서 따온 것이다.


윈도우즈 NT에서 셧다운 권한 얻기

  1. HANDLE hToken;   
  2. TOKEN_PRIVILEGES tkp;   
  3.  
  4. // Get a token for this process.   
  5.  
  6. if ( !OpenProcessToken( GetCurrentProcess(),   
  7. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))   
  8. error("OpenProcessToken");   
  9.  
  10. // Get the LUID for the shutdown privilege.   
  11.  
  12. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);   
  13.  
  14. tkp.PrivilegeCount = 1; // one privilege to set   
  15. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;   
  16.  
  17. // Get the shutdown privilege for this process.   
  18.  
  19. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);   
  20.  
  21. // Cannot test the return value of AdjustTokenPrivileges.   
  22.  
  23. if (GetLastError() != ERROR_SUCCESS)   
  24. error("AdjustTokenPrivileges");   
  25.  
  26. // Shut down the system and force all applications to close.   
  27.  
  28. if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0))   
  29. error("ExitWindowsEx");   



4. 기타

시스템을 종료하고 다시 부팅하는 수단은 응용 프로그램 설치작업시 많이 이용된다.
프로그램의 복사와 함께 시스템 설정값을 변경했다면 그 값들을 다시 읽어들여야 하기 때문이다.
시스템을 재부팅한 후에도 계속 후속 설치작업이 이루어져야 한다면 특별한 조치를 취해 두어야 한다.
재부팅되는 순간엔 셧다운 되기 전에 어떤 일이 있었는지를 시스템이 알 수는 없지 않은가?

자세한 방법은 딸려서 (그것에 관한 지식이...) 설명할 순 없지만 한 가지 힌트는 생각할 수 있다.
후속작업을 실행하는 프로그램을 윈도우즈 시작메뉴에 넣어두던가 아니면
윈도우즈 디렉토리에 있는 win.ini 파일에 기록해 두는 것이다.

win.ini 파일을 열어보면 'windows' 섹션이 있고 거기에 'run' 이라는 키가 존재한다.
그런 키가 없다면 여러분이 윈도우즈 시작시 실행할 프로그램 실행파일명과 함께 직접 적어두면 된다.
다음처럼...

[windows]
run=c:\NewProgram\setup.exe

[namo님이 알려주신 힌트]
다른 한가지 힌트(응용 프로그램 설치작업시 재부팅 후 작업 기술)는 레지스트리에 수행해야 할 애플리케이션을 기록하는 방법도 있지요. (레지스트리 파일에 쓰는 것이 .ini(화일시스템)에 기록하는 것보다 성능이 빠르답니다.)
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
AND