プログラミング工房 > HSP > サンプルモジュール > 

XPや7などのNT系Windowsでシャットダウン・再起動・ログオフ・サスペンド・休止を行う

(2012/04/09更新)

sysexit命令やExitWindowsEx APIだけでNT系Windowsをシャットダウンできない理由

HSPからWindowsの終了や再起動を行う方法としてはhspext.dll命令のsysexitやWin32 APIのExitWindowsExがありますが、動作環境がXPや2000などのNT系OSの場合、これらの命令/APIを実行しただけではWindowsは言うことを聞いてくれません。

実はNT系OSで上記のことをさせるには、命令/APIの実行前にOSのシャットダウン権限を得ている状態でなければいけません。これはプログラムを実行しているユーザがWindowsの管理者権限を持っていたとしても必要になります。

プログラムでOSのシャットダウン権限を得るには、「プロセスにSE_SHUTDOWN_NAME特権を割り当てる」必要があります。具体的な手順としては、「現在のプロセスのアクセストークンを取得」→「アクセストークンのSE_SHUTDOWN_NAME特権を有効にする」という形になります。


現在のプロセスのアクセストークンを取得

トークンを取得するには、まずGetCurrentProcess APIで現在のプロセスの擬似ハンドルを取得してから、OpenProcessToken APIを実行します。GetCurrentProcessで得られるハンドルはクローズの必要はありませんが、トークンの方は不要になった時点でクローズする必要があります。


アクセストークンのSE_SHUTDOWN_NAME特権を有効にする

この部分の処理では3つの構造体が必要なため、まずそれ用の変数を用意します。TOKEN_PREVILEGES構造体については、今回設定しなければならない特権情報が1つなのでサイズが16バイトになっていますが、使用される状況によっては変わる可能性があります。

次にLookupPrivilegeValue APIによって適切なLUID構造体を取得し、それを元にLUID_AND_ATTRIBUTES構造体、TOKEN_PREVILEGES構造体を作成。最後にアクセストークンとTOKEN_PREVILEGES構造体を引数にしてAdjustTokenPrivileges APIを実行します。

トークンの変更がうまくいけば、あとはExitWindowsEx APIを実行するだけでWindowsの終了/再起動が可能です(ExitWindowsExの呼び出しはモジュールに含まれています)。ExitWindowsExの代わりにhspext.dllのsysexit命令を使ってもかまいません。


モジュールの使い方

利用したいスクリプトでインクルードしてから、命令一覧にある命令から一つ選んで呼び出して下さい。また呼び出した後には必ずend命令を入れておくようにして下さい。マシンによってはwin_shutdownでもwin_poweroffでも同じ動作になるようですが、確実に電源を切りたい場合はwin_poweroffを使っておけばいいでしょう。


参考ページ

http://www31.ocn.ne.jp/~yoshio2/vcmemo18-1.html
http://www6.plala.or.jp/MilkHouse/practical/contents310/contents31002.html
http://itpro.nikkeibp.co.jp/article/Windows/20060208/228778/


関連リンク

Windows用フリーウェア - NT系Windowsを終了・再起動・ログオフ・スリープさせるためのユーティリティ、shutdown / reboot / logoff/ sleep


>> ダウンロード (ブラウザで見ると文字化けしますが、データは正常です)

/***********************************************************

	NT系Windowsシャットダウン機能モジュール

		【2012/04/09更新】

	(使い方)

		以下の命令のうちいずれか一つを実行する。

		サスペンドまたは休止させる場合は、命令を呼び出し
		た後にend命令を実行してプログラムを終了させて下さい。

		9x系Windowsではこのモジュールを使う必要はない。

		作者の環境では、win_shutdownとwin_poweroffは「Windows終了」+
		「電源オフ」という同じ動作になりました。

	(命令一覧)

		●ログオフ
	
			#deffunc win_logoff
	
		●シャットダウン
	
			#deffunc win_shutdown
	
		●再起動
	
			#deffunc win_reboot
	
		●電源オフ
	
			#deffunc win_poweroff

		●サスペンド(スリープ)
	
			#deffunc win_suspend
			#deffunc win_sleep
	
		●休止(ハイバネート)
	
			#deffunc win_hibernate
	
***********************************************************/
#ifndef	__GM_EXIT_WINDOWS__
#define	global	__GM_EXIT_WINDOWS__

	#include "kernel32.as"
	#include "advapi32.as"
	#include "user32.as"

	#uselib "PowrProf.dll"
	#func global SetSuspendState "SetSuspendState" int, int, int  

#module

	#const	TRUE						1
	#const	FALSE						0
	#const	NULL						0

	#const	TOKEN_ADJUST_PRIVILEGES		0x20

	#const	SIZE_LUID					8	// 構造体サイズ
	#const	SIZE_LUID_AND_ATTRIBUTES	12
	#const	SIZE_TOKEN_PRIVILEGES		16	// 特権情報を1つだけ設定する場合(PRIVILEGE_COUNT=1)
	#define	SE_SHUTDOWN_NAME			"SeShutdownPrivilege"
	#const	SE_PRIVILEGE_ENABLED		0x02
	#const	PRIVILEGE_COUNT				1

	#const	EWX_LOGOFF					0
	#const	EWX_SHUTDOWN				1
	#const	EWX_REBOOT					2
	#const	EWX_POWEROFF				8
 
