Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove annoying BEEP with RichTextBox

I placed a RichTextBox control on a new form and launched the project. So the RichTextBox.Text = "";

Each time I press Up or Down keys I heard the annoying BEEP sound! How to get rid of this issue?

Using "e.SuppressKeyPress = true" in the KeyDown method locks the Cursor position.

like image 621
Murat from Daminion Software Avatar asked Feb 25 '23 09:02

Murat from Daminion Software


2 Answers

first we need send EM_GETOLEINTERFACE message to rich edit window - this is Retrieves an IRichEditOle object that a client can use to access a rich edit control's Component Object Model (COM) functionality.

then for retrieve an ITextServices pointer, call QueryInterface on the private IUnknown pointer returned by EM_GETOLEINTERFACE.

here exist interesting point - the IID_ITextServices not well known but need get in runtime from Msftedit.dll

from 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.

after we got ITextServices pointer - we simply can call OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0)

code example:

    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();
                    }
                }
            }
        }
    }
like image 172
RbMm Avatar answered Feb 27 '23 21:02

RbMm


The following is a .Net implementation of using the ITextServices interface method described in @RbMm's answer. If you find this useful, please consider upvoting that answer.

This implementation exposes two extension methods (EnableBeep and DisableBeep) for the Winform RichTextBox. Both a C# and Vb.Net version are provided.

As we are only interested in using the OnTxPropertyBitsChange function defined in the ITextServices interface, an abbreviated implementation of the interface is used similar to those created when "Embed Interop Types" is used for a COM reference. If you have installed the Windows SDK, you should find the "TextServ.h" file at a path similar to "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\TextServ.h". This file defines the full ITextServices class interface.

C# version

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://learn.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 version

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://learn.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

If you are unfamiliar with using extension methods, please refer to the following documentation links.

Extension Methods (C# Programming Guide)

Extension Methods (Visual Basic)

like image 45
TnTinMn Avatar answered Feb 27 '23 21:02

TnTinMn