c# – 如何在Unity中实现和使用低级键盘钩子来禁用Windows快捷方式?

我的问题

如何在Unity中实现和使用低级键盘钩子来禁用Windows快捷方式?

我想通过意外使用Windows密钥来防止用户失去我的游戏焦点.这是因为我的应用程序是专为可以随机键盘的幼儿设计的.

searching stack overflow开始,我似乎需要实现一个低级键盘钩子.

我试过了什么

以下内容已在Unity中实施.当按下打印屏幕按钮时,它应该将我的应用程序的背景颜色变为黑色,证明我已正确实现它.但是,在测试时,看看我是否可以使用此捕获键盘输入,我发现了这段代码

        Debug.Log("Print Screen");
        Camera cam = FindobjectOfType<Camera>();
        cam.backgroundColor = Color.black; 

没有被调用,背景颜色不会变为黑色.

代码

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace SnagFree.TrayApp.Core
{
    class GlobalKeyboardHookEventArgs : HandledEventArgs
    {
        public GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; }
        public GlobalKeyboardHook.LowLevelKeyboardInputEvent KeyboardData { get; private set; }

        public GlobalKeyboardHookEventArgs(
            GlobalKeyboardHook.LowLevelKeyboardInputEvent keyboardData,
            GlobalKeyboardHook.KeyboardState keyboardState)
        {
            KeyboardData = keyboardData;
            KeyboardState = keyboardState;
        }
    }

    //Based on https://gist.github.com/Stasonix
    class GlobalKeyboardHook : Idisposable
    {
        public event EventHandler<GlobalKeyboardHookEventArgs> Keyboardpressed;

        public GlobalKeyboardHook()
        {
            _windowsHookHandle = IntPtr.Zero;
            _user32LibraryHandle = IntPtr.Zero;
            _hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetwindowsHookEx behavIoUr.

            _user32LibraryHandle = LoadLibrary("User32");
            if (_user32LibraryHandle == IntPtr.Zero)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }



            _windowsHookHandle = SetwindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
            if (_windowsHookHandle == IntPtr.Zero)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode, $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
            }
        }

        protected virtual void dispose(bool disposing)
        {
            if (disposing)
            {
                // because we can unhook only in the same thread, not in garbage collector thread
                if (_windowsHookHandle != IntPtr.Zero)
                {
                    if (!UnhookWindowsHookEx(_windowsHookHandle))
                    {
                        int errorCode = Marshal.GetLastWin32Error();
                        throw new Win32Exception(errorCode, $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
                    }
                    _windowsHookHandle = IntPtr.Zero;

                    // ReSharper disable once DelegateSubtraction
                    _hookProc -= LowLevelKeyboardProc;
                }
            }

            if (_user32LibraryHandle != IntPtr.Zero)
            {
                if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
                {
                    int errorCode = Marshal.GetLastWin32Error();
                    throw new Win32Exception(errorCode, $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
                }
                _user32LibraryHandle = IntPtr.Zero;
            }
        }

        ~GlobalKeyboardHook()
        {
            dispose(false);
        }

        public void dispose()
        {
            dispose(true);
            GC.SuppressFinalize(this);
        }

        private IntPtr _windowsHookHandle;
        private IntPtr _user32LibraryHandle;
        private HookProc _hookProc;

        delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpFileName);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool FreeLibrary(IntPtr hModule);

        /// <summary>
        /// The SetwindowsHookEx function installs an application-defined hook procedure into a hook chain.
        /// You would install a hook procedure to monitor the system for certain types of events. These events are
        /// associated either with a specific thread or with all threads in the same desktop as the calling thread.
        /// </summary>
        /// <param name="idHook">hook type</param>
        /// <param name="lpfn">hook procedure</param>
        /// <param name="hMod">handle to application instance</param>
        /// <param name="dwThreadId">thread identifier</param>
        /// <returns>If the function succeeds, the return value is the handle to the hook procedure.</returns>
        [DllImport("USER32", SetLastError = true)]
        static extern IntPtr SetwindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);

        /// <summary>
        /// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetwindowsHookEx function.
        /// </summary>
        /// <param name="hhk">handle to hook procedure</param>
        /// <returns>If the function succeeds, the return value is true.</returns>
        [DllImport("USER32", SetLastError = true)]
        public static extern bool UnhookWindowsHookEx(IntPtr hHook);

        /// <summary>
        /// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
        /// A hook procedure can call this function either before or after processing the hook information.
        /// </summary>
        /// <param name="hHook">handle to current hook</param>
        /// <param name="code">hook code passed to hook procedure</param>
        /// <param name="wParam">value passed to hook procedure</param>
        /// <param name="lParam">value passed to hook procedure</param>
        /// <returns>If the function succeeds, the return value is true.</returns>
        [DllImport("USER32", SetLastError = true)]
        static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);

        [StructLayout(LayoutKind.Sequential)]
        public struct LowLevelKeyboardInputEvent
        {
            /// <summary>
            /// A virtual-key code. The code must be a value in the range 1 to 254.
            /// </summary>
            public int VirtualCode;

            /// <summary>
            /// A hardware scan code for the key. 
            /// </summary>
            public int HardwareScanCode;

            /// <summary>
            /// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
            /// </summary>
            public int Flags;

            /// <summary>
            /// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
            /// </summary>
            public int TimeStamp;

            /// <summary>
            /// Additional information associated with the message. 
            /// </summary>
            public IntPtr Additionalinformation;
        }

        public const int WH_KEYBOARD_LL = 13;
        //const int HC_ACTION = 0;

        public enum KeyboardState
        {
            KeyDown = 0x0100,
            KeyUp = 0x0101,
            SysKeyDown = 0x0104,
            SysKeyUp = 0x0105
        }

        public const int VkSnapshot = 0x2c;
        //const int VkLwin = 0x5b;
        //const int VkRwin = 0x5c;
        //const int VkTab = 0x09;
        //const int VkEscape = 0x18;
        //const int VkControl = 0x11;
        const int KfAltdown = 0x2000;
        public const int LlkhfAltdown = (KfAltdown >> 8);

        public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            bool fEatKeystroke = false;

            var wparamTyped = wParam.ToInt32();
            if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
            {
                object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));
                LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o;

                var eventArguments = new GlobalKeyboardHookEventArgs(p, (KeyboardState)wparamTyped);

                EventHandler<GlobalKeyboardHookEventArgs> handler = Keyboardpressed;
                handler?.Invoke(this, eventArguments);

                fEatKeystroke = eventArguments.Handled;
            }

            return fEatKeystroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }
    }
}

