【问题标题】:How to eliminate the MessageBeep from the RICHEDIT control?如何从 RICHEDIT 控件中消除 MessageBeep?
【发布时间】:2019-09-16 23:25:12
【问题描述】:

RichEdit control 有这个非常烦人的功能。每次用户试图将光标移过其“终点”时,它都会发出哔哔声。例如,您可以使用同样实现 RICHEDIT 的WordPad 对其进行测试。打开它,输入一些文本,然后点击Home 键。如果光标不在行首:

点击Home 键会将其移动到那里,但再次点击Home 键将产生此哔声。

乍一看,它似乎覆盖了WM_KEYDOWNWM_KEYUP 消息并阻止了RICHEDIT 可以发出哔声的情况......直到我真正开始实施它。不幸的是,它并不像听起来那么简单,因为该控件在很多情况下都会发出哔哔声!所以我的击键阻塞代码实际上膨胀到了 300 多行,而且我仍然看到有一些按键我没有考虑到,或者更糟糕的是,我可能已经覆盖了一些有用的行为。 (阅读下文了解更多详情。)


然后我决定研究一下 RICHEDIT 控件本身的实现。果然,例如,如果我们看一下Home 按键的实现,我的Windows 10 操作系统上的C:\WINDOWS\SysWOW64\msftedit.dll 在映射的偏移量@987654343 处具有称为?Home@CTxtSelection@@QAEHHH@Z(或public: int __thiscall CTxtSelection::Home(int,int) demangled)的函数@,这被硬编码为调用MessageBeep(MB_OK),或者正是我要消除的内容:

如果您查看上面屏幕截图中的地址0x6B64FD38,有一种内置方法可以绕过它,看起来像是标志0x800

所以在深入研究msftedit.dll 之后,似乎有一个名为?OnAllowBeep@CTxtEdit@@QAEJH@Z(或public: long __thiscall CTxtEdit::OnAllowBeep(int) demangled)的函数可以修改此标志:

经过一番研究,我发现 RICHEDIT 控件中内置了 COM 接口,例如 ITextServicesITextHostITextServices::OnTxPropertyBitsChange 方法中将该标志引用为 TXTBIT_ALLOWBEEP

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

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


PS。这就是为什么我没有走覆盖按键的路线: 只是给你一个例子。说,如果我覆盖VK_HOME 按键。我需要确保光标不在行首,而且没有选择。但是,当光标位于窗口的最顶部时,我需要确保 Ctrl 键没有按下。然后与Shift 键相同,我什至不确定Alt 用它做什么......等等。哦,这只是Home 键。还有上、下、左、右、PageUp、PageDown、End、Delete、Backspace。 (这就是我所知道的。可能还有更多,而且我什至没有在谈论 IME 或其他键盘布局等。)换句话说,它变得一团糟! 所以,最终我意识到预期击键不是要走的路。

【问题讨论】:

  • 真的需要EM_GETOLEINTERFACE发送到richedit窗口,然后查询ITextServices接口并调用pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);都是简单的,但是链接问题下没有这样的答案
  • 我回复here。不知道在这个问题下也需要复制它,或者如何

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


【解决方案1】:

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

然后要检索ITextServices 指针,请在EM_GETOLEINTERFACE 返回的私有IUnknown 指针上调用QueryInterface

这里有一个有趣的点——IID_ITextServices 不为人所知,但需要从 Msftedit.dll

进入运行时

来自About Windowless Rich Edit Controls

Msftedit.dll 导出一个名为 IID_ITextServices 的接口标识符 (IID),您可以使用它来查询 ITextServices 接口的 IUnknown 指针。

在我们得到 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();
                }
            }
        }
    }
}

【讨论】:

  • 哦,伙计,你又救了我的培根!但是该死的,我自己几乎得到了它。一路通过 IDA 挖掘,得到了所有的内部结构,但那该死的 COM 难倒了我。无论如何,感谢您的帮助!
  • @c00000fd - 我认为在调试器下更容易从调用 MessageBeep 的位置以及我们查询的对象成员、它实现的接口中获得 - 与静态代码工具不同,它只需要几分钟。真的。然后重点了解EM_GETOLEINTERFACE
  • 哦,你的意思是在我发布这个问题后你很快就解决了?
  • @c00000fd - 是的,大约 5-10 分钟用于研究,同时用于代码测试
  • 该死,这太棒了,兄弟!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2018-06-28
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多