【问题标题】:Regularly invoking an interactive process (WatiN tests) via Task Scheduler通过任务计划程序定期调用交互式进程(WatiN 测试)
【发布时间】:2010-12-02 15:27:12
【问题描述】:

我正在使用一套 WatiN 测试驱动 IE 进行一些定期的健全性检查,以监控网站。

当我以交互方式调用它和/或将任务计划程序中的任务配置为“仅在用户登录时运行”时,该套件工作正常。

但是,当我将它设置为“无论用户是否登录时运行”,并选中“以最高权限运行”选项(在 Windows Server 2008 和许多其他操作系统下,WatiN 无法令人满意地与浏览器对话没有管理员权限),WatiN 无法与它的 iexplore.exe 实例进行令人满意的通信(它们启动,但我得到一个timeout exception as detailed in this post)。我已将我正在访问的站点添加到 IE 的管理员和非管理员上下文的受信任站点中。我尝试过使用和不使用提升,使用和不禁用 ESC,以及使用和不关闭互联网区域的保护模式。由于我的非 GUI 测试很开心,我认为这是在非交互式计划任务的上下文中可能的交互类型的限制,即使“以最高权限运行”也是如此。

目前,我的临时解决方法是要求 [TS] 会话始终保持打开状态,准备好运行计划任务。

如果我要坚持这一点,我至少会添加一个心跳通知,以允许某些东西监控任务实际上正在运行[例如,如果有人注销会话或重新启动机器]。

但是,我正在寻找更永久的东西——能够在我的 Windows Server 2008 [x64] 机器上定期调用我的 WatiN 测试 [使用 xunit-console.x86.exe v 1.5 运行] 的东西,就像任务计划程序,但具有适当的交互式会话。

如果可能的话,我宁愿不使用 psexec 或 remcom,而且除了添加另一个故障点之外,我看不到创建 Windows 服务会如何做任何事情,但我很想听听所有经过验证的解决方案.

