Завершение работы открытых приложений и выключение компьютера - Delphi вопрос Win10 - PullRequest
0 голосов
/ 12 февраля 2020

В настоящее время я создаю приложение в Delphi, которое позволит компьютеру полностью выключиться после закрытия открытых приложений. В настоящее время я сталкиваюсь с проблемами, когда код не работает должным образом в Win10 enterprise, и он выводит пользователя из системы, но не полностью выключает P C. мозги кода приведены ниже, и мне неясно, почему это просто вывело бы меня из системы и не полностью выключило компьютер.

Пользователь определит параметры для запуска через 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.

1 Ответ

3 голосов
/ 12 февраля 2020

Вы неправильно обрабатываете параметры командной строки в своем методе ProcessCommandLine().

Когда вы вызываете свое приложение с этой командной строкой:

-w -a shutdown -t 45

FindByParam(CMD_ACTION) возвращает TCommandLineParameter, Param которого равен 'a', а Options равно 'shutdown'. Но тогда вы ищете 'shutdown' в Param вместо Options. Вы не нашли соответствия, поэтому в конечном итоге вы вызываете ExitWindowsEx() со своим флагом по умолчанию EWX_LOGOFF вместо предполагаемого флага 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; 
...