求助:.NET Core中NTAccount.Translate失败后触发Safe handle已关闭异常的解决办法
我之前也踩过这个坑,这确实是.NET Core早期版本(比如你提到的2.2.0搭配System.Security.Principal.Windows 4.5.1)里的一个棘手问题——当尝试翻译不存在的域账户抛出异常后,底层的安全句柄会被意外释放,导致后续所有相关的域名翻译调用全部失败,必须重启应用才能恢复功能。
问题根源
这个bug的核心在于NTAccount.Translate()方法在执行失败时,没有正确处理内部的安全句柄资源,导致后续调用复用了已经被释放的句柄,从而触发System.ObjectDisposedException。
可行的Workaround方案
这里有两个实用的解决思路,你可以根据项目情况选择:
1. 升级到较新的.NET Core稳定版本
最彻底的解决方案就是升级框架版本——这个句柄泄漏的问题在.NET Core 3.1及以后的版本中已经被官方修复了。如果你的项目允许升级,直接把Microsoft.NETCore.App升级到3.1或更高版本,同时同步更新System.Security.Principal.Windows到对应版本,就能一劳永逸解决这个问题。
2. 用独立进程隔离翻译操作(适合无法升级框架的场景)
如果暂时不能升级框架,我们可以利用进程间资源隔离的特性,把域名翻译逻辑放到一个独立的控制台工具中,主应用通过启动子进程来执行翻译操作。这样就算子进程因为无效域出错,也不会污染主应用的资源状态。
首先编写一个独立的翻译工具(比如命名为DomainTranslateTool.exe):
class Program { static int Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Invalid input"); return 1; } try { var ntAccount = new NTAccount(args[0]); if (ntAccount.IsValidTargetType(typeof(SecurityIdentifier))) { var sid = (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier)); Console.WriteLine(sid.ToString()); return 0; // 执行成功返回0 } } catch { // 捕获所有异常,不输出错误信息 } return 1; // 执行失败返回非0 } }
然后在主应用中调用这个工具:
static string TranslateDomain(string accountName) { var processInfo = new ProcessStartInfo { FileName = "DomainTranslateTool.exe", Arguments = $"\"{accountName}\"", // 处理包含空格的账户名 RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = AppContext.BaseDirectory }; using (var process = Process.Start(processInfo)) { process.WaitForExit(); if (process.ExitCode == 0) { return process.StandardOutput.ReadToEnd().Trim(); } return string.Empty; } }
测试验证
用你提供的测试代码替换Translate方法为上面的TranslateDomain,或者升级框架后运行:
static void Main(string[] args) { var adminSid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); var adminAccount = adminSid.Translate(typeof(NTAccount)); var validAccountName = adminAccount.ToString(); foreach (var account in new[] { validAccountName, @"DOMAIN_NOT_EXISTS\Benutzer", validAccountName }) { Console.WriteLine($"Testing: {account}"); Console.WriteLine($"Result: {TranslateDomain(account)}\n"); } Console.ReadKey(); }
你会发现,即使中间调用了不存在的域,最后一次对有效域名的翻译依然能正常返回结果,不会再抛出ObjectDisposedException。
内容的提问来源于stack exchange,提问作者user27007

