I am writing an application that responds to keyboard actions. To automate testing, I wrote a keyboard simulator that uses User32.dll / SendInput to automate key presses and releases. This approach works fine on the local developer machine, but fails when checked-in and run on a TFS build agent. My guess is that the user mode activities are not getting processed as expected because it is being run as a service. Is there a way to automate keyboard presses and releases that is compatible with automated testing as a service?
Setup:
Hosted TFS (tfspreview.com)
Visual Studio 2010 Ultimate
MSTest, xUnit.NET
Windows 7 Pro (dev)
Windows 2008 R2 (build controller / build agent)
Offending code:
Test case:
KeyboardSimulator.PressKey(Keys.A);
Keyboard Simulator:
public static void PushKeyDown(Keys key)
{
SendKeyboardInput(key, (uint)FLAGS.NONE);
}
public static void ReleaseKey(Keys key)
{
SendKeyboardInput(key, (uint)FLAGS.KEYUP);
}
public static void PressKey(Keys key)
{
PushKeyDown(key);
ReleaseKey(key);
}
#region Internals
private static KEYBDINPUT createKeybdInput(Keys key, uint flag)
{
KEYBDINPUT i = new KEYBDINPUT();
i.wVk = (ushort)key;
i.wScan = 0;
i.time = 0;
i.dwExtraInfo = IntPtr.Zero;
i.dwFlags = flag;
return i;
}
private static void SendKeyboardInput(Keys key, uint flag)
{
INPUT[] inputs = new INPUT[1];
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki = createKeybdInput(key, flag);
uint intReturn = SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
if (intReturn != 1) throw new ApplicationException("Could not send key");
}
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] input, int structSize);
The Team Foundation Build server runs as a Windows Service by default. As you guessed, this means that it can’t interact with the desktop (it doesn’t have one).
You’ll need to configure your build service to run in interactive mode. Follow these steps:
Run build service as: Interactive ProcessOf course, you’ll need to remain logged onto that machine for the duration. If it’s a separate build server, you can configure automatic logon and add
TfsBuildServiceHost.exeto the automatic startup list. With this configuration, you shouldn’t need to logon to it again.