I'm using a suite of WatiN tests driving IE to do some periodic sanity checking for the purposes of monitoring a site.
The suite works fine when I invoke it interactively and/or when I configure the task in Task Scheduler to "Run only when the user is logged on".
However, when I set it to "Run whether the user is logged on or not", and check the "Run with highest privileges" option (WatiN can't talk to the browser satisfactorily under Windows Server 2008 and many other OSes without having admin privileges), WatiN can't communicate with it's iexplore.exe instances satisfactorily (they start, but I get a timeout exception as detailed in this post). I have added the site I'm hitting to the Trusted sites for both admin and non-admin contexts of IE. I've tried with and without elevation, with and without disabling ESC and with and with and without turning off Protected Mode for the internet zone. As my non-GUI tests are happy, I assume it's a limitation of the type of interactivity that's possible in the context of a non-interactive Scheduled Task, even when "Run with highest privileges".
Right now, my temporary workaround is to require a [TS] session to remain open at all times, ready to run the scheduled task.
If I was to persist with this, I'd at a minimum add a heartbeat notification to allow something to monitor that the task is actually getting to run [e.g., if someone logs the session off or reboots the box].
However, I'm looking for something more permanent -- something that is capable of regularly invoking my WatiN tests [run using xunit-console.x86.exe v 1.5] on my Windows Server 2008 [x64] box, just like Task Scheduler but with a proper Interactive session.
I'd prefer not to use psexec or remcom if possible, and can't see how creating a Windows Service would do anything other than add another point of failure but I'd be interested to hear of all proven solutions out there.
I was able to run Watin tests using Scheduled Task in the "Run whether user is logged on or not" mode.
In my case I tracked the issue down to m_Proc.MainWindowHandle being always 0 when IE is created from a scheduled task running without a logged on user.
In Watin sources this is in the IE.cs:CreateIEPartiallyInitializedInNewProcess function
My workaround is to manually enumerate top level windows and find a window with className == "IEFrame" that belongs to the process instead of using Process.MainWindowHandle property.
Here is the code snippet. All pinvoke i copied directly from Watin source.
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);
}
}
then instead of new IE() use IEBrowserHelper.CreateIEBrowser()
From the command-prompt, you can schedule an interactive task like this:
C:\Users\someUser>schtasks /create /sc once /st 16:28 /tn someTask /tr cmd.exe
... where sc is the schedule, st is the start-time, tn is the taskname you choose (can be anything), and tr is the command you want to run. Obviously, for a recurring task, you would change sc to monthly, weekly, etc. Just type "schtasks /create /?" for more info.