/*
* アクセストークンのSE_SHUTDOWN_NAME 特権を有効に
 */
#deffunc _adjust_privilege

	/* 現在のプロセスのアクセストークンを取得 */
	GetCurrentProcess
	hproc	= stat		// 現在のプロセスの擬似ハンドル。クローズ不要。
	htoken	= 0			// SE_SHUTDOWN_NAME 特権を設定するためのアクセストークン用。要クローズ。
	OpenProcessToken	hproc, TOKEN_ADJUST_PRIVILEGES, varptr(htoken)

	/* アクセストークンのSE_SHUTDOWN_NAME 特権を有効に */
	dim	luid,					SIZE_LUID / 4					// LUID 構造体
	dim	luid_and_attributes,	SIZE_LUID_AND_ATTRIBUTES / 4	// LUID_AND_ATTRIBUTES 構造体
	dim	token_privileges,		SIZE_TOKEN_PRIVILEGES / 4		// TOKEN_PREVILEGES 構造体
	LookupPrivilegeValue	NULL, SE_SHUTDOWN_NAME, varptr(luid)
	luid_and_attributes		= luid(0), luid(1), SE_PRIVILEGE_ENABLED
	token_privileges		= PRIVILEGE_COUNT, luid_and_attributes(0), luid_and_attributes(1), luid_and_attributes(2)
	AdjustTokenPrivileges	htoken, FALSE, varptr(token_privileges), 0, NULL, NULL
	CloseHandle				htoken

	return

/*
* 指定の方法でWindowsを終了
 */
#deffunc _exit_win	int ewx_type_

	_adjust_privilege
	ExitWindowsEx	ewx_type_, NULL

	return

/*
* Windowsをサスペンド(スリープ)または休止(ハイバネート)
 */
#deffunc _suspend_win int flag_hiberbate_

	_adjust_privilege
	SetSuspendState	flag_hiberbate_, FALSE, FALSE

	return

/*
* ログオフ
 */
#deffunc win_logoff

	_exit_win	EWX_LOGOFF
	return

/*
* シャットダウン
 */
#deffunc win_shutdown

	_exit_win	EWX_SHUTDOWN
	return

/*
* 再起動
 */
#deffunc win_reboot

	_exit_win	EWX_REBOOT
	return

/*
* 電源オフ
 */
#deffunc win_poweroff

	_exit_win	EWX_POWEROFF
	return

/*
* スリープ(=サスペンド)
 */
#deffunc win_sleep

	_suspend_win FALSE	
	return

/*
* サスペンド(=スリープ)
 */
#deffunc win_suspend

	_suspend_win FALSE	
	return

/*
* 休止(ハイバネート)
 */
#deffunc win_hibernate

	_suspend_win TRUE	
	return

#global


#endif

/*
%dll
gm_exit_windows.hsp
%date
2012/04/09
%author
chrono
%port
Win
%note
利用しているDLL	: kernel32.dll, advapi32.dll, user32.dll, powrprof.dll
9x系Windowsではこのモジュールを使う必要はありません。

;----------
%index
win_logoff
NT系Windowsからログオフ
%prm
%inst
%type
ユーザ定義命令
%sample
%href
win_shutdown
win_reboot
win_poweroff
win_sleep
win_suspend
win_hibernate
;----------
%index
win_shutdown
NT系Windowsをシャットダウン
%prm
%inst
%type
ユーザ定義命令
%sample
%href
win_logoff
win_reboot
win_poweroff
win_sleep
win_suspend
win_hibernate
;----------
%index
win_reboot
NT系Windowsを再起動
%prm
%inst
%type
ユーザ定義命令
%sample
%href
win_logoff
win_shutdown
win_poweroff
win_sleep
win_suspend
win_hibernate
;----------
%index
win_poweroff
NT系Windowsマシンを電源オフ
%prm
%inst
%type
ユーザ定義命令
%sample
%href
win_logoff
win_shutdown
win_reboot
win_sleep
win_suspend
win_hibernate
;----------
%index
win_sleep
NT系Windowsマシンをスリープ(=サスペンド)
%prm
%inst
同じモジュールのwin_suspend命令でも同じことができます。
%type
ユーザ定義命令
%sample
%href
win_logoff
win_shutdown
win_reboot
win_suspend
win_hibernate
;----------
%index
win_suspend
NT系Windowsマシンをサスペンド(=スリープ)
%prm
%inst
同じモジュールのwin_sleep命令でも同じことができます。
%type
ユーザ定義命令
%sample
%href
win_logoff
win_shutdown
win_reboot
win_sleep
win_hibernate
;----------
%index
win_hibernate
NT系Windowsマシンを休止(ハイバネート)
%prm
%inst
%type
ユーザ定義命令
%sample
%href
win_logoff
win_shutdown
win_reboot
win_sleep
win_suspend
%*/