C#控制台程序 - 技巧合集

捕获控制台关闭事件

using BSServer.GameServer;
using log4net;
using System.Runtime.InteropServices;

namespace BSServer
{
    internal class Program
    {
        #region 控制台关闭
        public delegate bool ControlCtrlDelegate(int CtrlType);
        [DllImport("kernel32.dll")]
        private static extern bool SetConsoleCtrlHandler(ControlCtrlDelegate HandlerRoutine, bool Add);
        private static ControlCtrlDelegate cancelHandler = new ControlCtrlDelegate(HandlerRoutine);
        #endregion



        static bool HandlerRoutine(int eventType)
        {
            Loger.Info("系统关闭。eventType : " + eventType);
            
            return false;
        }

        static void Main(string[] args)
        {
            new Loger("svr");
    
            SetConsoleCtrlHandler(cancelHandler, true);
            Loger.Info("系统启动。");


            Thread.Sleep(-1);
            Loger.Info("系统关闭。");
        }
    }
}

这个需要引入kernel32,还可以使用另外一个方法:

currentDoamin.ProcessExit += OnExitApp;
//Log4net的实例化需要放在ProcessExit 下面
new Loger("svr");

private static void OnExitApp(object? sender, EventArgs e)
{
    Loger.Info("系统关闭。");
}

如果使用了log4net来输出,要记得Log4net的实例化需要放在currentDoamin.ProcessExit += OnExitApp的后面,否则在程序退出的时候无法写入log。

全局捕获报错

通过UnhandledException 来实现。

using BSServer.GameServer;
using log4net;
using System.Runtime.InteropServices;

namespace BSServer
{
    internal class Program
    {
       

        static void Main(string[] args)
        {
            new Loger("svr");
        
            Loger.Info("系统启动。");

            AppDomain currentDoamin = default(AppDomain);
            currentDoamin = AppDomain.CurrentDomain;
            currentDoamin.UnhandledException += GlobalUnhandledExceptionHandler;
            
           

            Thread.Sleep(-1);
            Loger.Info("系统关闭。");
        }

        private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {
            Exception ex = default(Exception);
            ex = (Exception)e.ExceptionObject;
            //ILog log = LogManager.GetLogger(typeof(Program));
            //log.Error(ex.Message + "\n" + ex.StackTrace);

            Loger.Error("错误:" + ex.Message + "\n" + ex.StackTrace);
        }
    }
}

控制台鼠标点击窗口程序会暂停运行的问题

在控制台程序上选择文本,点击鼠标,或者拖动滚动条都会暂停程序的运行,我们看到WriteLine无法输出。其实根源就是WriteLine函数。

在网上看到了通过关闭控制台程序的快速编辑功能来实现,但是会导致鼠标无法选择复制。如下代码:

using BSServer.GameServer;
using log4net;
using System.Runtime.InteropServices;

namespace BSServer
{
    internal class Program
    {

        #region 关闭控制台程序的快速编辑
        //关闭 cmd 窗口默认为快速编辑(quickedit),解决控制台程序,鼠标点击暂停运行

        const int STD_INPUT_HANDLE = -10;
        const uint ENABLE_QUICK_EDIT_MODE = 0x0040;
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern IntPtr GetStdHandle(int hConsoleHandle);
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint mode);
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode);


        public static void DisbleQuickEditMode()
        {
            IntPtr hStdin = GetStdHandle(STD_INPUT_HANDLE);
            uint mode;
            GetConsoleMode(hStdin, out mode);
            mode &= ~ENABLE_QUICK_EDIT_MODE;
            SetConsoleMode(hStdin, mode);

        }
        #endregion



        static void Main(string[] args)
        {
            new Loger("svr");
            DisbleQuickEditMode();

            Loger.Info("系统启动。");


            Thread.Sleep(-1);
            Loger.Info("系统关闭。");
        }

      
    }
}

Console.WriteLine是一个阻塞操作(blocking operation),真正的元凶是他。在我们鼠标选择的时候WriteLine无法工作,导致程序被阻塞了。

所以我们只要写一个总的WriteLine静态函数,然后异步执行就不会阻塞程序运行了。
例如:

public static void Debug(string str)
{
    //Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}]" + " DEBUG:" + str);

    _ = WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}]" + " DEBUG:" + str);
}
async static Task WriteLine(string str)
{
    await Task.Delay(1);
    Console.WriteLine(str);
}

这下完美了,不但可以编辑文本,也不影响输出。

根据宏定义判断不同的平台是Win还是Linux

如果您想在编译时执行此检查
直接修改项目文件 ,添加如下:

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net7.0</TargetFramework>
 <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
 <IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
 <IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>

 <ImplicitUsings>enable</ImplicitUsings>
  <Nullable>enable</Nullable>
  <ApplicationIcon>images\icon.ico</ApplicationIcon>
  <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 <PublishTrimmed>true</PublishTrimmed>
 <PublishReadyToRun>true</PublishReadyToRun>
 <PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
	
	<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
	</PropertyGroup>
	<PropertyGroup Condition="'$(IsOSX)'=='true'">
<DefineConstants>OSX</DefineConstants>
	</PropertyGroup>
	<PropertyGroup Condition="'$(IsLinux)'=='true'">
<DefineConstants>Linux</DefineConstants>
	</PropertyGroup>

C#代码中

#if Windows
            Loger.Info("Win平台特殊处理。");
#endif

具体参考这里

如果直接想编译指定的编译,可以添加编译符号,例如添加LINUX

	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
	  <DefineConstants>$(DefineConstants);LINUX</DefineConstants>
	</PropertyGroup>
	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
	  <DefineConstants>$(DefineConstants);LINUX</DefineConstants>
	</PropertyGroup>

在代码中直接

#if LINUX
            Loger.Info("LINUX。");
#endif

增加修订号

默认控制台没有AssemblyInfo.cs修订号可以修改。
我们手动增加
首先在项目设置里添加GenerateAssemblyInfo

<PropertyGroup>
  <!--<OutputType>Exe</OutputType>-->
  <TargetFramework>net6.0</TargetFramework>
 <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 <ImplicitUsings>enable</ImplicitUsings>
  <Nullable>enable</Nullable>
  <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>

然后创建一个AssemblyInfo.cs,添加如下内容

using System.Reflection;
using System.Runtime.InteropServices;

// 在此类的 SDK 样式项目中,现在,在此文件中早前定义的几个程序集属性将在生成期间自动添加,并使用在项目属性中定义的值进行填充。有关包含的属性以及如何定制此过程的详细信息,请参阅
// https://aka.ms/assembly-info-properties

// 将 ComVisible 设置为 false 会使此程序集中的类型对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,请将该类型的 ComVisible
// 属性设置为 true。

[assembly: ComVisible(false)]

// 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID。

[assembly: Guid("aa30961a-1f5e-469d-8e3e-9db5a5174521")]

//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.1")]

/*
 * 1.0.0.1 : 初次版本
 */

或者在项目点击创建新建项
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值