如何通过进程ID,得到程序主窗口的句柄?(100分)

  • 主题发起人 主题发起人 zhiyouqing
  • 开始时间 开始时间
Z

zhiyouqing

Unregistered / Unconfirmed
GUEST, unregistred user!
如何通过进程ID,得到程序主窗口的句柄?
 
通过进程ID获取句柄:OpenProcess等
具体实例:
function KillTask(ExeFileName: string): integer;{Uses Tlhelp32}
const
PROCESS_TERMINATE=$0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
result := 0;
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while integer(ContinueLoop) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) = UpperCase(ExeFileName))
or (UpperCase(FProcessEntry32.szExeFile) = UpperCase(ExeFileName))) then
Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE, BOOL(0), FProcessEntry32.th32ProcessID), 0));
ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);

end;
CloseHandle(FSnapshotHandle);
end;
 
通过进程ID获取句柄:OpenProcess 可以得到进程句柄,但窗口句柄呢?
 
杀死进程容易,我是想向主窗口发送模拟按键。。。
 
1.枚举: 每找到一个窗口后,先得到它的进程ID,然后和已知的作对比
2.可以用 EnumWindows 遍历系统中的顶层窗口,对每个窗口用 GetWindowThreadProcessId
得到该窗口所属的 Process 的 Id,再跟你用 ToolHelp API 得到的各 Process 的 Id 比
较就能知道该窗口属于哪个进程,但是不是进程的主窗口则不好判断,大概需要其他条件,
如已知进程主窗口的标题。
3.试一下:
unit Unit1;

interface

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

type
EnumThreadWndProc=function(hw:HWND;lP:LPARAM):boolean;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

function Enum(hw:HWND;lP:LPARAM):boolean;stdcall;

var
Form1: TForm1;
st:TStartUpInfo;
pp:array[1..3] of TProcessInformation;
ppp:Thandle;

implementation

{$R *.DFM}

function Enum(hw:HWND;lP:LPARAM):boolean;
var
Buffer:Array[0..255] of Char;
begin
if GetTopWindow(hw)<>0 then
begin
GetWindowText(hw,buffer,256);
TListBox(lp).items.addobject(buffer,Tobject(hw));
end;
Result:=true;
end;



procedure TForm1.Button1Click(Sender: TObject);
var
proc:EnumThreadWndProc;
i:integer;
begin
proc:=@Enum;
for i:=1 to 2 do
EnumThreadWindows(pp.dwThreadId,@proc,lparam(listbox1));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
FillChar(st,sizeof(st),#0);
with st do
begin
cb:=sizeof(st);
dwFlags:=StartF_UsesTDHandles or STARTF_USESHOWWINDOW;
lptitle:=nil;
wShowWindow:=SW_HIDE;
end;
CreateProcess(PChar('c:/program files/microsoft office/office/winword.exe'),
nil,nil,nil,true,DETACHED_PROCESS,nil,nil,st,pp[1]);
CreateProcess(PChar('c:/program files/microsoft office/office/Excel.exe'),
nil,nil,nil,true,DETACHED_PROCESS,nil,nil,st,pp[2]);
end;

procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
ppp:=Thandle(Listbox1.Items.Objects[Listbox1.ItemIndex]);
if IsWindowVisible(ppp) then
BringWindowToTop(ppp)
else
showwindow(ppp,sw_show);
end;

end.
 
为了在程序实现下述目的:
1.打开一个计算器供用户使用;
2.但只打开一次,如果没有关闭,下次再用按钮打开计算器时,把上次打开的调到前面;
3.程序关闭时,同时关闭可能打开的计算器;
说白了,就是如何调用一个外部程序,同时获得这个程序窗口的句柄

从以前讨论中,抄得部分代码,"组装"成下面的样子:用CreateProcess
打开计算器,用EnumThreadWindows查找打开的计算器窗口句柄,
用ShowWindows显示计算器,用TerminateProcess
或者TerminateThread关闭可能已经打开的计算器.

但是EnumThreadWindows部分的代码被说成是画蛇添足,因为
CreateProcess中已经有了窗口句柄?或者说是线程句柄?那么如何
用这个线程句柄得到窗口句柄呢?因为ShowWindows要使用一个窗口
句柄?EnumWindows好象又没办法找到这个特定的计算器窗口?不用
CreateProcess,有其他API可以即打开外部程序,又同时获得这个程序
的窗口句柄的好办法吗?或者直接用GetTopWindow? 实在想不通...

还有,用EnumThreadWindows时,会产生一个延时?外面要加个While,
不然会打开五六个计算器后,才能找到计算器窗口? why?

最后,千万别说"...都不知道?!",因为我确实不知道,代码都是抄的. :)

