我试着把 keys 扣钩住.按下数字锁定键和大写锁定键时显示一个窗口,并在窗口中显示当前锁定键的状态.当我试图快速单击数字锁定键或大写锁定键时,它将在100.Visual Studio显示101中崩溃.

这是100崩溃时的代码:

static void Main(string[] args)
{
    XamlCheckProcessRequirements();
    
    global::WinRT.ComWrappersSupport.InitializeComWrappers();
    global::Microsoft.UI.Xaml.Application.Start((p) => {
        var context = new global::Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
        global::System.Threading.SynchronizationContext.SetSynchronizationContext(context);
        new App();
    });
}

我认为这次崩溃似乎是因为TextBlock文本的快速更 retrofit 成的.

这是100:

<Page
    x:Class="MoreFlyout.Views.FlyoutPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:MoreFlyout.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.Resources>
        <AcrylicBrush x:Key="CustomAcrylicBrush" AlwaysUseFallback="False" />
    </Page.Resources>

    <Page.ContextFlyout>
        <Flyout x:Name="FlyoutWindowContextFlyout" ShouldConstrainToRootBounds="False">
            <Flyout.SystemBackdrop>
                <local:AcrylicSystemBackdrop />
            </Flyout.SystemBackdrop>
            <Flyout.FlyoutPresenterStyle>
                <Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
                    <Setter Property="Background" Value="Transparent" />
                </Style>
            </Flyout.FlyoutPresenterStyle>

            <Grid
                x:Name="ContentArea"
                MinWidth="164"
                Padding="-8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="48" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <HyperlinkButton
                    x:Name="IconButton"
                    Grid.Column="0"
                    Width="32"
                    Height="32"
                    IsEnabled="False">
                    <FontIcon
                        x:Name="StatusFontIcon"
                        Margin="-4"
                        FontSize="12"
                        Foreground="White"
                        Glyph="&#xE72E;" />
                </HyperlinkButton>

                <TextBlock
                    x:Name="StatusTextBlock"
                    Grid.Column="1"
                    VerticalAlignment="Center"
                    TextAlignment="Center" />
            </Grid>
        </Flyout>
    </Page.ContextFlyout>
</Page>

这是100:

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using MoreFlyout.Helpers;
using MoreFlyout.ViewModels;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;

namespace MoreFlyout.Views;

public sealed partial class FlyoutPage : Page
{
    private readonly Microsoft.UI.Dispatching.DispatcherQueue dispatcherQueue;
    private static System.Timers.Timer? aTimer;
    private UnhookWindowsHookExSafeHandle HookID;

    private const int WM_KEYDOWN = 0x0100;
    private const int VK_NUMLOCK = 0x90;
    private const int VK_CAPSLOCK = 0x14;

    private static bool numKeyState = false;
    private static bool capsKeyState = false;

    public FlyoutViewModel ViewModel
    {
        get;
    }

    public FlyoutPage()
    {
        ViewModel = App.GetService<FlyoutViewModel>();
        InitializeComponent();

        dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();

        numKeyState = (PInvoke.GetKeyState(VK_NUMLOCK) & 1) == 1;
        capsKeyState = (PInvoke.GetKeyState(VK_CAPSLOCK) & 1) == 1;

        HookID = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, HookCallback, null, 0);

        App.FlyoutWindow.Closed += FlyoutWindowClosed;
    }

    private void FlyoutWindowClosed(object sender, WindowEventArgs args) => HookID.Close();

    private LRESULT HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (nCode >= 0 && wParam == WM_KEYDOWN)
        {
            var vkCode = Marshal.ReadInt32(lParam);
            if (vkCode == VK_NUMLOCK)
            {
                StatusTextBlock.Text = numKeyState ? "StatusWords_NumUnlock".GetLocalized() : "StatusWords_NumLock".GetLocalized();
                StatusFontIcon.Glyph = numKeyState ? "\uE785" : "\uE72E";
                numKeyState = !numKeyState;
            }
            else if (vkCode == VK_CAPSLOCK)
            {
                StatusTextBlock.Text = capsKeyState ? "StatusWords_CapsUnlock".GetLocalized() : "StatusWords_CapsLock".GetLocalized();
                StatusFontIcon.Glyph = capsKeyState ? "\uE785" : "\uE72E";
                capsKeyState = !capsKeyState;
            }

            if(FlyoutWindowContextFlyout.IsOpen == false) FlyoutWindowContextFlyout.ShowAt(this);
        }
        return PInvoke.CallNextHookEx(null, nCode, wParam, lParam);
    }

