How to build the lParam of WM_KEYDOWN|UP?

Youk 20 Reputation points
2023-04-22T16:31:51.38+00:00

I'm trying to replicate QKeyEvents sent on a QWidget to another non-Qt Window, for this, I'm using the WINAPI PostMessage together WM_KEYDOWN / WM_KEYUP messages

Based on this stack answer and Microsoft documentation of Keystroke Message Flags

I wrote this small compilable example:

//mainwindow.h
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindowClass; };
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
   Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    bool eventFilter(QObject* obj, QEvent* event);
protected:
    virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
private:
    Ui::MainWindowClass *ui;
};

// mainwindow.cpp
#include "MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindowClass())
{
    ui->setupUi(this);
    QTextEdit* textEdit = new QTextEdit(this);
    textEdit->installEventFilter(this);
    textEdit->setGeometry(50, 50, 200, 50);
}

bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
    switch (event->type())
    {
    case QEvent::KeyPress:
    case QEvent::KeyRelease:
    {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        WPARAM wParam = keyEvent->nativeVirtualKey();

        bool keyRelease = keyEvent->type() == QEvent::KeyRelease;

        LPARAM lParam = 0;
        lParam |= (keyRelease) ? (1 << 31): (0); // Transition state
        lParam |= (keyEvent->isAutoRepeat() || keyRelease) ? (1 << 30) : 0;  // Previous key state
        lParam |= (keyEvent->modifiers() & Qt::AltModifier) ? (1 << 29) : 0;
        bool isExtendedKey = (keyEvent->nativeScanCode() & 0x0100) != 0; // Extended key
        lParam |= (isExtendedKey ? (1 << 24) : 0);
        lParam |= (keyEvent->nativeScanCode() << 16); // Scan code
        lParam |= 1; // Repeat count

        // Just for debugging:
        WORD keyFlags = HIWORD(lParam);
        WORD scanCode = LOBYTE(keyFlags); // scan code

        qDebug() << (keyRelease ? "[[KEYUP]]" : "[[KEYDOWN]]")
            << "\nwParam" << wParam << "lParam" << lParam
            << "\nscanCode" << scanCode << "\n";

        //PostMessage(hwnd, keyEvent->type() == QEvent::KeyPress ? WM_KEYDOWN : WM_KEYUP, wParam, lParam);
    }
    }
    return false;
}

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
    MSG* msg = static_cast<MSG*>(message);

    switch (msg->message)
    {
    case WM_KEYDOWN:
    case WM_KEYUP:
    {    
        WORD keyFlags = HIWORD(msg->lParam);
        WORD scanCode = LOBYTE(keyFlags); // scan code

		qDebug() << (msg->message == WM_KEYDOWN ? "KEYDOWN" : "KEYUP")
			<< "\nwParam" << msg->wParam << "lParam" << msg->lParam
            << "\nscanCode:" << scanCode;            
        break;
    }
    default:
    break;
    }
    return QWidget::nativeEvent(eventType, message, result);
}

The lParam from the nativeEvent (window procedure) is different from the one I'm building inside the eventFilter, trying to figure out what I'm calculating wrong. When I input a in the textEdit this is what I get:

KEYDOWN 
wParam 65 lParam 1966081 
scanCode: 30
[[KEYDOWN]] 
wParam 65 lParam 1966081 
scanCode 30 

KEYUP 
wParam 65 lParam 3223191553 
scanCode: 30
[[KEYUP]] 
wParam 65 lParam -1071775743 
scanCode 30
Developer technologies C++
0 comments No comments
{count} votes

Accepted answer
  1. RLWA32 49,536 Reputation points
    2023-04-23T09:59:43.1233333+00:00

    To avoid arithmetic overflow in the bitwise shift operations cast the value to be shifted to an LPARAM. For example, instead of

    lParam |= (keyRelease) ? (1 << 31) : (0); // Transition state
    

    use

    lParam |= (keyRelease) ? ((LPARAM)1 << 31) : (0); // Transition state
    

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.