3

I'm trying to send messages to a window that says Ctrl and Up-arrow has been pressed. I've got the basics down, I can send presses of the space key that registeres fine. But I can't seem to get the ctrl+ working. chosen code snippets:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

Now this works fine for sending Space:

public static void SendKeyPress(IntPtr handle, VKeys key)
{
    SendMessage(handle, (int) WMessages.WM_KEYDOWN, (int) key, 0);
    SendMessage(handle, (int)WMessages.WM_KEYUP, (int)key, 0);
}

But this doesn't work for sending Ctrl+ to VLC to increase the sound volume:

public static void SendKeyPress(IntPtr handle, VKeys key, bool control)
{
    int lParamKeyDown = 0;
    lParamKeyDown |= 1;
    lParamKeyDown |= 1 << 24;

    int lParamKeyUp = lParamKeyDown;
    lParamKeyUp |= 1 << 30;
    lParamKeyUp |= 1 << 31; //it was down before

    int lParamCtrlDown = lParamKeyDown;
    int lParamCtrlUp = lParamKeyUp;

    lParamKeyDown |= (int)MapVirtualKey((uint)key, 0) << 16;
    lParamKeyUp |= (int)MapVirtualKey((uint)key, 0) << 16;
    lParamCtrlDown |= (int)MapVirtualKey((uint)VKeys.VK_CONTROL, 0) << 16;
    lParamCtrlUp |= (int)MapVirtualKey((uint)VKeys.VK_CONTROL, 0) << 16;

    IntPtr controlPtr = new IntPtr((int)VKeys.VK_CONTROL);
    IntPtr lParamCtrlDownPtr = new IntPtr(lParamCtrlDown);
    IntPtr lParamCtrlUpPtr = new IntPtr(lParamCtrlUp);
    IntPtr lParamKeyDownPtr = new IntPtr(lParamKeyDown);
    IntPtr lParamKeyUpPtr = new IntPtr(lParamKeyUp);
    IntPtr keyPtr = new IntPtr((int)key);
    object o = new object();
    HandleRef wndRef = new HandleRef(o, handle);
    PostMessage(wndRef, (uint)WMessages.WM_KEYDOWN, controlPtr, lParamCtrlDownPtr);
    PostMessage(wndRef, (uint) WMessages.WM_KEYDOWN, keyPtr, lParamKeyDownPtr);

    PostMessage(wndRef, (uint) WMessages.WM_KEYUP, controlPtr, lParamCtrlUpPtr);
    PostMessage(wndRef, (uint) WMessages.WM_KEYUP, keyPtr, lParamKeyUpPtr);
 }

What am I missing?

Edit3: The Messages are exactly the same and there are no extra messages since I switched to PostMessage but VLC still won't increase or decrease the volume.

It's not just VLC either, Spotify won't accept the same command even though the messagess look exactly alike in Spy++.

Mwiza
  • 7,780
  • 3
  • 46
  • 42
dutt
  • 7,909
  • 11
  • 52
  • 85
  • AutoIt is pretty reliable at sending key strokes and has a library/API that would allow you to send keyboard commands from C#. I don't have any code off hand though. – AaronLS Apr 30 '10 at 17:06
  • You're not pressing CTRL UP, you're pressing UP CTRL! – NibblyPig Apr 30 '10 at 17:08
  • AaronLS: Would prefer to not use third part libraries for something this small. SLC: There, forgot to update the code. The version I'm testing is the one currently shown, first Ctrl and then Up. Sorry for the mistake. – dutt Apr 30 '10 at 17:52

3 Answers3

4

I don't have a great way to test it, but would it work if the order of these two lines:

SendMessage(handle, (int) WMessages.WM_KEYDOWN, (int) key, 0);
SendMessage(handle, (int) WMessages.WM_KEYDOWN, (int)VKeys.VK_CONTROL, 0);

was changed to:

SendMessage(handle, (int) WMessages.WM_KEYDOWN, (int)VKeys.VK_CONTROL, 0);
SendMessage(handle, (int) WMessages.WM_KEYDOWN, (int) key, 0);

so that the control key being down essentially wraps the press of the other key?

ChronoPositron
  • 1,498
  • 2
  • 10
  • 14
2

I found a working solution. You use the SetKeyboardState function to depress the control key, and then you can send any key you want. This Delphi code sends Ctrl+Right to a Memo component.

var
  s: TKeyboardState;
  PrevState: byte;
begin
  GetKeyboardState(s);
  PrevState := s[VK_CONTROL];
  s[VK_CONTROL] := 128;
  SetKeyboardState(s);
  SendMessage(Memo1.Handle, WM_KEYDOWN, VK_RIGHT, 0);
  s[VK_CONTROL] := PrevState;
  SetKeyboardState(s)
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Doesn't seem to work. I'm trying to send a Ctrl+Up to VLC to increase the volume but it doesn't register. – dutt Apr 30 '10 at 17:02
  • OK. SetKeyboardState does only change the state for the current thread. So this code will definitely not work in the case of inter-process communication. – Andreas Rejbrand Apr 30 '10 at 17:04
  • Ah, well that explains that then. Added the inter-process clarification to the question for clarification. – dutt Apr 30 '10 at 17:49
2

I now found a method to send Ctrl+Right (for example) to a window of another process. If hEdit is the handle of the window, then the following code works.

AttachThreadInput(GetCurrentThreadID, GetWindowThreadProcessId(hEdit, 0), true);
hEdit := SetFocus(hEdit);

keybd_event(VK_CONTROL, 0, 0, 0);
keybd_event(VK_RIGHT, 0, 0, 0);
keybd_event(VK_RIGHT, 0, KEYEVENTF_KEYUP, 0);
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);

SetFocus(hEdit); // restore focus
AttachThreadInput(GetCurrentThreadID, GetWindowThreadProcessId(hEdit, 0), false); // "Disconnect"

Update

The drawback of this method is that the focus is changed for a small duration of time. In fact, the best solution, I believe, is a combination of my previous and this post:

var
  s: TKeyboardState;
  PrevState: byte;

AttachThreadInput(GetCurrentThreadID, GetWindowThreadProcessId(hEdit, 0), true);

GetKeyboardState(s);
PrevState := s[VK_CONTROL];
s[VK_CONTROL] := 128;
SetKeyboardState(s);
SendMessage(hEdit, WM_KEYDOWN, VK_RIGHT, 0);
s[VK_CONTROL] := PrevState;
SetKeyboardState(s);

AttachThreadInput(GetCurrentThreadID, GetWindowThreadProcessId(hEdit, 0), false);
Werner Henze
  • 16,404
  • 12
  • 44
  • 69
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Thanks, unfortunatly neither of these methods appear to work. I tried your AttachThreadInput wrapping around a SendInput-application and that didn't work either. I know I'm sending to the right window, sending Space pauses/resumes, and the messages are exactly alike according to Spy++. What else could differ? – dutt May 01 '10 at 07:51