■パソコンをシャットダウンする

ExitWindowsEx 関数を使用するとシャットダウンできるようです、でもしかし、なんだかアプリケーションにSE_SHUTDOWN_NAME特権が必要なようでそう簡単にシャットダウンできそうにありません。

1 必要なアクセス権を持ったアクセストークンを取得
2 アクセストークンのLUIDを取得
3 LUIDで特権を有効可
4 ExitWindowsEx 関数が動くようになる

という面倒な操作が必要になってくるようです。


▼アプリケーションに特権を取得する方法

まずは、アクセストークンを取得
アクセストークンにはプロセスまたはスレッドに関連付けられているユーザー アカウントの ID と特権が入っているそうです。
それを行うのがOpenProcessToken 関数で、プロセスのプライマリ アクセス トークンへのハンドルを取得するそうです。

BOOL OpenProcessToken(HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE pTokenHandle);

必要なアクセス権を設定する項目には、
TOKEN_ADJUST_PRIVILEGES :アクセス トークンの特権を有効または無効にするために必要
TOKEN_QUERY :アクセス トークンのクエリを実行するために必要
もしくは面倒ならこれでも行けます。
TOKEN_ALL_ACCESS :トークンに対して可能なすべてのアクセス権を組み合わせる

次に、特権を使用できるようにしたいのですが、アクセス トークンの特権を取得および調整する関数は 、LUID 型を使用して特権を識別するらしくて、対応したLUID型を取得します。

BOOL LookupPrivilegeValue(CSTR SystemName,CSTR Name,PLUID pLuid);

次に、やっとの事で特権を有効にするためにAdjustTokenPrivileges関数を使用します

BOOL AdjustTokenPrivileges(HANDLE TokenHandle,BOOL DisableAllPrivileges,PTOKEN_PRIVILEGES pNewState,DWORD BufferLength,PTOKEN_PRIVILEGES pPreviousState,DWORD ReturnLength);

そのためにはTOKEN_PRIVILEGES構造体に値をセットして実行しないといけないそうです
typedef struct _TOKEN_PRIVILEGES {
  DWORD               PrivilegeCount;配列内のエントリの数
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
}
必要な値を構造体にセットしてAdjustTokenPrivileges関数を実行します。
GetLastError()でエラーがなければ特権が取得できました。


▼パソコンをシャットダウンするコード

文章に書くとよくわからないので実際のコードで表現します。
実行するとパソコンがシャットダウンします。
HANDLE TokenHandle;
LUID Luid;
TOKEN_PRIVILEGES NewState;

if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&TokenHandle)){
	return -1;
}

if(!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &Luid)){
	return -1;
}

NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = Luid;
NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

AdjustTokenPrivileges(TokenHandle,FALSE,&NewState, 0, (PTOKEN_PRIVILEGES)NULL, 0);

if(GetLastError() != ERROR_SUCCESS){
	return -1;
}

#define EWX_FORCEIFHUNG 0x00000010
//パソコンをシャットダウンする
ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCEIFHUNG, 0);


■ボタンを押すとシャットダウンするアプリケーションを作成


以下のコードをコンパイルして実行するとウインドウが出て中央に広がったボタンを押すとパソコンがシャットダウンします。

#include <windows.h>

#define ID_BUTTON_1 1011
HWND hButton;

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE,int);

char szClassName[]="sample01"; //ウインドウクラス

HINSTANCE hInst;
HWND hWnd;

int WINAPI WinMain(HINSTANCE hCurInst,HINSTANCE hPrevInst,
					LPSTR lpsCmdLine,int nCmdShow ){
	MSG msg;
	BOOL bRet;

	hInst = hCurInst;
	if(!InitApp(hCurInst)) return FALSE;
	if(!InitInstance(hCurInst,nCmdShow)) return FALSE;
	while((bRet = GetMessage(&msg,NULL,0,0)) != 0){
		if(bRet == -1){
			MessageBox(NULL,"GetMessage ERR","Error",MB_OK);
			break;
		}else{
			if(!IsDialogMessage(hWnd,&msg)){//タブキーでコントロールを移動したくない場合はこの行をコメントする
				TranslateMessage(&msg); 
				DispatchMessage(&msg); 
			} 
		}
	}
	return (int)msg.wParam;
}

