Крюк мыши низкого уровня получает положение курсора и hwnd

У меня есть быстрый вопрос. Я создал стандартное окно с API-интерфейсом окна из MSDN, создав сайт окна. Я хочу, чтобы программа изменила заголовок окна, в котором находится курсор мыши, когда я нажимаю клавишу на клавиатуре. Для этого я установил крючок для мыши низкого уровня, например:

LRESULT CALLBACK LowLevelMouseProc(__in  int nCode, __in  WPARAM wParam, __in  LPARAM lParam) {
    MSLLHOOKSTRUCT* p = (MSLLHOOKSTRUCT*)lParam;
    HWND hiWnd = WindowFromPoint(p->pt); //Get a handle to the top-most window
    ScreenToClient(hiWnd, &p->pt); //Converts the cursor position from screen to the specified window
    char buf[33];

    switch (wParam) {

    case WM_MOUSEMOVE:
        snprintf(buf, sizeof(buf) - 1, "X:%ld, Y:%ld", p->pt.x, p->pt.y); //Put the cursor coordinates into a char buffer
        SendMessage(hiWnd, WM_SETTEXT, 0, (LPARAM)buf); //Send a message to the other window to change the title
        break;
    }
    return CallNextHookEx(0, nCode, wParam, lParam);
}

При вызове хука мыши только моя программа меняет текст. Кроме того, текст представляет собой набор китайских иероглифов, причем не один или два, а такой набор:

藡覶 跾 瑍痸碚 齫儽戃 羭聧蔩, 圪妀 跾 鶀嚵巆 堔埧娾 爂犤繵 摿斠榱 軥軱逴 潫 徖梜, 薍薝 

Должен ли я возиться с окном переднего плана и вместо этого получить фоновое окно? Или окно, в котором находится текущая позиция мыши? я бы предположил, что

ScreenToClient(hiWnd, &p->pt)

Спасибо за помощь, ребята!


person Omar Martinez    schedule 21.08.2015    source источник
comment
Китайский текст настоятельно предполагает, что вы случайно передаете строку ANSI в функцию Unicode. Мы уже видим, что вы передаете строку ANSI в buf, так что половина теории подтверждается. Другая половина определяет, используете ли вы Unicode-версию SendMessage.   -  person Raymond Chen    schedule 21.08.2015


Ответы (1)


Я хочу, чтобы программа изменила заголовок окна, в котором находится курсор мыши, когда я нажимаю клавишу на клавиатуре.

Тогда почему вы используете хук мыши вместо хука клавиатуры? В хуке клавиатуры вы можете использовать GetCursorPos(), чтобы получить текущую позицию мыши на экране.

Что касается вашей реальной проблемы, вы не проверяете, чтобы WindowFromPoint() вообще находил окно, вы не передаете правильно выровненный указатель памяти на WM_SETTEXT и не принимаете во внимание, является ли окно назначения Ansi или Unicode .

Вместо этого попробуйте что-то вроде этого:

Крючок для клавиатуры:

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if ((nCode == HC_ACTION) && (wParam == WM_KEYUP))
    {
        KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;
        if (p->vkCode == ...) // whatever key you are looking for
        {
            POINT pt;
            GetCursorPos(&pt);

            HWND hiWnd = WindowFromPoint(pt);
            if (hiWnd)
            {
                ScreenToClient(hiWnd, &pt);

                if (IsWindowUnicode(hiWnd))
                {
                    LPWSTR buf = (LPWSTR) GlobalAlloc(GMEM_FIXED, 33 * sizeof(WCHAR));
                    if (buf)
                    {
                        snwprintf(buf, 33, L"X:%ld, Y:%ld", pt.x, pt.y);
                        SendMessageW(hiWnd, WM_SETTEXT, 0, (LPARAM)buf);
                        GlobalFree(buf);
                    }
                }
                else
                {
                    LPSTR buf = (LPSTR) GlobalAlloc(GMEM_FIXED, 33);
                    if (buf)
                    {
                        snprintf(buf, 33, "X:%ld, Y:%ld", pt.x, pt.y);
                        SendMessageA(hiWnd, WM_SETTEXT, 0, (LPARAM)buf);
                        GlobalFree(buf);
                    }
                }
            }
        }
    }

    return CallNextHookEx(0, nCode, wParam, lParam);
}

Крючок для мыши:

LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if ((nCode == HC_ACTION) && (wParam == WM_MOUSEMOVE))
    {
        MSLLHOOKSTRUCT* p = (MSLLHOOKSTRUCT*)lParam;

        HWND hiWnd = WindowFromPoint(p->pt);
        if (hiWnd)
        {
            POINT pt = p->pt;
            ScreenToClient(hiWnd, &pt);

            if (IsWindowUnicode(hiWnd))
            {
                LPWSTR buf = (LPWSTR) GlobalAlloc(GMEM_FIXED, 33 * sizeof(WCHAR));
                if (buf)
                {
                    snwprintf(buf, 33, L"X:%ld, Y:%ld", pt.x, pt.y);
                    SendMessageW(hiWnd, WM_SETTEXT, 0, (LPARAM)buf);
                    GlobalFree(buf);
                }
            }
            else
            {
                LPSTR buf = (LPSTR) GlobalAlloc(GMEM_FIXED, 33);
                if (buf)
                {
                    snprintf(buf, 33, "X:%ld, Y:%ld", pt.x, pt.y);
                    SendMessageA(hiWnd, WM_SETTEXT, 0, (LPARAM)buf);
                    GlobalFree(buf);
                }
            }
        }
    }

    return CallNextHookEx(0, nCode, wParam, lParam);
}
person Remy Lebeau    schedule 21.08.2015
comment
Разве мне не нужна низкоуровневая мышь для положения курсора за пределами главного окна и щелчки мышью? - person Omar Martinez; 21.08.2015
comment
Как я сказал и показал, вы можете использовать GetCursorPos() для получения позиции мыши. Но если вы хотите реагировать на клики мыши (о которых вы не упоминали ранее), то вы можете использовать хук для мыши. Хотя вам следует рассмотреть возможность использования API Raw Input. крючков низкого уровня. Документация LowLevelKeyboardProc и LowLevelMouseProc даже говорит об этом: В большинстве случаев, когда приложению необходимо использовать ловушки низкого уровня, вместо этого оно должно отслеживать необработанный ввод. - person Remy Lebeau; 21.08.2015
comment
Я не думаю, что вам нужно вызывать GlobalAlloc для выделения буферов. Поскольку WM_SETTEXT является стандартным сообщением Windows, система автоматически упорядочивает аргументы указателя через границы процесса. - person IInspectable; 21.08.2015
comment
@IInspectable: Да, WM_SETTEXT выполняет автоматическую маршализацию между процессами, но также известно, что он дает сбой, если указатель не выровнен правильно. Об этом есть комментарий в WM_SETTEXT документации: Серьезная ошибка 64-битной Windows - Если указатель String, переданный в WM_SETTEXT или SetWindowText(), не находится по четному адресу в памяти, функция/сообщение не установит WindowText и вернет TRUE ... Я наблюдал это только в 64-битной версии Windows 7, независимо от того, было ли приложение скомпилировано как 32-битное или 64-битное. - person Remy Lebeau; 21.08.2015
comment
@IInspectable: позволить ОС выделить буфер памяти — это один из способов обеспечить правильное выравнивание. Вы, вероятно, можете использовать LocalAlloc() или, может быть, даже new, но я бы не стал полагаться на использование буфера стека, если эта проблема реальна. - person Remy Lebeau; 21.08.2015
comment
@RemyLebeau: я не знал об этом конкретном ограничении. Однако я не вижу ничего плохого в том, чтобы полагаться на то, что компилятор сделает все правильно. Неважно, выделяется ли массив динамически, или используется автоматическая продолжительность хранения. Компилятор правильно выровняет память. Если вы столкнетесь с указателем, который не выровнен должным образом, у вас все равно будет неопределенное поведение. Парень, жалующийся в Дополнениях сообщества, по-видимому, не знает о своей ошибке, вызванной созданием невыровненного указателя. - person IInspectable; 21.08.2015
comment
@IInspectable: он специально создал указатель с неправильным выравниванием для демонстрационных целей, но также заявил, что если вы используете структуры, выровненные по BYTE и содержащие строки, вы можете легко столкнуться с этой проблемой. В данной ситуации это не так, но, тем не менее, это реальная возможность. - person Remy Lebeau; 21.08.2015
comment
@RemyLebeau: это не меняет основной проблемы: вы начинаете с невыровненного указателя. Не имеет значения, намеренно ли указатель неправильно выровнен или (более тонко) неправильно выровнен из-за ошибочной упаковки структуры. В любом случае это неопределенное поведение. - person IInspectable; 21.08.2015