捕获控制台关闭事件
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 : 初次版本
*/
或者在项目点击创建新建项