我试着在try catch中加入StatusTextBlock.Text = numKeyState ? "StatusWords_NumUnlock".GetLocalized() : "StatusWords_NumLock".GetLocalized();个,但没有任何变化,仍然显示一个关于"系统"的崩溃.执行不例外".

我将这些NuGet用于核心代码:

  • Microsoft.Windows.CsWin32版本:0.3.49—beta
  • Microsoft. WindowsAppSDK版本:1.5.240311000

推荐答案

发生这种情况是因为你运行在. NET上,它是一个垃圾收集器(GC),可以在不通知你的情况下移动东西.这就是所谓的"管理"是有原因的:—)

但是,当你使用本地API挂钩键盘时,你会给Windows一个原始指针,该指针总是指向你的方法代码.如果GC改变了指针指向的内容,那么所有的事情都可能在回调时发生,比如执行引擎崩溃,或者崩溃等等.

要解决这个问题,必须要求GC不要移动方法代码.您可以使用GCHandle来实现这个目标,或者简单地创建一个静态方法的引用.

这就是我在这里演示的,我几乎保持了FlyoutPage的HookCallback实例方法(不再需要Result),但给了Windows一个在运行时不会移动的真正的静态钩子方法:

public sealed partial class FlyoutPage : Page
{
    // this static assignement will ensure GC doesn't move the procedure around
    private static readonly HOOKPROC _hook = GlobalHookCallback;
    private static readonly UnhookWindowsHookExSafeHandle _hookId;

    static FlyoutPage()
    {
        // hook here (expected to be done in UI thread in our case, it facilitates everything)
        _hookId = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, _hook, null, 0);
        
        // unhook on exit (more or less useless)
        AppDomain.CurrentDomain.ProcessExit += (s, e) => { _hookId.Close(); };
    }

    // the hook function must be static
    private static LRESULT GlobalHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
    {
        // lucky us WH_KEYBOARD_LL calls back on initial hooking thread, ie: the UI thread
        // so no need for Dispatcher mumbo jumbo
        
        // get your navigation service and defer to the instance method if found
        var navigation = App.GetService<INavigationService>();
        if (navigation?.Frame != null)
        {
            if (navigation.Frame.Content is not FlyoutPage)
            {
                navigation.NavigateTo(typeof(FlyoutViewModel).FullName!);
            }

            if (navigation.Frame.Content is FlyoutPage page)
            {
                page.HookCallback(nCode, wParam, lParam);
            }
        }
        return PInvoke.CallNextHookEx(null, nCode, wParam, lParam);
    }

    public FlyoutPage()
    {
        ... remove hook from here
    }

    private void HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
    {
        ... just defined as void 
    }
 }

Csharp相关问答推荐

ß != ss与ICU进行不区分大小写的比较

需要深入了解NpgSQL DateTimeOffset处理

EF Core 8—应用客户端投影后无法转换集操作

`Task`只有在C#中等待时才会运行吗?

从c#列表中删除额外的对象&对象&>从ASP.NET WebForm返回json响应

注册所有IMediatR类

如何在C#中实现非抛出`MinBy`?

EF核心新验证属性`DeniedValues`和`StringCompison`不起作用

RabbitMQ群集的MassTransit配置问题

在被Interactive Server切换后,Blazor SSR页面无法正确加载JS

Foreach非常慢的C#

正在try 将自定义字体添加到我的控制台应用程序

数据库操作预计影响1行,但实际影响0行; after _dbContext.SaveChanges();

使用免费的DotNet库从Azure函数向Azure文件共享上的现有Excel文件追加行

.NET文档对继承的困惑

如何对正方形格线进行对角分组

我想我必须手动使用res1(字符串形式的PowerShell哈希表)

CsvHelper在第二次迭代时抛出System.ObjectDisposedException

为什么我不能在固定语句中使用外部函数?

自定义ConsoleForMatter中的DI/Http上下文