上下文:
我负责将一个服务从 .NET Framework 4.7.2 升级到 .NET Core 6。该服务由启动其他一些服务的主服务调用。这个主服务是所有这些子服务的错误句柄输出,我的服务也不例外。要获取此输出窗口的句柄以从我的服务写入日志,请使用
Win32::GetStdHandle
。
这在 .NET Framework 中完美运行:在 Visual Studio 2022 中启动应用程序进行调试时,所有日志记录信息都会转到
Output
窗口,并且当使用主服务启动时,会记录到主服务控制台窗口。
问题在于:在 .NET Core 6 中,在完全相同的条件下,
Win32::GetStdHandle
调用返回 0,即无效句柄。
如何让 GetStdHandle 调用按预期工作,在 .NET Core 6 中返回正确的句柄,或者使用任何解决方法来获取启动服务的进程的 STD_ERROR_HANDLE?
可重现的示例,在 .NET Framework 4.7.2 或 .NET Core 6 中:
(注意:将输出到控制台应用程序中的控制台,如果在 Windows 应用程序中并在 Visual Studio 中启动,则将输出到
Output
。)
internal class Program
{
static void Main(string[] args)
{
SafeFileHandle handle;
TestLogHandle test = new TestLogHandle();
test.Init();
test.SendMessage("aaaaaaa");
}
}
class TestLogHandle
{
private SafeFileHandle _handle;
private FileStream _stream;
const int STD_ERROR_HANDLE = -12;
private const int BUFFER_SIZE = 4096;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
internal FileStream Init()
{
IntPtr iStdOut = GetStdHandle(STD_ERROR_HANDLE);
_handle = new SafeFileHandle(iStdOut, true); //returns 0 in .NET Core 6
if (_handle.IsInvalid)
{
throw new Exception("Invalid handle."); //throws in .NET Core 6
}
_stream = new FileStream(_handle, FileAccess.Write, BUFFER_SIZE, false);
return _stream;
}
internal void SendMessage(string message)
{
if (_stream == null) return;
var encoder = new ASCIIEncoding();
byte[] messageBuffer = encoder.GetBytes(string.Format("{0}\r\n", message));
_stream.Write(messageBuffer, 0, messageBuffer.Length);
_stream.Flush();
}
}
在 .NET Core 6 中,与标准句柄(例如 STD_ERROR_HANDLE)相关的行为已更改。具体来说,在 .NET Core 中,控制台应用程序默认没有与之关联的控制台窗口。结果,
GetStdHandle
返回无效句柄。
您可以使用
Console.OpenStandardError()
获取表示标准错误句柄的流。
class TestLogHandle
{
private FileStream _stream;
private const int BUFFER_SIZE = 4096;
internal FileStream Init()
{
_stream = Console.OpenStandardError();
if (_stream == null)
{
throw new Exception("Error opening standard error.");
}
return _stream;
}
internal void SendMessage(string message)
{
byte[] bytes = Encoding.UTF8.GetBytes(message);
_stream.Write(bytes, 0, bytes.Length);
}
}