如何从RICHEDIT控件中删除MessageBeep?

时间:2019-04-27 20:38:59

标签: c++ winapi com reverse-engineering richedit

RichEdit control具有此非常烦人的功能。每当用户尝试将光标移过其“ 终点”时,它都会发出蜂鸣声。例如,您可以使用也实现RICHEDIT的WordPad对其进行测试。打开它,输入一些文本,然后按【{{1}】键。如果光标不在行首:

enter image description here

按下Home键会将其移至该位置,但是再次按下Home键将产生此提示音。

乍看之下,似乎像覆盖HomeWM_KEYDOWN消息并阻止RICHEDIT可以发出蜂鸣声的情况是一种解决方案...直到我真正开始实现它。但是不幸的是,它并没有听起来那么简单,因为该控件在许多情况下都发出哔哔声!因此,我的按键阻止代码实际上膨胀到了300多个行,而且我仍然看到有些按键不是我要考虑的,或者更糟的是,我可能重写了一些有用的行为。 (有关详细信息,请阅读下文。)


然后,我决定查看RICHEDIT控件本身的实现。确实可以肯定,例如,如果我们看一下WM_KEYUP按键的实现,则在Windows 10操作系统上的Home具有称为C:\WINDOWS\SysWOW64\msftedit.dll(或?Home@CTxtSelection@@QAEHHH@Z已取消映射的偏移量public: int __thiscall CTxtSelection::Home(int,int),即已硬编码为调用MessageBeep(MB_OK),或者完全我要消除的内容:

enter image description here

如果您在上面的屏幕快照中查看地址0x3FC00,则有一种内置的方法可以绕过它,看起来像标志0x6B64FD38

因此,在深入研究0x800之后,似乎有一个名为msftedit.dll(或?OnAllowBeep@CTxtEdit@@QAEJH@Z被拆掉)的函数可以修改此标志:

enter image description here

经过更多研究,我发现RICHEDIT控件内置了COM接口,例如public: long __thiscall CTxtEdit::OnAllowBeep(int)ITextServices,它们在ITextServices::OnTxPropertyBitsChange中将该标记引用为ITextHost方法。

但是,不幸的是,我似乎找不到如何直接更改TXTBIT_ALLOWBEEP标志的方式(COM不是我的强项。)我试图研究实现ITextHost,但是它有一个许多虚拟方法与我不知道如何实现的目标无关。

有人知道如何清除TXTBIT_ALLOWBEEP标志吗?


PS。这就是为什么我没有采用覆盖按键的方法的原因: 仅举一个例子。假设我覆盖了TXTBIT_ALLOWBEEP键。我需要确保光标不在该行的开头,而且也没有选择。但是,我需要确保在光标位于窗口最上方的情况下,VK_HOME键没有按下。然后,与Ctrl键相同,我什至不确定Shift对它的作用……等等。哦,这只是Alt键。还有上,下,左,右,PageUp,PageDown,End,Delete和Backspace。 (这就是我所知道的。可能还有更多,而且我什至没有在谈论IME或其他键盘布局等。)换句话说,这变得一团糟! 因此,最终我意识到,预测按键不是可行的方法。

1 个答案:

答案 0 :(得分:6)

首先,我们需要向丰富的编辑窗口发送EM_GETOLEINTERFACE消息-这是检索IRichEditOle对象,客户端可以使用该对象来访问丰富的编辑控件的组件对象模型(COM)功能。

然后要获取ITextServices指针,请对EM_GETOLEINTERFACE返回的专用QueryInterface指针调用IUnknown

这里存在一个有趣的地方-IID_ITextServices鲜为人知,但需要从 Msftedit.dll

进入运行时。

来自About Windowless Rich Edit Controls

  

Msftedit.dll 会导出名为 IID_ITextServices 的接口标识符(IID),您可以使用该接口标识符查询IUnknown指针以获取 ITextServices 接口。

在获得ITextServices指针之后-我们只需使用TXTBIT_ALLOWBEEP掩码调用OnTxPropertyBitsChange

代码示例:

#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();
                }
            }
        }
    }
}