向窗口发送Ctrl+Up命令

3

我正在尝试向一个窗口发送消息,该窗口显示Ctrl和Up-arrow已被按下。我已经掌握了基本操作,可以发送空格键的按压,这很好地注册了。但是我似乎无法让ctrl+正常工作。

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

现在,这对于发送空格是有效的:
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);
}

但是这种方法无法发送 Ctrl+ 给VLC来增加音量:
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);
 }

我错过了什么?

编辑3:自从我切换到PostMessage以来,消息完全相同,没有额外的消息,但VLC仍然无法增加或减少音量。

不仅仅是VLC,即使在Spy ++中看起来完全相同,Spotify也不接受相同的命令。


AutoIt 在发送按键方面非常可靠,并且具有一个库/API,可以让您从 C# 发送键盘命令。不过我手头没有任何代码。 - AaronLS
你不是按下CTRL UP,而是按下UP CTRL! - NibblyPig
AaronLS:对于这么小的东西,我宁愿不使用第三方库。SLC:好了,忘记更新代码了。我正在测试的版本是当前显示的版本,先按Ctrl然后按上箭头。对于我的错误表示抱歉。 - dutt
3个回答

4

我没有一个很好的测试方法,但如果这两行的顺序改变,它会工作吗:

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

被更改为:

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

那么按下控制键实际上就相当于同时按下了另一个键吗?


1
我同意,发送Ctrl按键按下、按键按下、按键抬起、Ctrl抬起,就像有人在按键一样。 - wallyk
我认为这不起作用,尽管这些命令的顺序比原来的更明显。 - Andreas Rejbrand
正如Andreas所说,这并不起作用。但是它更整洁,所以我保留了这段代码。 - dutt

2
我找到了一个可行的解决方案。您可以使用SetKeyboardState函数来按下控制键,然后发送任何想要的键。这段Delphi代码向Memo组件发送Ctrl+Right。
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;

似乎不起作用。我正在尝试发送Ctrl+Up到VLC来增加音量,但它没有注册。 - dutt
OK。SetKeyboardState 只会更改当前线程的状态。因此,在进程间通信的情况下,该代码肯定不起作用。 - Andreas Rejbrand
啊,那样就解释了。为了澄清问题,我添加了进程间的澄清。 - dutt

2

我现在找到了一种方法,可以将Ctrl+Right(例如)发送到另一个进程的窗口。如果hEdit是该窗口的句柄,则以下代码有效。

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"

更新

这种方法的缺点是焦点在短时间内会发生变化。事实上,我认为最好的解决方案是将我的先前文章和这篇文章结合起来:

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

谢谢,不幸的是这两种方法似乎都不起作用。我尝试了您的AttachThreadInput包装SendInput应用程序,但也没有起作用。我知道我正在发送到正确的窗口,发送空格可以暂停/恢复,并且根据Spy ++,消息完全相同。还有什么可能不同? - dutt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接