【问题讨论】:

    标签: windows-server-2008 watin scheduled-tasks web-testing


    【解决方案1】:

    我能够在“无论用户是否登录时运行”模式下使用计划任务运行 Watin 测试。 在我的情况下,我将问题跟踪到 m_Proc.MainWindowHandle 始终为 0,当 IE 是从没有登录用户的情况下运行的计划任务创建时。 在 Watin 来源中,这是在 IE.cs:CreateIEPartiallyInitializedInNewProcess 函数中

    我的解决方法是手动枚举顶级窗口并找到属于进程的 className == "IEFrame" 的窗口,而不是使用 Process.MainWindowHandle 属性。

    这里是代码 sn-p。我直接从 Watin 源复制了所有 pinvoke。

       public static class IEBrowserHelper
        {
            private static Process CreateIExploreInNewProcess()
            {
                var arguments = "about:blank";
    
                arguments = "-noframemerging " + arguments;
    
                var m_Proc = Process.Start("IExplore.exe", arguments);
                if (m_Proc == null) throw new WatiN.Core.Exceptions.WatiNException("Could not start IExplore.exe process");
    
                return m_Proc;
            }
            class IeWindowFinder
            {
                #region Interop
                [DllImport("user32.dll", SetLastError = true)]
                static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
                public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
                [DllImport("user32.dll", SetLastError = true)]
                public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
                [DllImport("user32", EntryPoint = "GetClassNameA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
                internal static extern int GetClassName(IntPtr handleToWindow, StringBuilder className, int maxClassNameLength);
                #endregion
    
                readonly Process IeProcess;
                IntPtr HWnd = IntPtr.Zero;
    
                public IeWindowFinder(Process ieProcess)
                {
                    this.IeProcess = ieProcess;
                }
                public IntPtr Find()
                {
                    EnumWindows(FindIeWindowCallback, IntPtr.Zero);
                    return HWnd;
                }
    
                bool FindIeWindowCallback(IntPtr hWnd, IntPtr lParam)
                {
                    uint processId;
                    GetWindowThreadProcessId(hWnd, out processId);
                    if (processId == IeProcess.Id)
                    {
                        int maxCapacity = 255;
                        var sbClassName = new StringBuilder(maxCapacity);
                        var lRes = GetClassName(hWnd, sbClassName, maxCapacity);
                        string className = lRes == 0 ? String.Empty : sbClassName.ToString();
                        if (className == "IEFrame")
                        {
                            this.HWnd = hWnd;
                            return false;
                        }
                    }
                    return true;
                }
            }
    
            public static WatiN.Core.IE CreateIEBrowser()
            {
                Process ieProcess = CreateIExploreInNewProcess();
    
                IeWindowFinder findWindow = new IeWindowFinder(ieProcess);
    
                var action = new WatiN.Core.UtilityClasses.TryFuncUntilTimeOut(TimeSpan.FromSeconds(WatiN.Core.Settings.AttachToBrowserTimeOut))
                {
                    SleepTime = TimeSpan.FromMilliseconds(500)
                };
    
                IntPtr hWnd = action.Try(() =>
                {
                    return findWindow.Find();
                });
                ieProcess.Refresh();
                return  WatiN.Core.IE.AttachTo<WatiN.Core.IE>(
                    new WatiN.Core.Constraints.AttributeConstraint("hwnd", hWnd.ToString()), 5);
            }
        }
    

    然后使用 IEBrowserHelper.CreateIEBrowser() 代替 new IE()

    【讨论】:

    • 有趣。它适用于哪些操作系统?我个人不再有此要求,因此即使将其视为解决方案也感到不舒服,因此接受还有很长的路要走。我无法想象实际上依赖于这个 hacky IRL 的任何东西,但为了花时间,这里是一个 +1
    • 我自己也有这个要求,开始挖掘,在我用谷歌搜索后,我不得不深入研究并调试 Watin。它现在肯定对我有用,也是我上面描述的唯一代码更改。我不完全明白为什么 MainWindowHandle 不起作用,我也玩了很多 IE 和任务设置,但我想我把所有东西都恢复了,它仍然有效。无论如何感谢您的 +1 :) 我的第一篇文章和第一个 +1 在这里。
    • 很好的解决方案。适用于 Windows Server 2008 R2 和 Windows 7
    • 在进行压力测试时,是否可以通过某种方式对其进行更改,使其也适用于多个窗口?
    【解决方案2】:

    从命令提示符中,您可以安排这样的交互式任务:

    C:\Users\someUser>schtasks /create /sc once /st 16:28 /tn someTask /tr cmd.exe
    

    ... 其中 sc 是计划,st 是开始时间,tn 是您选择的任务名称(可以是任何名称),tr 是您要运行的命令。显然,对于重复任务,您可以将 sc 更改为每月、每周等。只需键入“schtasks /create /?”了解更多信息。

    【讨论】:

    • 您为什么觉得这符合问题中的就像任务计划程序但具有适当的交互式会话规定?整个问题是这个过程需要是交互式的——你有没有使用这种技术来做一些类似的事情。 ...还是我错过了什么?
    • 我的答案中的这一行在计算机的控制台/管理会话中启动了一个交互式命令提示符,但 cmd.exe 可以替换为不同的可执行文件。我可能误解了你的问题。
    • 如果有 2 个 TS 会话为用户 A 和 B 打开,你如何认为你的命令选择在哪个会话中运行/是否可以让它选择一个之外的会话,例如WinXP 只有一个会话。 AIUI 我需要在交互式会话中生成进程 - 即具有真实 UI 的真实桌面。即,cmd.exe 需要由登录会话中的进程作为父项 - 而不仅仅是非交互式 WindowStation。老实说,自从提出这个问题以来,我已经把大部分上下文都删掉了,我知道我在这些 cmets 中严重扼杀了术语!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    相关资源
    最近更新 更多