如何消除 RICHEDIT 控件的 MessageBeep?
How to eliminate the MessageBeep from the RICHEDIT control?
RichEdit control 有这个非常烦人的功能。每次用户试图将光标移过其“终点”时,它都会发出哔哔声。例如,您可以使用同样实现了 RICHEDIT 的 WordPad
来测试它。打开它,输入一些文本,然后点击 Home
键。如果光标不在行首:
按 Home
键会将其移动到那里,但再次按 Home
键会发出此蜂鸣声。
乍一看,似乎覆盖了 WM_KEYDOWN
和 WM_KEYUP
消息并阻止了 RICHEDIT 可以发出哔哔声的情况是一个解决方案……直到我真正开始实施它。不幸的是,它并不像听起来那么简单,因为在很多情况下该控件都会发出哔哔声!因此,我的击键阻止代码确实膨胀到 300 多行,而且我仍然看到有一些我没有考虑到的按键,或者更糟的是,我可能已经覆盖了一些有用的行为。 (阅读下文了解更多详情。)
然后我决定深入了解 RICHEDIT 控件本身的实现。果然,例如,如果我们查看 Home
按键的实现,我的 Windows 10 OS 上的 C:\WINDOWS\SysWOW64\msftedit.dll
具有名为 [=18= 的函数](或 public: int __thiscall CTxtSelection::Home(int,int)
demangled)在映射偏移 0x3FC00
处,硬编码调用 MessageBeep(MB_OK),或 exactly 我要消除的东西:
如果您查看上面屏幕截图中的地址 0x6B64FD38
,就会发现有一种内置的方法可以绕过它,看起来像是标志 0x800
.
所以在深入研究 msftedit.dll
之后,似乎有一个名为 ?OnAllowBeep@CTxtEdit@@QAEJH@Z
的函数(或 public: long __thiscall CTxtEdit::OnAllowBeep(int)
demangled)可以修改此标志:
经过更多研究后,我发现 RICHEDIT 控件中内置了 COM 接口,例如 ITextServices
和 ITextHost
将 [=43] 中的标志引用为 TXTBIT_ALLOWBEEP
=]方法。
不幸的是,我似乎无法找到直接更改 TXTBIT_ALLOWBEEP
标志的方法(COM 不是我的强项。)我尝试研究实施 ITextHost
,但它有很多虚方法与我想要实现的目标无关,我不知道如何实现。
有人知道如何清除 TXTBIT_ALLOWBEEP
标志吗?
PS。这就是为什么我没有走覆盖按键的路线:
只是给你举个例子。比如说,如果我覆盖 VK_HOME
按键。我需要确保光标不在行的开头,而且没有选择。但是,我需要确保在光标位于 window 最顶部的情况下 Ctrl
键没有按下。然后与 Shift
键相同,我什至不确定 Alt
对它做了什么......等等。哦,这只是 Home
键。还有上、下、左、右、PageUp、PageDown、End、Delete、Backspace。 (这就是我所知道的。可能还有更多,而且我什至没有谈论 IME 或其他键盘布局等)换句话说,它变得一团糟!
所以,最终我意识到预期击键 不是 的方法。
首先我们需要发送 EM_GETOLEINTERFACE
消息到 rich edit window - 这是检索 IRichEditOle 对象,客户端可以使用该对象访问 rich edit 控件的组件对象模型 (COM) 功能。
然后为了检索 ITextServices
指针,在 EM_GETOLEINTERFACE
返回的私有 IUnknown
指针上调用 QueryInterface
。
这里存在有趣的一点 - IID_ITextServices
不为人知但需要在运行时从 Msftedit.dll
获取
来自 About Windowless Rich Edit Controls
Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.
在我们得到 ITextServices
pointer - we simply can call 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();
}
}
}
}
}
RichEdit control 有这个非常烦人的功能。每次用户试图将光标移过其“终点”时,它都会发出哔哔声。例如,您可以使用同样实现了 RICHEDIT 的 WordPad
来测试它。打开它,输入一些文本,然后点击 Home
键。如果光标不在行首:
按 Home
键会将其移动到那里,但再次按 Home
键会发出此蜂鸣声。
乍一看,似乎覆盖了 WM_KEYDOWN
和 WM_KEYUP
消息并阻止了 RICHEDIT 可以发出哔哔声的情况是一个解决方案……直到我真正开始实施它。不幸的是,它并不像听起来那么简单,因为在很多情况下该控件都会发出哔哔声!因此,我的击键阻止代码确实膨胀到 300 多行,而且我仍然看到有一些我没有考虑到的按键,或者更糟的是,我可能已经覆盖了一些有用的行为。 (阅读下文了解更多详情。)
然后我决定深入了解 RICHEDIT 控件本身的实现。果然,例如,如果我们查看 Home
按键的实现,我的 Windows 10 OS 上的 C:\WINDOWS\SysWOW64\msftedit.dll
具有名为 [=18= 的函数](或 public: int __thiscall CTxtSelection::Home(int,int)
demangled)在映射偏移 0x3FC00
处,硬编码调用 MessageBeep(MB_OK),或 exactly 我要消除的东西:
如果您查看上面屏幕截图中的地址 0x6B64FD38
,就会发现有一种内置的方法可以绕过它,看起来像是标志 0x800
.
所以在深入研究 msftedit.dll
之后,似乎有一个名为 ?OnAllowBeep@CTxtEdit@@QAEJH@Z
的函数(或 public: long __thiscall CTxtEdit::OnAllowBeep(int)
demangled)可以修改此标志:
经过更多研究后,我发现 RICHEDIT 控件中内置了 COM 接口,例如 ITextServices
和 ITextHost
将 [=43] 中的标志引用为 TXTBIT_ALLOWBEEP
=]方法。
不幸的是,我似乎无法找到直接更改 TXTBIT_ALLOWBEEP
标志的方法(COM 不是我的强项。)我尝试研究实施 ITextHost
,但它有很多虚方法与我想要实现的目标无关,我不知道如何实现。
有人知道如何清除 TXTBIT_ALLOWBEEP
标志吗?
PS。这就是为什么我没有走覆盖按键的路线:
只是给你举个例子。比如说,如果我覆盖 VK_HOME
按键。我需要确保光标不在行的开头,而且没有选择。但是,我需要确保在光标位于 window 最顶部的情况下 Ctrl
键没有按下。然后与 Shift
键相同,我什至不确定 Alt
对它做了什么......等等。哦,这只是 Home
键。还有上、下、左、右、PageUp、PageDown、End、Delete、Backspace。 (这就是我所知道的。可能还有更多,而且我什至没有谈论 IME 或其他键盘布局等)换句话说,它变得一团糟!
所以,最终我意识到预期击键 不是 的方法。
首先我们需要发送 EM_GETOLEINTERFACE
消息到 rich edit window - 这是检索 IRichEditOle 对象,客户端可以使用该对象访问 rich edit 控件的组件对象模型 (COM) 功能。
然后为了检索 ITextServices
指针,在 EM_GETOLEINTERFACE
返回的私有 IUnknown
指针上调用 QueryInterface
。
这里存在有趣的一点 - IID_ITextServices
不为人知但需要在运行时从 Msftedit.dll
来自 About Windowless Rich Edit Controls
Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.
在我们得到 ITextServices
pointer - we simply can call 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();
}
}
}
}
}