C# WPF 控制台管理器调试断言崩溃

C# WPF Console Manager Debug Assert Crash

我在 dotNet 中从事的当前项目处于一个有趣的困境中。目前,我的项目是一个 WPF 应用程序,它为控制台应用程序配置设置,然后启动应用程序的主要功能(无限循环 API 更新地图上对象的挂钩)。以前,我一直在使用我在 No output to console from a WPF application? 找到的解决方案,作为解决方案提供的 ConsoleManager class 运行良好。我最近 运行 遇到了解决方案停止工作的问题,总是在 Debug.Assert(_out!=NULL) 的调试或发布版本中失败,向我吐出以下错误消息。

Process terminated. Assertion failed.
   at CT_API_GUI.ConsoleManager.InvalidateOutAndError() in C:\Users\pixel\RiderProjects\AFMChronotrackAPI\ConsoleManager.cs:line 82
   at CT_API_GUI.ConsoleManager.Show() in C:\Users\pixel\RiderProjects\AFMChronotrackAPI\ConsoleManager.cs:line 38
   at CT_API_GUI.WinApi.Main() in C:\Users\pixel\RiderProjects\AFMChronotrackAPI\WinApi.cs:line 53
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at CT_API_GUI.WinApi.Main()
   at CT_API_GUI.MainWindow.Run_OnClick(Object sender, RoutedEventArgs e) in C:\Users\pixel\RiderProjects\AFMChronotrackAPI\MainWindow.xaml.cs:line 41
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
   at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run()
   at CT_API_GUI.App.Main()

其中 ConsoleManager 的第 82 行是 Debug.Assert(_out != NULL)

当前 运行来自 JetBrains Rider 的 ning dotNet 5.0.302 IDE 2021.1.5.

我发现让 WPF 应用程序启动控制台比较少见,但为了正确配置设置,它是最方便的。

非常感谢任何帮助!

编辑: 这是我为 ConsoleManager 使用的具体代码

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace CT_API_GUI
{
    public static class ConsoleManager
    {
        private const string Kernel32_DllName = "kernel32.dll";

        [DllImport(Kernel32_DllName)]
        private static extern bool AllocConsole();

        [DllImport(Kernel32_DllName)]
        private static extern bool FreeConsole();

        [DllImport(Kernel32_DllName)]
        private static extern IntPtr GetConsoleWindow();

        [DllImport(Kernel32_DllName)]
        private static extern int GetConsoleOutputCP();

        public static bool HasConsole
        {
            get { return GetConsoleWindow() != IntPtr.Zero; }
        }

        /// <summary>
        /// Creates a new console instance if the process is not attached to a console already.
        /// </summary>
        public static void Show()
        {
            //#if DEBUG
            if (!HasConsole)
            {
                AllocConsole();
                InvalidateOutAndError();
            }
            //#endif
        }

        /// <summary>
        /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
        /// </summary>
        public static void Hide()
        {
            //#if DEBUG
            if (HasConsole)
            {
                SetOutAndErrorNull();
                FreeConsole();
            }
            //#endif
        }

        public static void Toggle()
        {
            if (HasConsole)
            {
                Hide();
            }
            else
            {
                Show();
            }
        }

        static void InvalidateOutAndError()
        {
            Type type = typeof(System.Console);

            System.Reflection.FieldInfo _out = type.GetField("_out",
                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

            System.Reflection.FieldInfo _error = type.GetField("_error",
                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

            System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

            Debug.Assert(_out != null);
            Debug.Assert(_error != null);

            Debug.Assert(_InitializeStdOutError != null);

            _out.SetValue(null, null);
            _error.SetValue(null, null);

            _InitializeStdOutError.Invoke(null, new object[] {true});
        }

        static void SetOutAndErrorNull()
        {
            Console.SetOut(TextWriter.Null);
            Console.SetError(TextWriter.Null);
        }
    }
}

从 .NET Framework 4x 迁移到 .NET Core System.Console 中保存对 StdOut Textwriter 的引用的字段名称已从 .NET Framework 中的 _out 更改为 s_out.NET Core.

所以你需要调整你的反射代码...

#if NET_CORE 
    System.Reflection.FieldInfo _out = type.GetField("s_out", ...);
#else
    System.Reflection.FieldInfo _out = type.GetField("_out", ...);
#endif

源代码System.Console.NET Core https://source.dot.net/#System.Console/System/Console.cs,f907d79481da6ba4

源代码System.Console.NET Framework 4.6 https://github.com/microsoft/referencesource/blob/master/mscorlib/system/console.cs