Как устранить MessageBeep из элемента управления RICHEDIT? - PullRequest
5 голосов
/ 27 апреля 2019

Элемент управления RichEdit обладает этой очень раздражающей функцией. Он издает звуковой сигнал каждый раз, когда пользователь пытается переместить курсор за пределы « конечной точки ». Например, вы можете проверить это с WordPad, который также реализует RICHEDIT. Откройте его, введите текст, а затем нажмите клавишу Home. Если курсор не был в начале строки:

enter image description here

нажатие клавиши Home переместит его туда, но затем повторное нажатие клавиши Home произведет этот звуковой сигнал.

На первый взгляд это казалось переопределением WM_KEYDOWN и WM_KEYUP сообщений и блокированием ситуаций, когда RICHEDIT может издать этот звуковой сигнал, было решением ... пока я фактически не начал его реализовывать. К сожалению, это не так просто, как кажется, так как этот сигнал звучит во многих случаях! Так что мой код блокировки клавиш буквально увеличился до 300 с лишним строк, и я все еще вижу, что есть некоторые нажатия клавиш, которые я либо не учел, либо, что еще хуже, мог бы переопределить некоторые полезные действия. (Подробнее читайте ниже.)


Тогда я решил заглянуть внутрь реализации самого элемента управления RICHEDIT. И конечно же, например, если мы посмотрим на реализацию нажатия клавиши Home, C:\WINDOWS\SysWOW64\msftedit.dll в моей ОС Windows 10, имеет функцию с именем ?Home@CTxtSelection@@QAEHHH@Z (или public: int __thiscall CTxtSelection::Home(int,int) demangled) с отображенным смещением 0x3FC00, это жестко запрограммировано для вызова MessageBeep (MB_OK) или точно , что я пытаюсь устранить:

enter image description here

И если вы посмотрите на адрес 0x6B64FD38 на скриншоте выше, есть встроенный способ его обойти, который выглядит как флаг 0x800.

Итак, углубившись в msftedit.dll, можно найти функцию с именем ?OnAllowBeep@CTxtEdit@@QAEJH@Z (или public: long __thiscall CTxtEdit::OnAllowBeep(int) demangled), которая может изменить следующие флаги:

enter image description here

После нескольких дополнительных исследований я обнаружил, что в элемент управления RICHEDIT встроены COM-интерфейсы, такие как ITextServices и ITextHost, которые ссылаются на этот флаг как TXTBIT_ALLOWBEEP в ITextServices::OnTxPropertyBitsChange методе.

К сожалению, я, похоже, не могу найти способ, как я могу напрямую изменить этот флаг TXTBIT_ALLOWBEEP (COM не моя сильная сторона.) Я попытался изучить реализацию ITextHost, но это У меня много виртуальных методов, которые не имеют ничего общего с тем, чего я пытаюсь достичь, и я не знаю, как его реализовать.

Кто-нибудь знает, как очистить этот флаг TXTBIT_ALLOWBEEP?


PS. Вот почему я не пошел по пути отмены нажатий клавиш: Просто чтобы дать вам пример. Скажем, если я переопределю клавишу VK_HOME, нажмите. Мне нужно убедиться, что курсор находится не в начале строки, но также и нет выделения. Тем не менее, мне нужно убедиться, что клавиша Ctrl не нажата в ситуации, когда курсор находится в самой верхней части окна. Затем то же самое с ключом Shift, и я даже не уверен, что Alt делает с ним ... и так далее. О, и это просто клавиша Home. Также есть Вверх, Вниз, Влево, Вправо, PageUp, PageDown, End, Delete, Backspace. (И это то, что я знал. Может быть, больше, плюс я даже не говорю о IME или других раскладках клавиатуры и т. Д.) Другими словами, это становится беспорядком! Итак, в конце концов я понял, что предвидение нажатия клавиши - это не путь.

1 Ответ

7 голосов
/ 28 апреля 2019

сначала нам нужно отправить EM_GETOLEINTERFACE сообщение в окно расширенного редактирования - это Извлекает объект IRichEditOle, который клиент может использовать для доступа к функциональности модели компонентов (COM) элемента управления расширенного редактирования.

затем для получения указателя ITextServices вызовите QueryInterface для частного указателя IUnknown, возвращаемого EM_GETOLEINTERFACE.

здесь существует интересный момент - IID_ITextServices не очень известен, но нужно получить во время выполнения от Msftedit.dll

из Об элементах управления Windowless Rich Edit

Msftedit.dll экспортирует идентификатор интерфейса (IID) с именем IID_ITextServices , который можно использовать для запроса указателя IUnknown для интерфейса ITextServices .

после того, как мы получили ITextServices указатель - мы просто можем позвонить OnTxPropertyBitsChange с TXTBIT_ALLOWBEEP mask

пример кода:

#include <textserv.h>

if (HMODULE hmodRichEdit = LoadLibrary(L"Msftedit.dll"))
{
    // create richedit window
    if (HWND hwndRich = CreateWindowExW(0, MSFTEDIT_CLASS, ...))
    {
        if (IID* pIID_ITS = (IID*) GetProcAddress(hmodRichEdit, "IID_ITextServices"))
        {
            IUnknown* pUnk;
            if (SendMessageW(hwndRich, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk))
            {
                ITextServices* pTxtSrv;
                HRESULT hr = pUnk->QueryInterface(*pIID_ITS, (void**)&pTxtSrv);
                pUnk->Release();
                if (0 <= hr)
                {
                    pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);
                    pTxtSrv->Release();
                }
            }
        }
    }
}
...