运行第三方程序,我找到三个方法
一个是ShellExecute,一个是WinExec,一个是CreateProcess
我倾向于使用CreateProcess,因为根据其返回值我能确定知道程序是否运行。
现在无论用哪种方法,第三方程序肯定都能运行起来,问题是我怎么获取这个程序窗口的句柄呢?
以大家比较熟悉的做Oracle开发常用的PLSQL Developer为例,程序是运行起来了,我怎么获取那个登录窗口的句柄呢?
我本来是根据那个登录窗口标题“Oracle Logon”,用FindWindow来找的,但是因为程序运行起来需要一定时间,所以直接用FindWindow可能并不能立刻获取到这个登录窗口句柄。
有人说用sleep来处理,但我发现窗口句柄是取到了,但是取里面的用户名和密码框窗口的句柄却不能保证取到。
我的做法是这样的,首先我用别的程序可以判断出来在这个登录窗体中,用户名框和密码框在枚举登录窗口子窗体过程中的顺序,分别是2和1,然后在CreateProcess启动PLSQL Developer后,循环用FindWindow根据登录窗口标题“Oracle Logon”找登录窗口句柄,一直到句柄不为0才终止循环,然后枚举这个登录窗口里的所有子窗口,顺序是2的子窗口句柄就认为是用户名框句柄,顺序是1的就认为是密码框句柄。但我发现实际上我取出来用户名框和密码框句柄有的时候对,有的时候就不对(根据句柄取得对应class就知道不对了)。
不知道为什么。而且这个在循环中使用sleep我总感觉效率很低,如果没找到句柄就一直循环,很消耗资源啊。
也有人说CreateProcess后,
CreateProcResult := CreateProcess(nil, PChar(AppPath), nil, nil, false,
DETACHED_PROCESS, nil, nil, StartUpInfo, ProcessInformation);
其中的ProcessInformation.hProcess就是句柄,但我发现那个并不是登录窗口句柄。其实我就是想做一个自动登录程序,程序运行后自动填入用户名和密码,并执行登录。
所以请教大家,
1、到底怎样才能保证拿到登录窗口句柄和里面的用户名框密码框句柄,最好是窗口出来就立刻捕捉到,不出现等待???
2、如果是一个没有窗口标题的登录窗口,那我又怎么取它的窗口句柄呢?有很多程序启动之后,都是只有一个没有边框的登录窗口的,就像Delphi启动前那个欢迎界面,只不过在里面有用户名和密码框那种。不会的别回答,也别顶。别耽误大家时间。
一个是ShellExecute,一个是WinExec,一个是CreateProcess
我倾向于使用CreateProcess,因为根据其返回值我能确定知道程序是否运行。
现在无论用哪种方法,第三方程序肯定都能运行起来,问题是我怎么获取这个程序窗口的句柄呢?
以大家比较熟悉的做Oracle开发常用的PLSQL Developer为例,程序是运行起来了,我怎么获取那个登录窗口的句柄呢?
我本来是根据那个登录窗口标题“Oracle Logon”,用FindWindow来找的,但是因为程序运行起来需要一定时间,所以直接用FindWindow可能并不能立刻获取到这个登录窗口句柄。
有人说用sleep来处理,但我发现窗口句柄是取到了,但是取里面的用户名和密码框窗口的句柄却不能保证取到。
我的做法是这样的,首先我用别的程序可以判断出来在这个登录窗体中,用户名框和密码框在枚举登录窗口子窗体过程中的顺序,分别是2和1,然后在CreateProcess启动PLSQL Developer后,循环用FindWindow根据登录窗口标题“Oracle Logon”找登录窗口句柄,一直到句柄不为0才终止循环,然后枚举这个登录窗口里的所有子窗口,顺序是2的子窗口句柄就认为是用户名框句柄,顺序是1的就认为是密码框句柄。但我发现实际上我取出来用户名框和密码框句柄有的时候对,有的时候就不对(根据句柄取得对应class就知道不对了)。
不知道为什么。而且这个在循环中使用sleep我总感觉效率很低,如果没找到句柄就一直循环,很消耗资源啊。
也有人说CreateProcess后,
CreateProcResult := CreateProcess(nil, PChar(AppPath), nil, nil, false,
DETACHED_PROCESS, nil, nil, StartUpInfo, ProcessInformation);
其中的ProcessInformation.hProcess就是句柄,但我发现那个并不是登录窗口句柄。其实我就是想做一个自动登录程序,程序运行后自动填入用户名和密码,并执行登录。
所以请教大家,
1、到底怎样才能保证拿到登录窗口句柄和里面的用户名框密码框句柄,最好是窗口出来就立刻捕捉到,不出现等待???
2、如果是一个没有窗口标题的登录窗口,那我又怎么取它的窗口句柄呢?有很多程序启动之后,都是只有一个没有边框的登录窗口的,就像Delphi启动前那个欢迎界面,只不过在里面有用户名和密码框那种。不会的别回答,也别顶。别耽误大家时间。
根据线程ID,枚举窗体(EnumWindow),判断窗体的的进程ID是否是返回的句柄(GetWindowThreadProcessId可以获取创建窗口线程ID)。再根据相关的信息,就可以获取弹出窗体的句柄。 自动输入用户名和密码,可以通过模拟键盘输入来进行。不一定要获取用户名和密码框的句柄。模拟键盘输入:keybd_event(...); //使用看MSDN 如果你非要获取用户名和密码输入框的句柄,只能枚举子窗体,然后根据窗体的类名和样式来判断。 GetClassName(hWnd, sName);
if UpperCase(sName) = 'EDIT' then
begin
if (GetWindowLong(hWnd, GWL_STYLE) and ES_PASSWORD <> 0) then
ShowMessage('密码输入框!');
else
ShowMessage('用户名输入框!');
end;
另外窗体的控件句柄可用FindWindowEx来获取
我现在没明白这个程序跟我要的怎么联系到一起。
GetExitCodeProcess(ShExecInfo.hProcess,exCode);
while(exCode = STILL_ACTIVE)do
begin
Application.ProcessMessages;
if(PeekMessage(mesg,HWND(nil),0,0,PM_REMOVE)) then
begin
TranslateMessage(mesg);
DispatchMessage(mesg);
end;
GetExitCodeProcess(ShExecInfo.hProcess,exCode);//}
end;
CreateProcess后, 就会获取线程ID(最后一个参数)。
根据线程ID,枚举窗体(EnumWindow),判断窗体的的进程ID是否是返回的句柄(GetWindowThreadProcessId可以获取创建窗口线程ID)。再根据相关的信息,就可以获取弹出窗体的句柄。这句话,没完全理解。能继续解释一下吗?另外你举例判断用户名和密码框的办法不好,因为如果一个窗体上有很多个文本框呢?不能简单根据是否是密码来区分。
我想判断的是目标窗口已经出现,而不是程序是不是处于STILL_ACTIVE状态。
CreateProcess之后,程序肯定是STILL_ACTIVE状态吧
下面是刚回复的一个帖子,我已经测过了,不知道对你有没有用:
procedure TForm1.Button1Click(Sender: TObject);
type
//定义一个结构体,用来返回启动窗体的句柄
PFindData = ^TFindData;
TFindData = record
hProcess: THandle;
hWindow: THandle;
end; function EnumWindowsProc(Handle: THandle; lParam: LPARAM): Boolean; stdcall;
var
hProcess: THandle;
FindData: PFindData;
begin
Result := True; //继续枚举一下窗体
FindData := PFindData(lParam);
GetWindowThreadProcessId(Handle, hProcess); //取得窗体进程ID号
if hProcess = FindData^.hProcess then
begin
FindData^.hWindow := Handle; //已经找到!!
Result := False;
end;
end;var
FileName: string;
ProcessInfo: TProcessInformation;
StartUpInfo: TStartupInfo;
hButton: THandle;
AFindData: TFindData;
begin
FileName := ExtractFilePath(ParamStr(0)) + 'Project2.exe'; FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
StartUpInfo.wShowWindow := SW_SHOW; if CreateProcess(nil, PChar(FileName), nil, nil,
False, IDLE_PRIORITY_CLASS, nil, nil, StartUpInfo,
ProcessInfo) then
begin
//等就直到另外一下程序都准备好了
WaitForInputIdle(ProcessInfo.hProcess, INFINITE); AFindData.hProcess := ProcessInfo.dwProcessId;
AFindData.hWindow := 0;
EnumWindows(@EnumWindowsProc, Integer(@AFindData));
if AFindData.hWindow <> 0 then
begin
hButton := FindWindowEx(AFindData.hWindow, 0, 'TButton', 'Button1');
if hButton <> 0 then
SendMessage(hButton, CN_COMMAND, BN_CLICKED, 0);
end; CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end;
谢谢你的回答。但发现
AFindData.hWindow有的时候并不是程序窗口句柄,不知道为什么。这样有的时候能达到效果,有的时候就不能。
目标程序运行起来后,用Spy++查看程序窗口句柄,然后跟AFindData.hWindow对比,发现两者并不相等。
另外“让启动的程序是获得焦点的”这么怎么理解呢?怎么保证它是获得焦点的呢?没太理解,谢谢大家