如何向窗口发送按键信息?

10

我正在使用keybd_event()函数,想要使用SendMessage()函数向记事本发送按键事件,这可行吗?

2个回答

9

keybd_event()已被SendInput()取代,因此最好使用它。以下是一些示例代码,可以实现您所要求的功能。您可以使用SendMessage()直接编辑记事本窗口的编辑控件,也可以使用SendInput()合成要发送到窗口的按键。

使用SendInput()

int SendKeystrokesToNotepad( const TCHAR *const text )
{
    INPUT *keystroke;
    UINT i, character_count, keystrokes_to_send, keystrokes_sent;
    HWND notepad;

    assert( text != NULL );

    //Get the handle of the Notepad window.
    notepad = FindWindow( _T( "Notepad" ), NULL );
    if( notepad == NULL )
        return 0;

    //Bring the Notepad window to the front.
    if( !SetForegroundWindow( notepad ) )
        return 0;

    //Fill in the array of keystrokes to send.
    character_count = _tcslen( text );
    keystrokes_to_send = character_count * 2;
    keystroke = new INPUT[ keystrokes_to_send ];
    for( i = 0; i < character_count; ++i )
    {
        keystroke[ i * 2 ].type = INPUT_KEYBOARD;
        keystroke[ i * 2 ].ki.wVk = 0;
        keystroke[ i * 2 ].ki.wScan = text[ i ];
        keystroke[ i * 2 ].ki.dwFlags = KEYEVENTF_UNICODE;
        keystroke[ i * 2 ].ki.time = 0;
        keystroke[ i * 2 ].ki.dwExtraInfo = GetMessageExtraInfo();

        keystroke[ i * 2 + 1 ].type = INPUT_KEYBOARD;
        keystroke[ i * 2 + 1 ].ki.wVk = 0;
        keystroke[ i * 2 + 1 ].ki.wScan = text[ i ];
        keystroke[ i * 2 + 1 ].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
        keystroke[ i * 2 + 1 ].ki.time = 0;
        keystroke[ i * 2 + 1 ].ki.dwExtraInfo = GetMessageExtraInfo();
    }

    //Send the keystrokes.
    keystrokes_sent = SendInput( ( UINT )keystrokes_to_send, keystroke, sizeof( *keystroke ) );
    delete [] keystroke;

    return keystrokes_sent == keystrokes_to_send;
}

使用 SendMessage()
int SendKeystrokesToNotepad( const TCHAR *const text )
{
    HWND notepad, edit;

    assert( text != NULL );

    //Get the handle of the Notepad window.
    notepad = FindWindow( _T( "Notepad" ), NULL );
    if( notepad == NULL )
        return 0;

    //Get the handle of the Notepad window's edit control.
    edit = FindWindowEx( notepad, NULL, _T( "Edit" ), NULL );
    if( edit == NULL )
        return 0;

    SendMessage( edit, EM_REPLACESEL, ( WPARAM )TRUE, ( LPARAM )text );
    return 1;
}

我希望这有所帮助。


在SendInput()的例子中,KEYEVENTF_UNICODE是否关心文本的类型,它可能也是一个char?我没有看到KEYEVENTF_SCANCODE,但你把字符放在了wScan里。GetMessageExtraInfo()是必要的吗? - ManuelSchneid3r
1
@DevNoob:不,这应该适用于Unicode和非Unicode版本。 (我刚测试了一下。)是的,GetMessageExtraInfo()似乎是必需的,因为文档指定它是必需的。 顺便说一句,阅读MSDN文档以获取相应函数和类型的链接也可以找到您问题的答案。 - Sam
@DevNoob,我不完全确定你所说的关于 KEYEVENTF_KEYUP 被省略的意思。我也不确定你所引用的文档部分的含义。如果你想知道为什么需要按下和松开键盘事件,因为这些事件被用来生成相应的 WM_KEYDOWN 和 WM_KEYUP 窗口消息给目标窗口。我们在低级别上工作,并且我认为 Windows API 没有提供一个 WM_KEYPRESS 消息来表示单个按键操作。 - Sam
@DevNoob,使用WM_KEYDOWNWM_KEYUP实际上是一个非常好的解决方案,因为它非常兼容。这是因为这些窗口消息对于几乎所有Windows应用程序都是标准的。将它们分开可以给我们更多的控制权。我能想到的唯一不支持此方法的Windows应用程序类型是使用DirectInput的应用程序。这样做的一个缺点是SendInput只会将消息发送到当前焦点窗口,这可能是菜单而不是文本字段之类的东西。 - Sam
抱歉,复制粘贴错误。我已经做了,但微软应该清楚区分发送的字符和要用作输入的字符。这是我第一眼误解的地方。我仍然不明白为什么KEYEVENTF_SCANCODE被省略了。根据文档,“...如果指定,wScan标识键,wVk将被忽略。”来自这里 - ManuelSchneid3r

8

使用SendMessage将文本插入编辑缓冲区(这似乎是您想要的):

HWND notepad = FindWindow(_T("Notepad"), NULL);
HWND edit = FindWindowEx(notepad, NULL, _T("Edit"), NULL);
SendMessage(edit, WM_SETTEXT, NULL, (LPARAM)_T("hello"));

如果您需要键码和任意按键,可以使用SendInput()(适用于2k / xp并且更受欢迎),或keybd_event()(在新的操作系统中最终将调用SendInput)。这里有一些示例:http://www.codeguru.com/forum/showthread.php?t=377393
此外,对于SendMessage,还有WM_SYSCOMMAND / WM_KEYDOWN / WM_KEYUP / WM_CHAR事件,您可能会感兴趣。

它是如何将其发送到窗口的? - H4cKL0rD
这不行。您需要在hWnd参数中传递一个窗口句柄。此外,窗口句柄是第一个参数,而不是第三个。 - Anon.
你不能使用 SendMessage() 来发送按键。你无法控制键盘状态,特别是 Shift、Control 和 Alt 键。 - Hans Passant
2
还有,WM_SETTEXT不能发送键击。无论如何,正如nobugz所提到的那样,你不能可靠地使用SendMessage()来做这件事。请参阅http://blogs.msdn.com/oldnewthing/archive/2005/05/30/423202.aspx。 - shf301
1
您无需发送按键击打来输入文字。问答中已经提到了 keybd_event。问题是关于如何使用 SendMessage - jspcal
这个问题很令人困惑,因为它说要使用“SendMessage();发送按键”。是的,如果要发送按键,则不能使用SendMessage,但如果要使用SendMessage,则该问题不涉及发送按键。 - Sam Hobbs

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