//ウインドウクラスの登録
ATOM InitApp(HINSTANCE hInst){
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style =CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;//プロ―ジャー名
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInst;//インスタンス
	wc.hIcon = (HICON)LoadImage(NULL,
		MAKEINTRESOURCE(IDI_APPLICATION),
		IMAGE_ICON,
		0,
		0,
		LR_DEFAULTSIZE | LR_SHARED);
	wc.hCursor = (HCURSOR)LoadImage(NULL,
		MAKEINTRESOURCE(IDC_ARROW),
		IMAGE_CURSOR,
		0,
		0,
		LR_DEFAULTSIZE | LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;	//メニュー名
	wc.lpszClassName = (LPCSTR)szClassName;
	wc.hIconSm = (HICON)LoadImage(NULL,
		MAKEINTRESOURCE(IDI_APPLICATION),
		IMAGE_ICON,
		0,
		0,
		LR_DEFAULTSIZE | LR_SHARED);
	return (RegisterClassEx(&wc));
}
//ウインドウの生成
BOOL InitInstance(HINSTANCE hInst,int nCmdShow){
	hWnd = CreateWindow(szClassName,
		"いつでもシャットダウン",
		WS_OVERLAPPEDWINDOW,//ウインドウの種類
		CW_USEDEFAULT,		//x座標
		CW_USEDEFAULT,		//y座標
		300,		//幅
		200,		//高さ
		NULL,	//親ウインドウのハンドル、親を作るときは NULL
		NULL,	//メニューハンドル、クラスメニューを
				//使うときは NULL
		hInst,	//インスタンスハンドル
		NULL);
	if(!hWnd) return FALSE;
	ShowWindow(hWnd,nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}

//描画メソッド
//-------------------------------
//再描画したい時は次のように書く
//InvalidateRect(hWnd, NULL,1);
//UpdateWindow(hWnd);
//-------------------------------
void paint(HWND hWnd){
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd,&ps);

	EndPaint(hWnd,&ps);
}

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp){
	switch(msg){
		case WM_CREATE:
			{
				HWND hDWnd=GetDesktopWindow();//デスクトップのハンドルを取得
				RECT desktop_rect;
				GetWindowRect(hDWnd,&desktop_rect);
				RECT m_rect;
				GetWindowRect(hWnd,&m_rect);//hWndは自分のハンドルで自分のウィンドウサイズを求めている

				int width=(desktop_rect.right-(m_rect.right-m_rect.left))/2;
				int height=(desktop_rect.bottom-(m_rect.bottom-m_rect.top))/2;
				//hWndは自分のハンドル
				SetWindowPos(hWnd,HWND_TOP,width ,height ,(m_rect.right-m_rect.left),(m_rect.bottom-m_rect.top),SWP_SHOWWINDOW);	
				
				RECT rc;
				GetClientRect(hWnd,&rc);//ウインドウの範囲を取得
				//ボタンの生成 画面いっぱいにする
				hButton=CreateWindow("BUTTON", "シャットダウン",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,rc.left,rc.top,rc.right,rc.bottom, hWnd, (HMENU)ID_BUTTON_1, hInst ,NULL);
			}
			break;
		case WM_PAINT:
				RECT rc;
				GetClientRect(hWnd,&rc);//ウインドウの範囲を取得
				SetWindowPos(hButton,HWND_TOP,0 ,0 ,(rc.right-rc.left),(rc.bottom-rc.top),SWP_SHOWWINDOW);
			paint(hWnd);
			break;
		case WM_TIMER:
			return (DefWindowProc(hWnd,msg,wp,lp));//自分と関係のないタイマーは戻す
			break;
		case WM_COMMAND:
			switch (LOWORD(wp)){
				case ID_BUTTON_1:
				{
					HANDLE TokenHandle;
					LUID Luid;
					TOKEN_PRIVILEGES NewState;

					if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&TokenHandle)){
						return -1;
					}

					if(!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &Luid)){
						return -1;
					}

					NewState.PrivilegeCount = 1;
					NewState.Privileges[0].Luid = Luid;
					NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

					AdjustTokenPrivileges(TokenHandle,FALSE,&NewState, 0, (PTOKEN_PRIVILEGES)NULL, 0);

					if(GetLastError() != ERROR_SUCCESS){
						return -1;
					}

					#define EWX_FORCEIFHUNG 0x00000010
					//パソコンをシャットダウンする
					ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCEIFHUNG, 0);
				}
				break;

			}
			break;

		case WM_CLOSE:
			DestroyWindow(hWnd);//ウインドウを破棄
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return (DefWindowProc(hWnd,msg,wp,lp));
	}
	return 0;
}


▲トップページ > Windows と C++