终止打开的应用程序并关闭机器电源 - 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 一个 TCommandLineParameterParam'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;