如何确定浏览器关闭的时间? [英] How to determine when the browser is closed?

查看:273
本文介绍了如何确定浏览器关闭的时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Selenium Webdriver自动化网站(填写表格并点击)以节省用户的时间。我遇到了一个恼人的问题:

I am automating a website (filling forms and clicking around) with Selenium Webdriver to save time for my users. I have encountered an annoying problem though:


Selenium似乎不支持浏览器
本身的任何事件监听器。当浏览器关闭时 driver.quit() 未被调用
并且仍然存在一个不可用的驱动程序,它会抛出各种异常。让
无法知道浏览器什么时候关闭,我无法创建一个新的
驱动程序实例。

Selenium does not seem to support any event listeners for the browsers themselves. When the browser is closed driver.quit() is not called and an unusable driver remains which throws various exceptions. Having no way to know when the browser is closed, I cannot create a new driver instance.

我需要的是在浏览器关闭时通知我的程序的一些方法。

What I need is some way to notify my program when the browser is closed.

用户可能会因为破坏我程序的任何原因而关闭浏览器。如果不重新启动应用程序,用户将无法再次运行自动化任务。知道浏览器何时关闭将允许我调用 driver.quit()并在用户想要再次运行时创建一个新的驱动程序实例。

The user might close the browser for whatever reason which breaks my program. The user cannot run the automated tasks again without restarting the application. Knowing when the browser is closed would allow me to call driver.quit() and make a new driver instance if the user wants to run it again.

当浏览器死机时,抛出的错误在浏览器中并不统一,这个问题变得更加复杂。使用Firefox我可能会得到一个UnreachableBrowserException,包含Chrome NullPointer和WebDriverExceptions。

The problem is further complicated by the fact that when the browser dies the error thrown is not uniform across browsers. With Firefox I might get an UnreachableBrowserException, with Chrome NullPointer and WebDriverExceptions.

为了澄清,我知道如何关闭驱动程序和浏览器,但我不知道什么时候它们被外部资源关闭。这可以在Selenium 2中以跨浏览器的方式完成(如果是,如何)或者我是否需要找到另一种方式(如另一个库)来观看浏览器窗口?

To clarify, I know how to close the driver and the browser, but I don't know when they are closed by external sources. Can this be done in Selenium 2 in a cross-browser way (and if yes, how) or do I need to find another way (like another library) to watch the browser window?

推荐答案

我已经在Selenium中已经包含的JNA(Java Native Access)3.4的帮助下解决了这个问题。我的目标平台仅限Windows,但制作这种跨平台不需要太多工作。我必须执行以下操作:

I have solved the problem with the help of JNA (Java Native Access) 3.4 which was already included with Selenium. My target platform is Windows only, but it shouldn't take much work to make this cross-platform. I had to do the following:


  1. 在启动WebDriver之前收集所有浏览器进程ID(这可以通过 tasklist 或powershell Get-Process )。如果您确定在启动应用程序之前没有运行浏览器进程,则可以省略此步骤。

  2. 初始化驱动程序。

  3. 收集再次使用所有浏览器进程,然后取两者之间的区别。

  4. 使用LittlePanda评论中链接的代码作为基础,我们可以在新线程上创建一个观察者服务。当所有进程都已关闭时,这将提醒所有听众。

  5. Kernel32.INSTANCE.OpenProcess 方法允许我们从pid创建 HANDLE 对象。

  6. 使用这些句柄,我们可以让线程等到所有进程都发出信号 Kernel32.INSTANCE.WaitForMultipleObjects 方法。

  1. Collect all browser process IDs before launching WebDriver (this can be done with utilities like tasklist or powershell Get-Process). If you know for certain there will be no browser processes running before launching your application, this step can be omitted.
  2. Initialize the driver.
  3. Collect all browser processes again, then take the difference between the two.
  4. Using the code linked in LittlePanda's comment as a base, we can create a watcher service on a new thread. This will alert all listeners when all the processes have closed.
  5. The Kernel32.INSTANCE.OpenProcess method allows us to create HANDLE objects from the pids.
  6. With these handles we can have the thread wait until all processes are signaled with the Kernel32.INSTANCE.WaitForMultipleObjects method.






这是我使用的代码,以便它可以帮助其他人:


Here's the code I used so that it might help others:

public void startAutomation() throws IOException { 
        Set<Integer> pidsBefore = getBrowserPIDs(browserType);
        automator.initDriver(browserType); //calls new ChromeDriver() for example
        Set<Integer> pidsAfter = getBrowserPIDs(browserType);
        pidsAfter.removeAll(pidsBefore);

        ProcessGroupExitWatcher watcher = new ProcessGroupExitWatcher(pidsAfter);
        watcher.addProcessExitListener(new ProcessExitListener() {
            @Override
            public void processFinished() {
                if (automator != null) {
                    automator.closeDriver(); //calls driver.quit()
                    automator = null;
                }
            }
        });
        watcher.start();
        //do webdriver stuff
}

private Set<Integer> getBrowserPIDs(String browserType) throws IOException {
    Set<Integer> processIds = new HashSet<Integer>();

    //powershell was convenient, tasklist is probably safer but requires more parsing
    String cmd = "powershell get-process " + browserType + " | foreach { $_.id }";
    Process processes = Runtime.getRuntime().exec(cmd);
    processes.getOutputStream().close(); //otherwise powershell hangs
    BufferedReader input = new BufferedReader(new InputStreamReader(processes.getInputStream()));
    String line;

    while ((line = input.readLine()) != null) {
        processIds.add(Integer.parseInt(line));
    }
    input.close();

    return processIds;
}

观察者代码:

/**
* Takes a <code>Set</code> of Process IDs and notifies all listeners when all
* of them have exited.<br>
*/
public class ProcessGroupExitWatcher extends Thread {
    private List<HANDLE> processHandles;
    private List<ProcessExitListener> listeners = new ArrayList<ProcessExitListener>();

    /**
     * Initializes the object and takes a set of pids and creates a list of
     * <CODE>HANDLE</CODE>s from them.
     *
     * @param processIds
     *           process id numbers of the processes to watch.
     * @see HANDLE
     */
    public ProcessGroupExitWatcher(Set<Integer> processIds) {
        processHandles = new ArrayList<HANDLE>(processIds.size());
        //create Handles from the process ids
        for (Integer pid : processIds) {
            processHandles.add(Kernel32.INSTANCE.OpenProcess(Kernel32.SYNCHRONIZE, false, pid)); //synchronize must be used
        }
    }

    public void run() {
        //blocks the thread until all handles are signaled
        Kernel32.INSTANCE.WaitForMultipleObjects(processHandles.size(), processHandles.toArray(new HANDLE[processHandles.size()]), true,
            Kernel32.INFINITE);
       for (ProcessExitListener listener : listeners) {
            listener.processFinished();
       }
    }

    /**
     * Adds the listener to the list of listeners who will be notified when all
     * processes have exited.
     *
     * @param listener
     */
     public void addProcessExitListener(ProcessExitListener listener) {
         listeners.add(listener);
     }

    /**
     * Removes the listener.
     * 
     * @param listener
     */
    public void removeProcessExitListener(ProcessExitListener listener) {
         listeners.remove(listener);
    }
}

注意:使用时以这种方式使用powershell,执行用户的配置文件脚本。这可能会产生意外的输出,从而破坏了上述代码。因此,建议使用任务列表。

NOTE: when using powershell this way, the user's profile scripts are executed. This can create unexpected output which breaks the above code. Because of this, using tasklist is much more recommended.

这篇关于如何确定浏览器关闭的时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