WatiN LogonDialogHandlers not working correctly in Windows 7

时间秒杀一切 提交于 2019-12-17 16:57:07

问题


I have recently updated to Windows 7, VS2010 and IE8. We have an automation suite running tests against IE using WatiN. These tests require the logon dialog handler to be used in order to log different AD Users into the IE Browser.

This works perfectly when using Windows XP and IE8, but now using Windows 7 has resulted in the Windows Security dialog box no longer being recognised, the dialogue box is just being ignored. This is the method being used to startup the browser:

        public static Browser StartBrowser(string url, string username, string password)
        {
            Browser browser = new IE();
            WatiN.Core.DialogHandlers.LogonDialogHandler ldh = new WatiN.Core.DialogHandlers.LogonDialogHandler(username, password);
            browser.DialogWatcher.Add(ldh);
            browser.GoTo(url);
            return browser;
        }

any suggestions or help would be greatly appreciated...


回答1:


For whatever reason the code Clint posted had comments instead of entering the username, password and submitting, and referenced an undefined method, but is otherwise OK. Here's some completed (and working) code:

    public static Browser Win7Login(string username, string password, string URL) {
        Process ieProcess = Process.Start("iexplore.exe", URL);
        ieProcess.WaitForInputIdle();

        Thread.Sleep(2000);

        AutomationElement ieWindow = AutomationElement.FromHandle(ieProcess.MainWindowHandle);
        string t = ieWindow.Current.ClassName.ToString();

        Condition conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
        Condition List_condition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
        Condition Edit_condition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                    new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
        Condition button_conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                     new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

        AutomationElementCollection c = ieWindow.FindAll(TreeScope.Children, conditions);
        foreach (AutomationElement child in c) {
            if (child.Current.ClassName.ToString() == "#32770") {
                // find the list
                AutomationElementCollection lists = child.FindAll(TreeScope.Children, List_condition);
                // find the buttons
                AutomationElementCollection buttons = child.FindAll(TreeScope.Children, button_conditions);

                foreach (AutomationElement list in lists) {
                    if (list.Current.ClassName.ToString() == "UserTile") {
                        AutomationElementCollection edits = list.FindAll(TreeScope.Children, Edit_condition);
                        foreach (AutomationElement edit in edits) {
                            if (edit.Current.Name.Contains("User name")) {
                                edit.SetFocus();
                                ValuePattern usernamePattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                                usernamePattern.SetValue(username);
                            }
                            if (edit.Current.Name.Contains("Password")) {
                                edit.SetFocus();
                                ValuePattern passwordPattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                                passwordPattern.SetValue(password);
                            }
                        }
                    }
                }
                foreach (AutomationElement button in buttons) {
                    if (button.Current.AutomationId == "SubmitButton") {
                        InvokePattern submitPattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
                        submitPattern.Invoke();
                        break;
                    }
                }
            }
        }
        return IE.AttachTo<IE>(Find.By("hwnd", ieWindow.Current.NativeWindowHandle.ToString()), 30);
    }



回答2:


This can also be refactored as a DialogHandler like this:

public class Windows7LogonDialogHandler : BaseDialogHandler
{
    private readonly string _username;
    private readonly string _password;
    AndCondition _conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));

    readonly AndCondition _listCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                    new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));

    readonly AndCondition _editCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));

    readonly AndCondition _buttonConditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                 new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

    public Windows7LogonDialogHandler(string username, string password)
    {
        _username = username;
        _password = password;
    }

    public override bool HandleDialog(Window window)
    {
        if(CanHandleDialog(window))
        {
            var win = AutomationElement.FromHandle(window.Hwnd);
            var lists = win.FindAll(TreeScope.Children, _listCondition);
            var buttons = win.FindAll(TreeScope.Children, _buttonConditions);
            var another = (from AutomationElement list in lists
                           where list.Current.ClassName == "UserTile"
                           where list.Current.Name == "Use another account"
                           select list).First();
            another.SetFocus();

            foreach (var edit in from AutomationElement list in lists
                                 where list.Current.ClassName == "UserTile"
                                 select list.FindAll(TreeScope.Children, _editCondition)
                                     into edits
                                     from AutomationElement edit in edits
                                     select edit)
            {
                if (edit.Current.Name.Contains("User name"))
                {
                    edit.SetFocus();
                    var usernamePattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                    if (usernamePattern != null) usernamePattern.SetValue(_username);
                }
                if (edit.Current.Name.Contains("Password"))
                {
                    edit.SetFocus();
                    var passwordPattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                    if (passwordPattern != null) passwordPattern.SetValue(_password);
                }
            }
            foreach (var submitPattern in from AutomationElement button in buttons
                                          where button.Current.AutomationId == "SubmitButton"
                                          select button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern)
            {
                submitPattern.Invoke();
                break;
            }
            return true;
        }
        return false;
    }

    public override bool CanHandleDialog(Window window)
    {
        return window.ClassName == "#32770";
    }
}

Which is a little bit nicer. You can then use it like this:

using(var ie = new IE())
        {
            ie.DialogWatcher.Add(new Windows7LogonDialogHandler(@"domain\user", "password"));
            ie.GoTo("http://mysecuredwebsite");
        }



回答3:


We have eventually solved this issue by using the Windows Automation 3.0 API to pick up the dialogue box and handle the logging in. This was done as follows:

  1. Fire up an IE process and assign it to an AutomationElement
  2. You now have the ability to traverse through the child elements of the IEFrame, picking up the username and password Edit fields.
  3. Then send the username and password

