-->

WatiN and .net winforms WebBrowser control - is Di

2020-02-09 19:42发布

问题:

Our target is: Watin-enabled browser testing embedded in a .net winform.

Currently, we are using a .net WebBrowser control to embed browser behavior in a winform. We are attaching WatiN to the WebBroswer control on the form with code like this ( thanks prostynick ):

var thread = new Thread(() =>
{
    Settings.AutoStartDialogWatcher = false;
    var ie = new IE(webBrowser1.ActiveXInstance);
    ie.GoTo("http://www.google.com");
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

The problem is this - the "winform browser" needs to handle popups during testing/automation.

Question: How can popups be handled when Watin is attached to a winforms webBrowser control ( and not using its own WatiN-generated IE window )?
a) Can Watin's DialogWatcher still be used? If so ...how?
b) If not, then perhaps we can write our own DialogWatcher -- but we would need an hWnd or processID to add it. Where would we get correct hWnd or processId in this scenario where Waitin does not have its own window or process?

Thanks in advance for any ideas ...alternate approaches that reach the same target are welcome!

回答1:

I have just upgraded to newest version of WatiN (head revision - 1166 - in trunk: https://watin.svn.sourceforge.net/svnroot/watin/trunk/src/). Since there was a change in original DialogWatcher class it is now possible to use existing DialogWatcher with fewer code.

Create class:

public class WebBrowserIE : IE
{
    private IntPtr hwnd;

    public WebBrowserIE(WebBrowser webBrowserControl)
        : base(webBrowserControl.ActiveXInstance, false)
    {
        hwnd = webBrowserControl.FindForm().Handle;
        StartDialogWatcher();
    }

    public override IntPtr hWnd
    {
        get
        {
            return hwnd;
        }
    }

    protected override void Dispose(bool disposing)
    {
        hwnd = IntPtr.Zero;
        base.Dispose(disposing);
    }
}

Use it instead of original IE class and see disappearing javascript alert dialog:

var ie = new WebBrowserIE(webBrowser1);
var thread = new Thread(() =>
{
    ie.GoTo("http://www.plus2net.com/javascript_tutorial/window-alert.php");
    ie.Button(Find.ByValue("Click here to display alert message")).Click();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

Warning: Create WebBrowserIE instance outside the thread. Otherwise you will have to modify this class to avoid cross-thread operation when reading Handle property of Form.



回答2:

Oh man, this question comes up so many times and I always writing something like: But with a little bit of hacking you can create your own based on original DialogWatcher class (from: How to use watin with WebBrowser control?), so I dug in my source code to find it and I will just show how I did it. Maybe it's not perfect, but it works and I didn't have any problems with it.

  1. Create FormDialogWatcher class by copying original DialogWatcher, changing class name, namespace etc.
  2. I have deleted following fields and methods from original class. This is probably not needed, but you will probably use only one instance of WebBrowser controll, so you don't really need this code and I am not sure if it will work properly after the changes without deleting this. To delete:

    • private static IList<DialogWatcher> dialogWatchers
    • public static DialogWatcher GetDialogWatcher(IntPtr mainWindowHwnd)
    • public static DialogWatcher GetDialogWatcherFromCache(IntPtr mainWindowHwnd)
    • public static void CleanupDialogWatcherCache()
    • public void IncreaseReferenceCount()
    • public void DecreaseReferenceCount()
    • public int ReferenceCount { get; private set; }
    • private bool IsWindowOfIexploreProcess(Window window)
  3. In Start() method replace this:

    if (new Window(MainWindowHwnd).Exists())
    {
        var winEnumerator = new WindowsEnumerator();
        var windows = winEnumerator.GetWindows(win => true);
    

    with this:

    var mainWindow = new Window(MainWindowHwnd);
    if (mainWindow.Exists())
    {
        var winEnumerator = new WindowsEnumerator();
        var windows = winEnumerator.GetWindows(window => window.ProcessID == mainWindow.ProcessID);
    

    (the only real difference is inside GetWindows call)

  4. In HandleWindow(Window window) remove this line:

    if (!IsWindowOfIexploreProcess(window)) return;
    

That's all! To start it, just create it: new FormDialogWatcher(Handle) where Handle is just a property of Form. You can probably create it after creating IE object in your example code (LOL, I've just figured out, that there is my nick name in question :)) - Form_Load or something like that. It will start immediately (see constructor) and the main loop will break after the window cease to exist.

EDIT: Be aware that if you set this class (or WatiN settings) to close unhandled dialogs, then even your MessageBox.Show will be closed :)

EDIT 2 (important!): Whole explanation above is related to original DialogWatcher class taken from WatiN SVN trunk revision 1056. Direct link to this revision and file: http://watin.svn.sourceforge.net/viewvc/watin/trunk/src/Core/DialogHandlers/DialogWatcher.cs?revision=1056&content-type=text/plain&pathrev=1056