用法

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace SnagFree.TrayApp.Core

{

internal class Controller : MonoBehavIoUr
{
    public bool printScreen = false;

    private GlobalKeyboardHook _globalKeyboardHook;

    public void SetupKeyboardHooks()
    {
        _globalKeyboardHook = new GlobalKeyboardHook();
        _globalKeyboardHook.Keyboardpressed += OnKeypressed;
    }

    private void OnKeypressed(object sender, GlobalKeyboardHookEventArgs e)
    {

        //Debug.WriteLine(e.KeyboardData.VirtualCode);

        if (e.KeyboardData.VirtualCode != GlobalKeyboardHook.VkSnapshot)
            return;

        // seems, not needed in the life.
        //if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.SysKeyDown &&
        //    e.KeyboardData.Flags == GlobalKeyboardHook.LlkhfAltdown)
        //{
        //    MessageBox.Show("Alt + Print Screen");
        //    e.Handled = true;
        //}
        //else

        if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown)
        {

            e.Handled = true;
            Debug.Log("Print Screen");
            Camera cam = FindobjectOfType<Camera>();
            cam.backgroundColor = Color.black;
        }
    }

    public void dispose()
    {
        _globalKeyboardHook?.dispose();
    }
}
}

解决方法:

要禁用/放弃或忽略低级输入,您必须使用原始输入

详情:https://docs.microsoft.com/en-us/windows/desktop/inputdev/raw-input

但是在Unity中使用它并不是一个好主意!

解释为什么它不是一个好主意(在评论中提问):

使用统一的“板载”工具,您无法禁用/忽略Windows键或其他组合等关键事件,因为您的操作系统将首先处理这些事件,在此之后,它会将这些事件传递给您的程序,如Unity /独立播放器.要在Unity中使用不安全的代码(导入W32Libs),您必须处理您正在做的事情.没有人阻止你创建内存泄漏. Unity也不是这样设计的,这可能导致运行时出现不稳定和非常奇怪的行为(即使在编辑器中)

当您从操作系统/环境中删除他们正常使用的工作行为时,您也会开始讨厌您的用户,例如:当由于密钥被删除而无法访问“打开任务管理器”功能时,如何在崩溃时关闭应用程序?如何切换到我的桌面.

这就是我写“但在Unity中使用它不是一个好主意”的原因.

你可以,但如果你不得不要求一种方法,你可能没有经验使用它而不会损害你的系统或用户:)

相关文章

Windows2012R2备用域控搭建 前置操作 域控主域控的主dns:自...
主域控角色迁移和夺取(转载) 转载自:http://yupeizhi.blo...
Windows2012R2 NTP时间同步 Windows2012R2里没有了internet时...
Windows注册表操作基础代码 Windows下对注册表进行操作使用的...
黑客常用WinAPI函数整理之前的博客写了很多关于Windows编程的...
一个简单的Windows Socket可复用框架说起网络编程,无非是建...