运行第三方程序,我找到三个方法
一个是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启动前那个欢迎界面,只不过在里面有用户名和密码框那种。不会的别回答,也别顶。别耽误大家时间。

解决方案 »

  1.   

       CreateProcess后, 就会获取线程ID(最后一个参数)。
       根据线程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;
      

  2.   

    以前有一个朋友帮我解决过监控程序的方法,如下:http://www.mini188.com/showtopic-136.aspx 里面有一个现成代码。可以去下载附件至于登录的实现,使用遍历窗口的方式吧。1楼有答案了,如果是挺定的程序的话,可以使用SPY来查找相关信息,再实现相关的代码。
      

  3.   

    FindWindow(类名,标题名);没有标题名,可用SPY查类名
    另外窗体的控件句柄可用FindWindowEx来获取
      

  4.   

    to 5207:你说的代码我下载了,下面这部分没看懂。能解释一下吗?
    我现在没明白这个程序跟我要的怎么联系到一起。
    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;
      

  5.   

    to etomahawk:
    CreateProcess后, 就会获取线程ID(最后一个参数)。 
       根据线程ID,枚举窗体(EnumWindow),判断窗体的的进程ID是否是返回的句柄(GetWindowThreadProcessId可以获取创建窗口线程ID)。再根据相关的信息,就可以获取弹出窗体的句柄。这句话,没完全理解。能继续解释一下吗?另外你举例判断用户名和密码框的办法不好,因为如果一个窗体上有很多个文本框呢?不能简单根据是否是密码来区分。
      

  6.   

    to 5207:
    我想判断的是目标窗口已经出现,而不是程序是不是处于STILL_ACTIVE状态。
    CreateProcess之后,程序肯定是STILL_ACTIVE状态吧
      

  7.   

    写这样的程序最后用Spy++看一下你关心的文本框的类名和组件名,这样用FindWindowEx就有针对性。
    下面是刚回复的一个帖子,我已经测过了,不知道对你有没有用:
    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;
      

  8.   

    只是一个自动登录程序,不需要这么麻烦,用CreateProcess的时候,让启动的程序是获得焦点的,然后用keybd_event函数模拟键盘输入就可以了,不需要句柄
      

  9.   

    to blazingfire:
    谢谢你的回答。但发现
    AFindData.hWindow有的时候并不是程序窗口句柄,不知道为什么。这样有的时候能达到效果,有的时候就不能。
    目标程序运行起来后,用Spy++查看程序窗口句柄,然后跟AFindData.hWindow对比,发现两者并不相等。
      

  10.   

    winexec+findwindow不行吗?这个办法部分情况下行,但不能保证一定行。
      

  11.   

    winexec+findwindow也可以的,但是准确性差一点
      

  12.   

    能麻烦再说说keybd_event(...); 怎么用吗?
    另外“让启动的程序是获得焦点的”这么怎么理解呢?怎么保证它是获得焦点的呢?没太理解,谢谢大家