仅具有进程 ID 时关闭主应用程序窗口时出现问题
我有进程的 ID。该进程是一个具有主窗口的应用程序。 我试图通过向其主窗口发送 WM_CLOSE 来关闭该应用程序。 我正在使用 EnumWindows
搜索其主窗口。
问题是,我尝试关闭的这个应用程序并不总是关闭。 它是多线程应用程序。当我使用下面介绍的相同方法时,记事本和计算器总是关闭。但我不确定它是否正常工作,因为它返回给我同一个窗口的许多句柄,即使对于 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.
I have ID of the process. This process is an application which have a main window.
I am trying to close this application by sending WM_CLOSE to its main window.
I am searching its main window by using EnumWindows
.
The problem is, that this application which I try to close, does not close always.
It is multithreaded application. Notepad and Calc are always closing when I use the same method which is presented below. But I am not sure if it is working properly cause it returns me many handles to the same window, even for Calc.exe.
Is it possible that thread is taking a handle to window and then this handle somehow become corrupted? Or maybe I should not use GetWindowThreadProcessId(hHwnd,pPid)
but some other function in the callback?
I am out of ideas, would be grateful for any help. Thanks.
Code snippet:
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
请参阅此处这篇知识库文章,了解如何尽可能干净地关闭另一个应用程序。到目前为止你做得对。本文建议您
我同意。
Have a look in this Knowledge Base Article here on how to close another application as cleanly as possible. You are doing it right so far. The Article suggests that you
I agree.