unit...

interface

uses...

type
EnumThreadWndProc = function(hWnd:HWND; lParam:LPARAM): Boolean;

TfrmCreateAndRestore = class(TForm)
...
end;

function Enum(hWnd:HWND; lParam:LPARAM): Boolean;stdcall;

var
frmCreateAndRestore: TfrmCreateAndRestore;
CalledWinHandle: THandle;
ProcessInfo: TProcessInformation;
temp: THandle;

implementation

{$R *.DFM}

function Enum(hWnd:HWND; lParam:LPARAM): Boolean;
{回调函数}
begin
if GetTopWindow(hWnd) <> 0 then
CalledWinHandle := hWnd;
Result:=True;
end;

procedure TfrmCreateAndRestore.btnOpenCalcClick(Sender: TObject);
{打开计算器,同时获得窗口句柄}
var
StartInfo: TStartupInfo;
CallBackProc: EnumThreadWndProc;
ExitCode: Cardinal;
begin
if CalledWinHandle <> 0 then
begin
if GetExitCodeProcess(ProcessInfo.hProcess, ExitCode) then
begin
if ExitCode = 0 then
begin
ShowMessage('Calc have exited'); //测试用的
CalledWinHandle := 0;
end;
end
else begin
ShowMessage('Can''t retrive Calc window'); //测试用的
CalledWinHandle := 0;
end;
end;
if CalledWinHandle = 0 then
begin
//创建进程,打开计算器
FillChar(StartInfo, SizeOf(StartInfo), #0);
with StartInfo do
begin
cb := SizeOf(StartInfo);
dwFlags := StartF_UseShowWindow;
wShowWindow := SW_NORMAL;
end;
if CreateProcess(nil, PChar('Calc'), nil, nil, True,
CREATE_DEFAULT_ERROR_MODE, nil, nil, StartInfo, ProcessInfo) then
begin
//获得窗口句柄
CallBackProc := @Enum;
while CalledWinHandle = 0 do
//如果不做这个While,就会要打开五六个计算器才能找到,why?
EnumThreadWindows(ProcessInfo.dwThreadId, @CallBackProc, 0);
end
else begin
ShowMessage('Create fail'); //测试用的
Exit;
end;
end;
//显示计算器
if IsWindowVisible(CalledWinHandle) then
begin
SetForeGroundWindow(CalledWinHandle);
ShowWindow(CalledWinHandle, SW_RESTORE);
end
else
ShowWindow(CalledWinHandle, SW_SHOW);
end;

procedure TfrmCreateAndRestore.FormClose(Sender: TObject;
var Action: TCloseAction);
//窗口关闭时,同时关闭可能已经打开的计算器
var
ProcessHandle: THandle;
begin
if ProcessInfo.dwProcessId <> 0 then
begin
ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessInfo.dwProcessId);
if not TerminateProcess(ProcessHandle, 0) then
//或者直接用 TerminateThread(ProcessInfo.hThread, 0)也行
ShowMessage('Close Calc error'); //测试用的
end;
end;
 
接受答案了.
 
后退
顶部