如何使用 RichTextBox 消除烦人的 BEEP

Posted

技术标签:

【中文标题】如何使用 RichTextBox 消除烦人的 BEEP【英文标题】:How to remove annoying BEEP with RichTextBox 【发布时间】:2011-01-13 18:20:28 【问题描述】:

我在一个新窗体上放置了一个 RichTextBox 控件并启动了该项目。所以 RichTextBox.Text = "";

每次按向上或向下键时,我都会听到烦人的 BEEP 声!如何摆脱这个问题?

在 KeyDown 方法中使用“e.SuppressKeyPress = true”锁定光标位置。

【问题讨论】:

更改操作系统的声音选项 @Jani:记事本不这样做或 Notepad++ 即使在操作系统集中的选项。 【参考方案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 指针之后——我们可以简单地调用OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0)

代码示例:

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

【讨论】:

您好,您知道将代码转换为 C# 的方法吗?我一直在获取 ITextServices 类。 @Xam 嗨,对不起,我不知道 c# 我无法翻译成这个【参考方案2】:

以下是使用@RbMm's answer中描述的ITextServices接口方法的.Net实现。如果您觉得这很有用,请考虑支持该答案。

此实现公开了 Winform RichTextBox 的两个扩展方法(EnableBeepDisableBeep)。提供 C# 和 Vb.Net 版本。

由于我们只对使用定义在 ITextServices 接口中的OnTxPropertyBitsChange 函数感兴趣,因此使用了类似于将“嵌入互操作类型”用于 COM 引用时创建的接口的缩写实现。如果您已经安装了 Windows SDK,您应该在类似于“C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\TextServ.h”的路径中找到“TextServ.h”文件.该文件定义了完整的 ITextServices 类接口。

