andrewducker: (wanking)
andrewducker ([personal profile] andrewducker) wrote2008-11-29 09:39 pm

I Win at Geekiness!

I just solved a problem that's been killing me for two days at work.

I've been putting together automated Acceptance Tests at work, using Fitnesse to allow our business analysts to specify tests that are then run against the code on a daily basis before it's released to the system testers.

The problem being that they had some tests that didn't work - they wanted to check that only certain input was accepted in specified textboxes, that maximum lengths were set, etc. However, Textbox.MaxLength only affects keyboard input, it doesn't affect the .Text property if it's set directly.

So I spent Thursday and Friday trying to find a way of simulating keyboard input. Turns out that doing this is really, really easy if you can guarantee that the form you're testing is the top one on the desktop - i.e. the place where keyboard input would normally go. Problem being that I could guarantee that.

Ok - next step, grab a handle to the control I want to talk to and send it messages using PostMessage. Which I played with and got working in test code, and then couldn't get working in my actual tests. I realised at 5:30 last night that it wasn't going to work because I wasn't actually running a Message Loop - all of the forms are being launched via the TestRunner exe - and they don't work the same way they would if they had a windows message pump running in the background (if this makes no sense to you then this might help - or this for a longer explanation).

And then it hit me this afternoon - every control has a WndProc method in it (so that you can override it for custom message processing) - and I could call that directly rather than relying on the Message Loop to pass it to the correct place.

Fortunately I have Visual Studio at home, so I fired it up, played around for half an hour, and with a bit of reflection (because WndProc is "protected" rather than "public") I was happily passing messages to it.

I hate having unsolved problems - and I'm remarkably happy that I got this one done!


    static class TextSender
    {
        private const int WM_CHAR = 0x0102;

        public static void SendKeys(string text, Control control)
        {
            MethodInfo method = typeof(Control).GetMethod("WndProc", BindingFlags.Instance | BindingFlags.NonPublic);
            foreach (Char myChar in text.ToCharArray())
            {
                Message message = new Message();
                message.HWnd = control.Handle;
                message.Msg = WM_CHAR;
                message.WParam = (IntPtr)myChar;
                method.Invoke(control, new object[] { message });
            }
        }
    }

[identity profile] robhu.livejournal.com 2008-11-29 09:51 pm (UTC)(link)
Is there not a way to add code that is executed when .Text = is run? As in, code that checks if the assignment breaks the max length rule and if so throws an exception or something (which is what I'd do in a similar situation with Ruby).

To clarify: I mean on all TextBoxes or on some TextBox instances. I don't mean looking for places where you do someinstance.Text=... and replacing it with if ....length > foo then ...
Edited 2008-11-29 21:52 (UTC)