终止打开的应用程序并关闭机器电源 - Delphi Win10 问题
Terminating open applications &Powering Down the machine - Delphi Win10 question
目前,我正在 Delphi 创建一个应用程序,它允许计算机在关闭打开的应用程序后完全关闭电源。目前,我正在 运行 解决代码在 Win10 企业版中无法正常运行的问题,它正在注销用户,但没有完全关闭 PC。代码的核心在下面,我不清楚为什么这只会让我注销而不是完全关闭计算机。
用户将通过 CMD 将参数定义为 运行 以关闭引用该应用程序的计算机。应用程序将关闭打开的进程,并通过CMD传递关闭进程。
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
cmdl: TCommandLineParameterList;
cmd: TCommandLineParameter;
begin
cmdl := TCommandLineParameterList.Create;
cmdl.Initialize;
cmd := cmdl.FindByParam(CMD_CAPTION);
if Assigned(cmd) then
FWaitForWindowCaption := cmd.Param
else
FWaitForWindowCaption := '';
cmd := cmdl.FindByParam(CMD_CLASSNAME);
if Assigned(cmd) then
FWaitForWindowClassName := cmd.Param
else
FWaitForWindowClassName := '';
cmd := cmdl.FindByParam(CMD_TIMEOUT);
if Assigned(cmd) then
FTimeout := StrToIntDef(cmd.Param, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
cmd := cmdl.FindByParam(CMD_ACTION);
if Assigned(cmd) then begin
if cmd.Param = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if cmd.Param = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
end;
procedure TfrmCleanShutDown.ThreadTerminate(Sender: TObject);
begin
if (Sender as TWaitForWindowCloseThread).Success then
ExitWindowsEx(FAction, 0)
else
Label1.Caption := 'Timeout';
end;
如有任何帮助,我们将不胜感激。我不清楚为什么 win10 的行为方式只是注销用户而不是关闭,因为应用程序是通过 CMD 运行 并且用户具有关闭权限。很混乱。
从下面的评论中添加命令行代码。
unit uCommandLine;
interface
uses
SysUtils, Classes, Contnrs;
const
S_SWITCHES = '+-*';
type
//======================================
// TCommandLineParameter
//======================================
TCommandLineParameter = class
private
FParam: String;
FSwitch: String;
FOptions: String;
public
property Param: String read FParam write FParam;
property Switch: String read FSwitch write FSwitch;
property Options: String read FOptions write FOptions;
end; // TCommandLineParameter
//======================================
// TCommandLineParameterList
//======================================
TCommandLineParameterList = class(TObjectList)
private
function GetParameter(idx: Integer): TCommandLineParameter;
public
function Initialize: Boolean;
function FindByParam(const ParamString: String): TCommandLineParameter;
property Parameters[idx: Integer]: TCommandLineParameter read GetParameter;
end;
implementation
const
C_CMD_DELIM: Char = '-';
//======================================
// TCommandLineParameter
//======================================
//======================================
// TCommandLineParameterList
//======================================
//------------------------------------------------------------------------------
function TCommandLineParameterList.GetParameter(idx: Integer): TCommandLineParameter;
begin
Result := TCommandLineParameter(Items[idx]);
end;
//------------------------------------------------------------------------------
function TCommandLineParameterList.Initialize: Boolean;
var
n, idx: Integer;
p: TCommandLineParameter;
s: String;
begin
Result := True;
p := nil;
n := ParamCount;
for idx := 1 to n do begin
s := Trim(ParamStr(idx));
if s[1] = C_CMD_DELIM then begin
System.Delete(s, 1, 1);
p := FindByParam(s);
if p = nil then begin
p := TCommandLineParameter.Create;
p.Param := s;
Add(p);
end
else begin
Result := False;
break;
end;
end
else begin
if p <> nil then begin
p.Options := s;
p := nil;
end
else begin
Result := False;
break;
end;
end;
end;
end;
//------------------------------------------------------------------------------
function TCommandLineParameterList.FindByParam(const ParamString: String): TCommandLineParameter;
var
idx: Integer;
begin
Result := nil;
for idx := 0 to Count-1 do
if CompareStr(Parameters[idx].Param, ParamString) = 0 then begin
Result := Parameters[idx];
break;
end;
end;
end.
您没有在 ProcessCommandLine()
方法中正确处理命令行参数。
当您使用此命令行调用您的应用程序时:
-w -a shutdown -t 45
FindByParam(CMD_ACTION)
returns 一个 TCommandLineParameter
其 Param
是 'a'
而 Options
是'shutdown'
。但是您正在 Param
而不是 Options
中寻找 'shutdown'
。您没有找到匹配项,所以您最终使用默认的 EWX_LOGOFF
标志调用 ExitWindowsEx()
而不是
预期 EWX_POWEROFF
标志。
必须调试您的代码,您才会看到这种情况发生。
您的所有命令都犯了同样的错误。
改用这个:
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
cmdl: TCommandLineParameterList;
cmd: TCommandLineParameter;
begin
cmdl := TCommandLineParameterList.Create;
try
cmdl.Initialize;
cmd := cmdl.FindByParam(CMD_CAPTION);
if Assigned(cmd) then
FWaitForWindowCaption := cmd.Options
else
FWaitForWindowCaption := '';
cmd := cmdl.FindByParam(CMD_CLASSNAME);
if Assigned(cmd) then
FWaitForWindowClassName := cmd.Options
else
FWaitForWindowClassName := '';
cmd := cmdl.FindByParam(CMD_TIMEOUT);
if Assigned(cmd) then
FTimeout := StrToIntDef(cmd.Options, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
cmd := cmdl.FindByParam(CMD_ACTION);
if Assigned(cmd) then
begin
if cmd.Options = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if cmd.Options = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
finally
cmdl.Free;
end;
end;
也就是说,您的 TCommandLineParameterList
是完全多余的,可以删除,因为 RTL 的 SysUtils.FindCmdLineSwitch()
函数可以为您完成所有相同的工作,例如:
uses
..., System.SysUtils;
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
Value: string;
begin
if FindCmdLineSwitch(CMD_CAPTION, Value) then
FWaitForWindowCaption := Value
else
FWaitForWindowCaption := '';
if FindCmdLineSwitch(CMD_CLASSNAME, Value) then
FWaitForWindowClassName := Value
else
FWaitForWindowClassName := '';
if FindCmdLineSwitch(CMD_TIMEOUT, Value) then
FTimeout := StrToIntDef(Value, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
if FindCmdLineSwitch(CMD_ACTION, Value) then
begin
if Value = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if Value = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
end;
目前,我正在 Delphi 创建一个应用程序,它允许计算机在关闭打开的应用程序后完全关闭电源。目前,我正在 运行 解决代码在 Win10 企业版中无法正常运行的问题,它正在注销用户,但没有完全关闭 PC。代码的核心在下面,我不清楚为什么这只会让我注销而不是完全关闭计算机。
用户将通过 CMD 将参数定义为 运行 以关闭引用该应用程序的计算机。应用程序将关闭打开的进程,并通过CMD传递关闭进程。
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
cmdl: TCommandLineParameterList;
cmd: TCommandLineParameter;
begin
cmdl := TCommandLineParameterList.Create;
cmdl.Initialize;
cmd := cmdl.FindByParam(CMD_CAPTION);
if Assigned(cmd) then
FWaitForWindowCaption := cmd.Param
else
FWaitForWindowCaption := '';
cmd := cmdl.FindByParam(CMD_CLASSNAME);
if Assigned(cmd) then
FWaitForWindowClassName := cmd.Param
else
FWaitForWindowClassName := '';
cmd := cmdl.FindByParam(CMD_TIMEOUT);
if Assigned(cmd) then
FTimeout := StrToIntDef(cmd.Param, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
cmd := cmdl.FindByParam(CMD_ACTION);
if Assigned(cmd) then begin
if cmd.Param = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if cmd.Param = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
end;
procedure TfrmCleanShutDown.ThreadTerminate(Sender: TObject);
begin
if (Sender as TWaitForWindowCloseThread).Success then
ExitWindowsEx(FAction, 0)
else
Label1.Caption := 'Timeout';
end;
如有任何帮助,我们将不胜感激。我不清楚为什么 win10 的行为方式只是注销用户而不是关闭,因为应用程序是通过 CMD 运行 并且用户具有关闭权限。很混乱。
从下面的评论中添加命令行代码。
unit uCommandLine;
interface
uses
SysUtils, Classes, Contnrs;
const
S_SWITCHES = '+-*';
type
//======================================
// TCommandLineParameter
//======================================
TCommandLineParameter = class
private
FParam: String;
FSwitch: String;
FOptions: String;
public
property Param: String read FParam write FParam;
property Switch: String read FSwitch write FSwitch;
property Options: String read FOptions write FOptions;
end; // TCommandLineParameter
//======================================
// TCommandLineParameterList
//======================================
TCommandLineParameterList = class(TObjectList)
private
function GetParameter(idx: Integer): TCommandLineParameter;
public
function Initialize: Boolean;
function FindByParam(const ParamString: String): TCommandLineParameter;
property Parameters[idx: Integer]: TCommandLineParameter read GetParameter;
end;
implementation
const
C_CMD_DELIM: Char = '-';
//======================================
// TCommandLineParameter
//======================================
//======================================
// TCommandLineParameterList
//======================================
//------------------------------------------------------------------------------
function TCommandLineParameterList.GetParameter(idx: Integer): TCommandLineParameter;
begin
Result := TCommandLineParameter(Items[idx]);
end;
//------------------------------------------------------------------------------
function TCommandLineParameterList.Initialize: Boolean;
var
n, idx: Integer;
p: TCommandLineParameter;
s: String;
begin
Result := True;
p := nil;
n := ParamCount;
for idx := 1 to n do begin
s := Trim(ParamStr(idx));
if s[1] = C_CMD_DELIM then begin
System.Delete(s, 1, 1);
p := FindByParam(s);
if p = nil then begin
p := TCommandLineParameter.Create;
p.Param := s;
Add(p);
end
else begin
Result := False;
break;
end;
end
else begin
if p <> nil then begin
p.Options := s;
p := nil;
end
else begin
Result := False;
break;
end;
end;
end;
end;
//------------------------------------------------------------------------------
function TCommandLineParameterList.FindByParam(const ParamString: String): TCommandLineParameter;
var
idx: Integer;
begin
Result := nil;
for idx := 0 to Count-1 do
if CompareStr(Parameters[idx].Param, ParamString) = 0 then begin
Result := Parameters[idx];
break;
end;
end;
end.
您没有在 ProcessCommandLine()
方法中正确处理命令行参数。
当您使用此命令行调用您的应用程序时:
-w -a shutdown -t 45
FindByParam(CMD_ACTION)
returns 一个 TCommandLineParameter
其 Param
是 'a'
而 Options
是'shutdown'
。但是您正在 Param
而不是 Options
中寻找 'shutdown'
。您没有找到匹配项,所以您最终使用默认的 EWX_LOGOFF
标志调用 ExitWindowsEx()
而不是
预期 EWX_POWEROFF
标志。
必须调试您的代码,您才会看到这种情况发生。
您的所有命令都犯了同样的错误。
改用这个:
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
cmdl: TCommandLineParameterList;
cmd: TCommandLineParameter;
begin
cmdl := TCommandLineParameterList.Create;
try
cmdl.Initialize;
cmd := cmdl.FindByParam(CMD_CAPTION);
if Assigned(cmd) then
FWaitForWindowCaption := cmd.Options
else
FWaitForWindowCaption := '';
cmd := cmdl.FindByParam(CMD_CLASSNAME);
if Assigned(cmd) then
FWaitForWindowClassName := cmd.Options
else
FWaitForWindowClassName := '';
cmd := cmdl.FindByParam(CMD_TIMEOUT);
if Assigned(cmd) then
FTimeout := StrToIntDef(cmd.Options, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
cmd := cmdl.FindByParam(CMD_ACTION);
if Assigned(cmd) then
begin
if cmd.Options = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if cmd.Options = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
finally
cmdl.Free;
end;
end;
也就是说,您的 TCommandLineParameterList
是完全多余的,可以删除,因为 RTL 的 SysUtils.FindCmdLineSwitch()
函数可以为您完成所有相同的工作,例如:
uses
..., System.SysUtils;
procedure TfrmCleanShutdown.ProcessCommandLine;
const
CMD_CAPTION = 'w'; // window capShuttion
CMD_CLASSNAME = 'c'; // window class name
CMD_ACTION = 'a';
CMD_TIMEOUT = 't';
S_SHUTDOWN = 'shutdown';
S_LOGOFF = 'logoff';
S_RESTART = 'restart';
var
Value: string;
begin
if FindCmdLineSwitch(CMD_CAPTION, Value) then
FWaitForWindowCaption := Value
else
FWaitForWindowCaption := '';
if FindCmdLineSwitch(CMD_CLASSNAME, Value) then
FWaitForWindowClassName := Value
else
FWaitForWindowClassName := '';
if FindCmdLineSwitch(CMD_TIMEOUT, Value) then
FTimeout := StrToIntDef(Value, DEF_TIMEOUT)
else
FTimeout := DEF_TIMEOUT;
FAction := EWX_LOGOFF or EWX_FORCEIFHUNG; // default action
if FindCmdLineSwitch(CMD_ACTION, Value) then
begin
if Value = S_SHUTDOWN then
FAction := EWX_POWEROFF or EWX_FORCEIFHUNG
else if Value = S_RESTART then
FAction := EWX_REBOOT or EWX_FORCEIFHUNG
end;
end;