C# 版本

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//----
public static class RTBBeepExtensions

    public static void EnableBeep(this RichTextBox rtb)
    
        SetBeepInternal(rtb, true);
    

    public static void DisableBeep(this RichTextBox rtb)
    
        SetBeepInternal(rtb, false);
    

    private static void SetBeepInternal(RichTextBox rtb, bool beepOn)
    
        const Int32 WM_USER = 0x400;
        const Int32 EM_GETOLEINTERFACE = WM_USER + 60;
        const Int32 COMFalse = 0;
        const Int32 COMTrue = ~COMFalse; // -1

        ITextServices textServices = null;
        // retrieve the rtb's OLEINTERFACE using the Interop Marshaller to cast it as an ITextServices
        // The control calls the AddRef method for the object before returning, so the calling application must call the Release method when it is done with the object.
        SendMessage(new HandleRef(rtb, rtb.Handle), EM_GETOLEINTERFACE, IntPtr.Zero, ref textServices);
        textServices.OnTxPropertyBitsChange(TXTBIT.ALLOWBEEP, beepOn ? COMTrue : COMFalse));
        Marshal.ReleaseComObject(textServices);
    

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private extern static IntPtr SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, ref ITextServices lParam);

    #region ITextServices // From TextServ.h
    [ComImport(), Guid("8d33f740-cf58-11ce-a89d-00aa006cadc5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ITextServices
    
        //see: Slots in the V-table
        //     https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definemethod-method#slots-in-the-v-table
        void _VtblGap1_16();
        Int32 OnTxPropertyBitsChange(TXTBIT dwMask, Int32 dwBits);
    

    private enum TXTBIT : uint
    
        /// <summary>If TRUE, beeping is enabled.</summary>
        ALLOWBEEP = 0x800
    
    #endregion


VB.Net 版本

Imports System
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms
' -------
Public Module RTBBeepExtensions
  <Extension()>
  Public Sub EnableBeep(rtb As RichTextBox)
    SetBeepInternal(rtb, True)
  End Sub

  <Extension()>
  Public Sub DisableBeep(rtb As RichTextBox)
    SetBeepInternal(rtb, False)
  End Sub

  Private Sub SetBeepInternal(rtb As RichTextBox, beepOn As Boolean)
    Const WM_USER As Int32 = &H400
    Const EM_GETOLEINTERFACE As Int32 = WM_USER + 60
    Const COMFalse As Int32 = 0
    Const COMTrue As Int32 = Not COMFalse ' -1

    Dim textServices As ITextServices = Nothing
    ' retrieve the rtb's OLEINTERFACE using the Interop Marshaller to cast it as an ITextServices
    ' The control calls the AddRef method for the object before returning, so the calling application must call the Release method when it is done with the object.
    SendMessage(New HandleRef(rtb, rtb.Handle), EM_GETOLEINTERFACE, IntPtr.Zero, textServices)
    textServices.OnTxPropertyBitsChange(TXTBIT.ALLOWBEEP, If(beepOn, COMTrue, COMFalse))
    Marshal.ReleaseComObject(textServices)
  End Sub

  <DllImport("user32.dll", CharSet:=CharSet.Auto)>
  Private Function SendMessage(ByVal hWnd As HandleRef, ByVal msg As Int32, ByVal wParam As IntPtr, ByRef lParam As ITextServices) As IntPtr
  End Function

#Region "ITextServices" ' From TextServ.h
  <ComImport(), Guid("8d33f740-cf58-11ce-a89d-00aa006cadc5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
  Private Interface ITextServices
    'see: Slots in the V-table
    '     https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/imetadataemit-definemethod-method#slots-in-the-v-table
    Sub _VtblGap1_16()
    Function OnTxPropertyBitsChange(ByVal dwMask As TXTBIT, ByVal dwBits As Int32) As Int32
  End Interface

  Private Enum TXTBIT As UInt32
    ''' <summary>If TRUE, beeping is enabled.</summary>
    ALLOWBEEP = &H800
  End Enum
#End Region

End Module

如果您不熟悉使用扩展方法,请参考以下文档链接。

Extension Methods (C# Programming Guide)

Extension Methods (Visual Basic)

【讨论】:

哦!这个答案很棒。我远不是一个com互操作专家,所以非常感谢你的回答。虽然我目前没有从事任何与 Winforms 相关的工作,但我认为您的回答将帮助许多其他必须处理 RichTextBox 控件的人。 我在这一行收到 System.AccessViolationException :"textServices.OnTxPropertyBitsChange(TXTBIT.ALLOWBEEP, (beepOn ? COMTrue : COMFalse));" ... 试图读取或写入受保护的内存。【参考方案3】:

好吧,只有当插入符号在第一行并且键向上,或者在最后一行并且键向下或在位置 0 并且键向左等情况下,您才能尝试抑制键。

也就是说,这是大多数文本框的行为方式,并且声音是在您的操作系统首选项中设置的。如果您尝试将插入符号移动到文本框的限制之外,您会在写字板或 Outlook 中收到相同的哔声。

【讨论】:

【参考方案4】:

下面的代码应该可以停止哔哔声,并适用于包装和未包装的文本:

private void richTextBox1_KeyDown(object sender, KeyEventArgs e)

    if (
        richTextBox1.GetLineFromCharIndex(richTextBox1.SelectionStart) == 0 && e.KeyData == Keys.Up ||
        richTextBox1.GetLineFromCharIndex(richTextBox1.SelectionStart) == richTextBox1.GetLineFromCharIndex(richTextBox1.TextLength) && e.KeyData == Keys.Down ||
        richTextBox1.SelectionStart == richTextBox1.TextLength && e.KeyData == Keys.Right ||
        richTextBox1.SelectionStart == 0 && e.KeyData == Keys.Left
    ) e.Handled = true;

【讨论】:

以上是关于如何使用 RichTextBox 消除烦人的 BEEP的主要内容,如果未能解决你的问题,请参考以下文章

如何消除 GCC 中的外部库/第三方警告 [重复]

WPF的RichTextBox控件,文字列数,只显示一列?

如何使用 RichTextBox 显示 html 内容?

如何增加richtextbox中文本的字体大小

消除: warning C4996: 'sprintf': This function or variable may be unsafe. Consider 的方法

如何更改 RichTextBox 段落间距?