tags:

views:

223

answers:

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+up 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+Up 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++.

+3  A: 

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
I agree, send Ctrl down, the key down, the key up, Ctrl up, just as if someone were pressing the keys.
wallyk
I do not think that this works, although this order of commands is far more obvious than the original one.
Andreas Rejbrand
As Andreas said, this does not work. But it is neater so I left the code like that.
dutt
A: 

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
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
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
Ah, well that explains that then. Added the inter-process clarification to the question for clarification.
dutt
A: 

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);
Andreas Rejbrand
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