Once the browser has been authenticated we then attach it to a WatiN IE browser object. The code follows below:

        public static Browser LoginToBrowser(string UserName, string Password, string URL)
    {

        AutomationElement element = StartApplication("IEXPLORE.EXE", URL);
        Thread.Sleep(2000);
        string t = element.Current.ClassName.ToString();

             Condition conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
            Condition List_condition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
            Condition Edit_condition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
            Condition button_conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                         new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

        AutomationElementCollection c = element.FindAll(TreeScope.Children, conditions);
        foreach (AutomationElement child in c)
        {
            if (child.Current.ClassName.ToString() == "#32770")
            {
                //find the list
                AutomationElementCollection lists = child.FindAll(TreeScope.Children, List_condition);
                // find the buttons
                AutomationElementCollection buttons = child.FindAll(TreeScope.Children, button_conditions);

                foreach (AutomationElement list in lists)
                {
                    if (list.Current.ClassName.ToString() == "UserTile")
                    {
                        AutomationElementCollection edits = list.FindAll(TreeScope.Children, Edit_condition);
                        foreach (AutomationElement edit in edits)
                        {
                            if (edit.Current.Name.Contains("User name"))
                            {
                                edit.SetFocus();
                                //send the user name
                            }
                            if (edit.Current.Name.Contains("Password"))
                            {
                                edit.SetFocus();
                                //send the password
                            }
                        }
                    }
                }
                foreach (AutomationElement button in buttons)
                {
                    if (button.Current.AutomationId == "SubmitButton")
                    {
                        //click the button 
                        break;
                    }
                }
            }            
        }
        return IE.AttachToIE(Find.By("hwnd", element.Current.NativeWindowHandle.ToString()), 30) ;
    }

We did use a tool called UI Spy to examine the Windows UI. If you run it against XP and Win7 you can clearly see how the structure of the Windows Security dialogue box has changed between the 2 OS's.




回答4:


Nicholas Riley post works like a charm, however including the using System.Windows.Automation might be a little tricky. I thought microsoft would put this in the GAC, but they do not, at least for me running Windows 7 professional. I even downloaded the Automation Toolkit from here.

Turns out, there is a subject here on stack overflow that shows where the dll's are that you can browse to include as references in your project. The link for that is here.

Essentially, you just need to reference two dll's. UIAutomationClient.dll and UIAutomationTypes.dll (both in the same directory).




回答5:


Since nobody answered your question, I will, but unfortunately without ready solution.

I don't have Windows 7 to try at this moment, but it seems that WatiN's LogonDialogHandler is not compatible with Windows 7, so you have to write your own DialogHandler. The simplest way is to inherit from BaseDialogHandler. You can look at the source code of existing dialog handlers in WatiN. I made my self very simple and not universal one for handling certificate dialog. WinSpy++ can be very useful during implementation.




回答6:


If you set whatever process is running your watin to run as administrator in Windows 7, the DialogHandlers work just fine.




回答7:


I tried to use the two automation examples above and found that they did not handle the scenario when the other credentials have been remembered in which case you only see the password in box. In this case you need to programmatically click the "Use another account" section. So I modified the supplied code to do that and it is now working OK. Here's the modified code:

public static Browser Win7Login(string username, string password, string url)
    {
        var ieProcess = Process.Start("iexplore.exe", url);
        ieProcess.WaitForInputIdle();

        Thread.Sleep(2000);

        var ieWindow = AutomationElement.FromHandle(ieProcess.MainWindowHandle);

        var conditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
        var listCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
        var editCondition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                    new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
        var buttonConditions = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true),
                                     new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));

        var c = ieWindow.FindAll(TreeScope.Children, conditions);

        foreach (AutomationElement child in c)
        {
            if (child.Current.ClassName == "#32770")
            {
                // find the list
                var lists = child.FindAll(TreeScope.Children, listCondition);
                // find the buttons
                var buttons = child.FindAll(TreeScope.Children, buttonConditions);

                var another = (from AutomationElement list in lists
                              where list.Current.ClassName == "UserTile"
                              where list.Current.Name == "Use another account"
                              select list).First();

                another.SetFocus();

                foreach (var edit in from AutomationElement list in lists
                                                   where list.Current.ClassName == "UserTile"
                                                   select list.FindAll(TreeScope.Children, editCondition)
                                                   into edits from AutomationElement edit in edits select edit)
                {
                    if (edit.Current.Name.Contains("User name"))
                    {
                        edit.SetFocus();
                        var usernamePattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                        if (usernamePattern != null) usernamePattern.SetValue(username);
                    }
                    if (edit.Current.Name.Contains("Password"))
                    {
                        edit.SetFocus();
                        var passwordPattern = edit.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                        if (passwordPattern != null) passwordPattern.SetValue(password);
                    }
                }
                foreach (var submitPattern in from AutomationElement button in buttons
                                                        where button.Current.AutomationId == "SubmitButton"
                                                        select button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern)
                {
                    submitPattern.Invoke();
                    break;
                }
            }
        }
        return IE.AttachTo<IE>(Find.By("hwnd", ieWindow.Current.NativeWindowHandle.ToString()), 30);
    }

Thanks to the others who got me most of the way there.



来源:https://stackoverflow.com/questions/2841120/watin-logondialoghandlers-not-working-correctly-in-windows-7

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!