See the question and my original answer on StackOverflow

This happens because you're running on .NET wich as a garbage collector (GC) which can move things around without telling you. Hey, that's called "managed" for a reason :-)

But when you hook the keyboard using a native API, you give Windows a raw pointer that's always supposed to point to your method code. If GC changes what the pointer points to, all sort of things can happen at callback time, such as Execution Engine Exceptions, or crashes, etc. This is what you see.

To fix this, you must ask GC to not move your method code. You can use a GCHandle for that or simply create a reference to a static method.

That's what demonstrated here, I keep FlyoutPage's HookCallback instance method almost as is (result is not needed anymore) but give Windows a real static hook method that never moves during runtime:

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 
    }
 }