目录
一、API函数说明
1. GetCurrentProcess
2. OpenProcessToken
3. LookupPrivilegeValue
4. AdjustTokenPrivileges
5. ExitWindowsEx
二、Delphi实现源代码
在日常软件开发中,可能会遇到通过程序自动关闭电脑,在早期Windows 9x下,可以直接调用ExitWindowsEx函数实现关机或重新启动。但是在Windows 2000/XP以及最新的win10和win11下,在调用ExitWindowsEx函数之前,需要先调用AdjustTokenPrivileges函数来获得关机特权。
否则将无法实现程序关机 !
首先需要介绍几个windows API函数:
GetCurrentProcess
是一个Windows API函数,用于获取当前进程的句柄。该句柄是一个伪句柄,只能在进程内部使用,离开当前进程就没有意义了。
函数的原型如下:
HANDLE GetCurrentProcess(void);
该函数返回一个句柄,该句柄表示当前进程的伪句柄。这个伪句柄不需要使用CloseHandle
函数来关闭,因为它在进程结束时自动关闭。
这个函数主要用于在进程内部使用,以便于访问和操作当前进程的相关资源。例如,可以使用这个句柄来获取当前进程的名称、PID等。
请注意,这个函数只能用于获取当前进程的伪句柄,不能用于获取其他进程的句柄。如果需要获取其他进程的句柄,需要使用其他相应的API函数,例如OpenProcess
。
OpenProcessToken用于打开与进程相关联的访问令牌。这个函数可以用于获取对其他进程的访问权限,以便进行进程间通信、调试或其他需要访问其他进程资源的操作。
函数的原型如下:
BOOL OpenProcessToken(
HANDLE ProcessHandle,
DWORD DesiredAccess,
PHANDLE TokenHandle
);
参数说明:
ProcessHandle
:一个指向要打开访问令牌的进程的句柄。这个进程必须具有PROCESS_QUERY_LIMITED_INFORMATION
访问权限。DesiredAccess
:一个访问掩码,用于指定请求的访问类型。这些请求的访问类型与令牌的自由访问控制列表(DACL)进行比较,以确定授予或拒绝了哪些访问。TokenHandle
:一个指向句柄的指针,该句柄标识函数返回时新打开的访问令牌。函数的返回值是一个布尔值,指示操作是否成功。如果成功,则返回值为非零;如果失败,则返回值为零。
使用OpenProcessToken函数可以获取对其他进程的访问令牌,然后可以使用其他Windows API函数来执行相应的操作,例如读取或修改令牌的权限等。需要注意的是,在调用OpenProcessToken函数之前,需要了解相关的安全性和访问权限,以及如何使用返回的访问令牌执行其他操作。
LookupPrivilegeValue
用于查找指定进程的特权值。这个函数用于确定当前进程是否具有某个特定的特权,以便执行某些操作或访问某些资源。
函数的原型如下:
BOOL LookupPrivilegeValue(
LPCWSTR lpSystemName,
LPCWSTR lpName,
PLUID lpLuid
);
参数说明:
lpSystemName
:一个指向字符串的指针,该字符串指定了要查询的特权所属的系统的名称。通常,这个参数设置为NULL,表示使用本地系统。lpName
:一个指向字符串的指针,该字符串指定了要查询的特权的名称。lpLuid
:一个指向LUID结构的指针,该结构用于接收查询到的特权值。函数返回一个布尔值,指示是否成功找到指定的特权值。如果函数成功找到特权值,则返回值为非零;如果未找到特权值或发生错误,则返回值为零。
使用LookupPrivilegeValue
函数可以帮助确定当前进程是否具有执行特定操作所需的特权。这对于进程安全性和权限管理非常重要,因为只有具有适当特权的进程才能执行敏感操作或访问受保护的资源。
AdjustTokenPrivileges
用于调整与访问令牌相关联的特权状态。这个函数允许进程修改其令牌的特权集合,以便启用或禁用特定的特权。
函数的原型如下:
BOOL AdjustTokenPrivileges(
HANDLE TokenHandle,
BOOL DisableAllPrivileges,
LPTOKEN_PRIVILEGES NewState,
DWORD BufferLength,
LPTOKEN_PRIVILEGES PreviousState,
PDWORD ReturnLength
);
参数说明:
TokenHandle
:一个指向访问令牌句柄的指针,该令牌包含要更改的特权集合。DisableAllPrivileges
:一个布尔值,指示是否禁用所有特权。如果为TRUE,则禁用所有特权;如果为FALSE,则启用所有特权。NewState
:一个指向TOKEN_PRIVILEGES
结构的指针,该结构指定要更改的特权集合的新状态。如果DisableAllPrivileges
参数为TRUE,则此参数必须设置为NULL。BufferLength
:一个指向DWORD类型的指针,该值指定NewState
缓冲区的大小。如果NewState
参数为NULL,则此参数必须设置为0。PreviousState
:一个指向TOKEN_PRIVILEGES
结构的指针,该结构用于接收更改之前的特权集合状态。此参数可以为NULL。ReturnLength
:一个指向DWORD类型的指针,该值接收返回的特权集合状态的大小。如果PreviousState
参数不为NULL,则此参数将接收更改之前的特权集合的大小。函数返回一个布尔值,指示操作是否成功。如果函数成功地更改了特权状态,则返回值为非零;如果发生错误或未更改任何特权,则返回值为零。
使用AdjustTokenPrivileges
函数可以控制进程的特权级别,以执行敏感操作或访问受保护的资源。通过启用或禁用特定的特权,可以控制进程的安全行为和权限,从而提高系统的安全性。
ExitWindowsEx
是一个Windows API函数,用于控制Windows的开关,如关闭Windows、重新启动Windows等。
在Win9x下,可以直接调用ExitWindowsEx函数实现关机或重新启动。但在Win 2000/XP以及win10和win11下,调用此函数时,还需要先调用AdjustTokenPrivileges
函数。
这个函数的原型如下:
BOOL ExitWindowsEx(
UINT uFlags, // 关闭参数
DWORD dwReserved // 系统保留,一般取0
);
参数说明:
uFlags
:指定关闭的类型。此参数必须有下列值的组合:
EWX_FORCE
:强制终止进程。当此标志设置,Windows不会发送消息WM_QUERYENDSESSION和WM_ENDSESSION给目前在系统中运行的程序。这可能会导致应用程序丢失数据。因此,应该在紧急情况下使用此标志。EWX_LOGOFF
:关闭所有进程,然后注销用户。EWX_POWEROFF
:关闭系统并关闭电源。该系统必须支持断电。EWX_REBOOT
:关闭系统,然后重新启动系统。所有文件缓冲区已经刷新到磁盘上,所有正在运行的进程已经停止。dwReserved
:系统保留,一般取0。使用ExitWindowsEx函数时,应确保理解每个标志的含义和影响,并谨慎使用强制终止进程的标志(EWX_FORCE),以避免数据丢失或其他潜在问题。
{ 电脑关机函数,根据其他语言代码改写
sensor 2024-01-25
}
unit uPowerOFF;
interface
uses
Winapi.Windows;
// Winapi.Messages,
// System.SysUtils,
// System.Variants,
// System.Classes;
{
const
SE_PRIVILEGE_ENABLED = $00000002;
TOKEN_QUERY = $00000008;
TOKEN_ADJUST_PRIVILEGES = $00000020;
SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
EWX_LOGOFF = $00000000;
EWX_SHUTDOWN = $00000001;
EWX_REBOOT = $00000002;
EWX_FORCE = $00000004;
EWX_POWEROFF = $00000008;
EWX_FORCEIFHUNG = $00000010;
}
//内部函数
function DoExitWin(flag : Integer) : Boolean;
//公共函数,实际外部调用函数
procedure PowerOff;
procedure LogOff;
procedure Reboot;
implementation
//内部函数
function DoExitWin(flag : Integer) : Boolean;
var
tp : TTokenPrivileges;
hproc : THandle;
htok : THandle;
ok : Boolean;
ReturnLength : DWORD;
begin
hproc := GetCurrentProcess();
ok := OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES + TOKEN_QUERY,htok);
if not ok then Exit(False);
tp.PrivilegeCount := 1;
tp.Privileges[0].Luid := 0;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
ok := LookupPrivilegeValue(nil, SE_SHUTDOWN_NAME, tp.Privileges[0].Luid);
if not ok then Exit(False);
ok := AdjustTokenPrivileges(htok, false, tp, 0, nil, ReturnLength);
if not ok then Exit(False);
Result := ExitWindowsEx(flag, 0);
end;
//公共函数
procedure PowerOff;
begin
DoExitWin(EWX_FORCE + EWX_POWEROFF);//关机
end;
procedure LogOff; //注销
begin
DoExitWin(EWX_FORCE + EWX_LOGOFF);
end;
procedure Reboot; //重启
begin
DoExitWin(EWX_FORCE + EWX_REBOOT);
end;
end.
以上函数在win10和win11上测试通过!