记得暑假的时候偶无聊写了一个MyClock的小恶作剧软件,当时用到了关机.其实之前我一直以为关机嘛,不就是ExitWindowEx就可以了嘛,其实事情没那么简单,XP下的关机需要一个提升权限(Privilege)的过程.我简单整理了一下,WindowsXP下关机至少有下面几种方法:
一: 最简单的方法是
shutdown -s -t 0
关于shutdown的具体用法如下:
用法: shutdown [-i | -l | -s | -r | -a] [-f] [-m /computername] [-t xx] [-c "comment"] [-d up:xx:yy]
没有参数 显示此消息(与 ? 相同)
-i 显示 GUI 界面,必须是第一个选项
-l 注销(不能与选项 -m 一起使用)
-s 关闭此计算机
-r 关闭并重启动此计算机
-a 放弃系统关机
-m /computername 远程计算机关机/重启动/放弃
-t xx 设置关闭的超时为 xx 秒
-c "comment" 关闭注释(最大 127 个字符)
-f 强制运行的应用程序关闭而没有警告
-d [p]:xx:yy 关闭原因代码
u 是用户代码
p 是一个计划的关闭代码
xx 是一个主要原因代码(小于 256 的正整数)
yy 是一个次要原因代码(小于 65536 的正整数)
-f:强行关闭应用程序
-m /计算机名:控制远程计算机
-i:显示图形用户界面,但必须是Shutdown的第一个选项
-l:注销当前用户
-r:关机并重启
-t时间:设置关机倒计时
-c "消息内容":输入关机对话框中的消息内容(不能超127个字符
(关于如何在程序中去调用我想大家已经清楚了,C/C++中可以使用system,C#里面的可以参加我写rundll32的问题)
方法二: 利用ExitWindowEx
这个在XP下需要有一个提升权限的过程, 我用C#写了一下:
[StructLayout(LayoutKind.Sequential, Pack
=
1
)]
internal
struct
TokPriv1Luid

{
public int Count;
public long Luid;
public int Attr;
}

[DllImport(
"
kernel32.dll
"
, ExactSpelling
=
true
)]
internal
static
extern
IntPtr GetCurrentProcess();

[DllImport(
"
advapi32.dll
"
, ExactSpelling
=
true
, SetLastError
=
true
)]
internal
static
extern
bool
OpenProcessToken(IntPtr h,
int
acc,
ref
IntPtr phtok);

[DllImport(
"
advapi32.dll
"
, SetLastError
=
true
)]
internal
static
extern
bool
LookupPrivilegeValue(
string
host,
string
name,
ref
long
pluid);

[DllImport(
"
advapi32.dll
"
, ExactSpelling
=
true
, SetLastError
=
true
)]
internal
static
extern
bool
AdjustTokenPrivileges(IntPtr htok,
bool
disall,
ref
TokPriv1Luid newst,
int
len, IntPtr prev, IntPtr relen);

[DllImport(
"
user32.dll
"
, ExactSpelling
=
true
, SetLastError
=
true
)]
internal
static
extern
bool
ExitWindowsEx(
int
flg,
int
rea);

internal
const
int
SE_PRIVILEGE_ENABLED
=
0x00000002
;
internal
const
int
TOKEN_QUERY
=
0x00000008
;
internal
const
int
TOKEN_ADJUST_PRIVILEGES
=
0x00000020
;
internal
const
string
SE_SHUTDOWN_NAME
=
"
SeShutdownPrivilege
"
;
internal
const
int
EWX_LOGOFF
=
0x00000000
;
internal
const
int
EWX_SHUTDOWN
=
0x00000001
;
internal
const
int
EWX_REBOOT
=
0x00000002
;
internal
const
int
EWX_FORCE
=
0x00000004
;
internal
const
int
EWX_POWEROFF
=
0x00000008
;
internal
const
int
EWX_FORCEIFHUNG
=
0x00000010
;

调用如下:
bool
ok;
TokPriv1Luid tp;
IntPtr hproc
=
GetCurrentProcess();
IntPtr htok
=
IntPtr.Zero;
ok
=
OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES
|
TOKEN_QUERY,
ref
htok);
tp.Count
=
1
;
tp.Luid
=
0
;
tp.Attr
=
SE_PRIVILEGE_ENABLED;
ok
=
LookupPrivilegeValue(
null
, SE_SHUTDOWN_NAME,
ref
tp.Luid);
ok
=
AdjustTokenPrivileges(htok,
false
,
ref
tp,
0
, IntPtr.Zero, IntPtr.Zero);
ok
=
ExitWindowsEx(EWX_POWEROFF, EWX_FORCE);
方法三: 使用InitiateSystemShutdown
和ExitWindowEx一样, 它也需要提升权限
只需将
ok
=
ExitWindowsEx(EWX_POWEROFF, EWX_FORCE);
一句替换成
ok
=
InitiateSystemShutdown(
null
,
null
,
0
,
false
,
false
);
别忘了加上声明:
[DllImport(
"
Advapi32.dll
"
)]
internal
static
extern
bool
InitiateSystemShutdown(
string
lpMachineName,
string
lpMessage,
ulong
dwTimeout,
bool
bForceAppsClosed,
bool
bRebootAfterShutdown);
方法四: 这个方法是最有趣的了,利用ntdll.dll里面未公开的原生API进行2s关机,无须提升权限就可以了.
网上有人用IDA进行分析的文章, 有兴趣可以找找,反正我是看不懂了,- -#
好了, 这里有个小插曲,就是在C#中调用这个Native API,可能水平还是不够, 在调用过程老是出错.没办法,
想了一个阴招, 用VC编写dll, C#去调用, 呵呵, 最终结果不错哟!
下面先建立一个Win32 DLL Project 项目,然后把下面的函数拷贝到cpp当中编译,OK,dll就出来了
#include
<
windows.h
>

extern
"
C
"
__declspec(dllexport)
void
ShutdownWindow1(
void
)

{
const int SE_SHUTDOWN_PRIVILEGE = 0x13;
typedef int (*RtlAdjustPrivilege)(int,bool, bool,int*);
typedef int (*ZwShutdownSystem)(int);
int en = 0;
HMODULE module = LoadLibrary("ntdll.dll");
RtlAdjustPrivilege rtl = (RtlAdjustPrivilege)GetProcAddress(module, "RtlAdjustPrivilege");
ZwShutdownSystem shutdown = (ZwShutdownSystem)GetProcAddress(module,"ZwShutdownSystem");
int nRet=rtl(SE_SHUTDOWN_PRIVILEGE,TRUE,TRUE,&en);
if(nRet==0x0C000007C)
nRet = rtl(SE_SHUTDOWN_PRIVILEGE,TRUE,FALSE,&en);
nRet=shutdown(2);
}

然后把dll拷贝到C#工程的Release/Debug的文件目录下, 加上下面的话就OK了, 2s关机, So Cool!
[DllImport(
"
ShutdownDll.dll
"
,CallingConvention
=
CallingConvention.Cdecl)]
public
static
extern
void
ShutdownWindow();
调用 ShutdownWindow();