I'm buidling some simple macro functionality for my app so the users can record a sequence of keyboard inputs and replay them reliably via some menu.
Originally, I used:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
To map "Keys" objects to their string constant, and stored them on a string Queue as they were happening. Then (after resetting the form), I simulate replaying of these actions by flushing the queued strings and used: SendKeys.Send(string keys); to replay the keystrokes.
Example: assuming 2 textboxes are in a form, if you execute
SendKeys.Send("abc");
SendKeys.Send("{TAB}");
SendKeys.Send("123");
You end up with "abc" in the first textbox, then "123" in the second one. Nice!
This works but has two flaws:
-I need to keep maping the keydata parameter of ProcessCmdKey to a string, whenever I get a Keys object I haven't mapped.
-At some point I will have to do mouse events, and this approach won't cut it. (however I don't care about that much right now).
My second approach was to queue windows messages, and thats where my problem/question arises. If I queue (into some data structure) windows keyboard messages as they arrive to the form
private Queue<Message> mq = new Queue<Message>();
private bool flushing = false;
//QUEUE MESSAGES AS SEEN BY FORM. Note the flushing member prevents infinite queuing.
protected override bool ProcessKeyPreview(ref Message m)
{
if (!flushing)
{
Debug.WriteLine("enq");
mq.Enqueue(m);
}
return base.ProcessKeyPreview(ref m);
}
and then use the win32 call to flush my queue
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
//FLUSH THE QUEUE BY SENDING THE MESSAGES BACK TO THE WINDOW PROC OF THE FORM.
private void FlushQ()
{
Debug.WriteLine("flushing q " + mq.Count.ToString());
flushing = true;
while (mq.Count > 0)
{
Message m = mq.Dequeue();
SendMessage(new HandleRef(this, this.Handle), (uint)m.Msg, m.WParam, m.LParam);
}
flushing = false;
}
The flushQ function "seems" to work because I do see the messages flowing again into ProcessKeyPreview. But that's it. The textboxes don't get the text. I suspect the form never passes the message to the child controls, so if instead I do
SendMessage(new HandleRef(this.textBox1, this.textBox1.Handle), (uint)m.Msg, m.WParam, m.LParam);
then only the textbox1 will get the messages. That's no good either because 1) the receiver of the message should really be the control with the focus after an arbitrary number of dequeued steps. 2) My queueing doesn't seem to work for special keys like TAB, where the control should change focus.
I suspect I'm just not queuing things at the right place, and I don't know how to replay them properly so the messages go to the control that has the input focus (I could keep track of who has the input focus by handling the "enter" event, but I rather use something more reliable). I tried queuing things at WndProc but...I can't make sense of the sheer number of messages going there. Some should be ignored, some not and I honestly don't know which ones. Either way I can't replay them.
Am I totally wrong in my approach to save and replay keyboard messages? How does SendKeys.Send() get it right? I'll admit I'm new to windows message processing so any help/insight solving this problem would be appreciated. Disregard how to actually serialize a message queue to disk. And if you know of a way to get the mouse queuing properly while you're at it, great but I don't care about mouse input too much.
Thanks.