Проблемы с закрытием главного окна приложения при наличии только идентификатора его процесса

У меня есть идентификатор процесса. Этот процесс представляет собой приложение, имеющее главное окно. Я пытаюсь закрыть это приложение, отправив WM_CLOSE в его главное окно. Я ищу его главное окно, используя EnumWindows.

Проблема в том, что это приложение, которое я пытаюсь закрыть, не всегда закрывается. Это многопоточное приложение. Блокнот и Calc всегда закрываются, когда я использую тот же метод, который представлен ниже. Но я не уверен, что он работает правильно, потому что он возвращает мне много дескрипторов в одно и то же окно, даже для Calc.exe.

Возможно ли, что поток берет дескриптор окна, а затем этот дескриптор каким-то образом повреждается? Или, может быть, я должен использовать не GetWindowThreadProcessId(hHwnd,pPid), а какую-то другую функцию в обратном вызове?

У меня нет идей, буду признателен за любую помощь. Спасибо.

Фрагмент кода:

unit Unit22;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm22 = class(TForm)
    edtprocID: TEdit;
    lblEnterProcessID: TLabel;
    btnCloseProcessWindow: TButton;
    lblStatus: TLabel;
    procedure btnCloseProcessWindowClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  THandleAndHWND = record
    ProcID: THandle;
    WindowHandle: HWND;
  end;

var
  Form22: TForm22;

var
  HandleAndHWNDArray: array of THandleAndHWND;
  HandeIndex, lp: Integer;

implementation

{$R *.dfm}

function EnumProcess(hHwnd: HWND; lParam : integer): boolean; stdcall;
var
  pPid : DWORD;
begin
  //if the returned value in null the
  //callback has failed, so set to false and exit.
  if (hHwnd=0) then
  begin
    result := false;
  end else
  begin
    GetWindowThreadProcessId(hHwnd,pPid);
    Inc(HandeIndex);
    HandleAndHWNDArray[HandeIndex].ProcID := pPid;
    HandleAndHWNDArray[HandeIndex].WindowHandle := hHwnd;
    Result := true;
  end;

end;

procedure TForm22.btnCloseProcessWindowClick(Sender: TObject);
var
  ProcID: Cardinal;
  i, LastError: Integer;
begin
  HandeIndex := -1;
  ProcID := StrToInt(edtprocID.Text);

  SetLength(HandleAndHWNDArray, 3000);
  EnumWindows(@EnumProcess,lp);

  for i := 0 to HandeIndex do //After EnumWindows HandleIndex is above 500 despite the fact that I have like 10 openned windows max
  begin                       //That means that EnumWindows was called 500 times?
    if HandleAndHWNDArray[i].ProcID =  ProcID then //search for process equal to procces ID given by the user
    begin
      //if we have a processID then we have a handle to its main window
      if PostMessage(HandleAndHWNDArray[i].WindowHandle, WM_CLOSE, 0, 0) then
      begin
        lblStatus.Caption := 'message posted!';
      end else
      begin
        LastError := GetLastError;
        lblStatus.Caption := Format('Error: [%d] ' + SysErrorMessage(LastError), [LastError]);
      end;
      Exit;
    end;
  end;

end;

end.

person Wodzu    schedule 02.08.2011    source источник
comment
Вы можете просто позвонить TerminateProcess() и покончить с этим!   -  person David Heffernan    schedule 02.08.2011
comment
Я не могу. Требование - спокойно закрыть его с помощью WM_CLOSE.   -  person Wodzu    schedule 02.08.2011
comment
Похоже, это не работает! Когда это не удается, показывает ли приложение какой-либо пользовательский интерфейс?   -  person David Heffernan    schedule 02.08.2011


Ответы (1)


Посмотрите в этой статье базы знаний здесь, как максимально аккуратно закрыть другое приложение. Вы делаете это правильно до сих пор. Статья предполагает, что вы

  • сначала запости WM_CLOSE на все окна приложения (поскольку нельзя точно знать, какое из них главное).
  • ждать с тайм-аутом и если тайм-аут истекает
  • убить приложение с помощью TerminateProcess

Я согласен.

person Heinrich Ulbricht    schedule 02.08.2011