File: System\Windows\Forms\Controls\ToolStrips\ToolStripManager.ModalMenuFilter.HostedWindowsFormsMessageHook.cs
Web Access
Project: src\src\System.Windows.Forms\src\System.Windows.Forms.csproj (System.Windows.Forms)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.InteropServices;
 
namespace System.Windows.Forms;
 
public static partial class ToolStripManager
{
    internal partial class ModalMenuFilter
    {
        private class HostedWindowsFormsMessageHook
        {
            private HHOOK _messageHookHandle;
            private bool _isHooked;
            private HOOKPROC? _callBack;
            private readonly Lock _lock = new();
 
            public HostedWindowsFormsMessageHook()
            {
#if DEBUG
                _callingStack = Environment.StackTrace;
#endif
            }
 
#if DEBUG
            private readonly string _callingStack;
 
#pragma warning disable CA1821 // Remove empty Finalizers
            ~HostedWindowsFormsMessageHook()
            {
                Debug.Assert(
                    _messageHookHandle == IntPtr.Zero,
                    $"Finalizing an active mouse hook. This will crash the process. Calling stack: {_callingStack}");
            }
#pragma warning restore CA1821
#endif
 
            public bool HookMessages
            {
                get => !_messageHookHandle.IsNull;
                set
                {
                    if (value)
                    {
                        InstallMessageHook();
                    }
                    else
                    {
                        UninstallMessageHook();
                    }
                }
            }
 
            private unsafe void InstallMessageHook()
            {
                lock (_lock)
                {
                    if (!_messageHookHandle.IsNull)
                    {
                        return;
                    }
 
                    _callBack = MessageHookProc;
                    IntPtr hook = Marshal.GetFunctionPointerForDelegate(_callBack);
                    _messageHookHandle = PInvoke.SetWindowsHookEx(
                        WINDOWS_HOOK_ID.WH_GETMESSAGE,
                        (delegate* unmanaged[Stdcall]<int, WPARAM, LPARAM, LRESULT>)hook,
                        (HINSTANCE)0,
                        PInvoke.GetCurrentThreadId());
 
                    if (_messageHookHandle != IntPtr.Zero)
                    {
                        _isHooked = true;
                    }
 
                    Debug.Assert(_messageHookHandle != IntPtr.Zero, "Failed to install mouse hook");
                }
            }
 
            private unsafe LRESULT MessageHookProc(int nCode, WPARAM wparam, LPARAM lparam)
            {
                if (nCode == PInvoke.HC_ACTION && _isHooked
                    && (PEEK_MESSAGE_REMOVE_TYPE)(nuint)wparam == PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)
                {
                    // Only process messages we've pulled off the queue.
                    MSG* msg = (MSG*)(nint)lparam;
                    if (msg is not null)
                    {
                        // Call pretranslate on the message to execute the message filters and preprocess message.
                        if (Application.ThreadContext.FromCurrent().PreTranslateMessage(ref *msg))
                        {
                            msg->message = PInvoke.WM_NULL;
                        }
                    }
                }
 
                return PInvoke.CallNextHookEx(_messageHookHandle, nCode, wparam, lparam);
            }
 
            private void UninstallMessageHook()
            {
                lock (_lock)
                {
                    if (!_messageHookHandle.IsNull)
                    {
                        PInvoke.UnhookWindowsHookEx(_messageHookHandle);
                        _messageHookHandle = default;
                        _isHooked = false;
                    }
                }
            }
        }
    }
}