如何从RICHEDIT控件中删除MessageBeep?

c00*_*0fd 6 c++ com winapi reverse-engineering richedit

RichEdit控件有这个很烦人的功能。每次用户尝试将光标移过其“ 端点 ”时都会发出蜂鸣声。例如,您可以使用WordPad还实现RICHEDIT的进行测试。打开它,输入一些文本,然后Home按键。如果光标不在行首:

在此处输入图片说明

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)碎片化的))函数,该函数0x3FC00被硬编码为调用MessageBeep(MB_OK),或正是我要消除的内容:

在此处输入图片说明

而且,如果您查看0x6B64FD38上面的屏幕快照中的地址,则有一种内置的方法可以绕过它,看起来像是flag 0x800

因此,深入研究msftedit.dll一下,似乎有一个称为?OnAllowBeep@CTxtEdit@@QAEJH@Z(或去public: long __thiscall CTxtEdit::OnAllowBeep(int)缠结)的函数可以修改以下标志:

在此处输入图片说明

经过更多研究后,我发现RICHEDIT控件内置了COM接口,例如ITextServicesITextHost引用该标志TXTBIT_ALLOWBEEPITextServices::OnTxPropertyBitsChange方法。

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

有谁知道如何清除该TXTBIT_ALLOWBEEP标志?


PS。这就是为什么我没有采用覆盖按键的方法的原因:仅举一个例子。说,如果我覆盖VK_HOME按键。我需要确保光标不在该行的开头,而且也没有选择。但是,我需要确保Ctrl在光标位于窗口最上方的情况下该键没有按下。然后与Shift密钥相同,我什至Alt不知道该怎么做...等等。哦,这只是Home关键。还有上,下,左,右,PageUp,PageDown,End,Delete和Backspace。(这就是我所知道的。可能还有更多,而且我什至没有在谈论IME或其他键盘布局等。)换句话说,这变得一团糟!所以, 要走的路。

RbM*_*bMm 8

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

然后要获取ITextServices指针,请调用QueryInterfaceIUnknown返回的私有指针EM_GETOLEINTERFACE

这里存在有趣的一点- IID_ITextServices不太知名,但需要从Msftedit.dll进入运行时

来自关于无窗口的Rich Edit控件

Msftedit.dll导出名为IID_ITextServices的接口标识符(IID),可用于查询ITextServices接口的IUnknown指针。

在获得ITextServices指针之后-我们只需OnTxPropertyBitsChange使用TXTBIT_ALLOWBEEP遮罩即可调用

代码示例:

#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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)