与 Visual Studio 2022 中的 .NET Framework 相比,如何获取 .NET Core 6 中错误输出的窗口句柄

How to get a window handle for error output in .NET Core 6 compared to .NET Framework in Visual Studio 2022

提问人:Ninotter 提问时间:11/14/2023 更新时间:11/14/2023 访问量:45

问:

对于上下文:

我负责将服务从 .NET Framework 4.7.2 升级到 .NET Core 6。此服务由启动其他一些服务的主服务调用。这个主服务是所有这些子服务的错误句柄输出,我的服务也不例外。若要获取此输出窗口的句柄,以从我的服务写入日志,则使用。Win32::GetStdHandle

这在 .NET Framework 中完美运行:在 Visual Studio 2022 中启动应用程序进行调试时,所有日志记录信息都会转到窗口,当使用主服务启动时,它会记录到主服务控制台窗口。Output

问题来了:在 .NET Core 6 中,在完全相同的条件下,调用返回 0,即无效句柄。Win32::GetStdHandle

如何让 GetStdHandle 调用按预期工作,在 .NET Core 6 中返回正确的句柄,或获取启动服务的进程STD_ERROR_HANDLE的任何解决方法?

.NET Framework 4.7.2 或 .NET Core 6 中的可重现示例:

(注意:将输出到控制台应用程序中的控制台,并将输出到 Windows 应用程序中的 if 并在 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();
    }
}
C# NET .net-core pinvoke

评论

1赞 Simon Mourier 11/14/2023
我不复制。适用于 .NET 6、7、x86、x64、cmd、终端、Visual Studio 等。你应该提供一个完全可编译的项目(发布在github、互联网等上)。
0赞 Ninotter 11/14/2023
@SimonMourier 很抱歉,在控制台应用程序的情况下,您是完全正确的,但在使用 Visual Studio 时,在 Windows 应用程序中则不然(STD_ERROR_HANDLE不再是“输出”窗口)。我应该澄清一下,我正在调查为什么在 Visual Studio 中使用 .NET Core 时此行为会有所不同。
1赞 Simon Mourier 11/15/2023
GetStdHandle(STD_ERROR_HANDLE) => 0 只是意味着没有定义任何可以写入的内容,它既不不正确也不出乎意料。例如,只需调用 AllocConsole (learn.microsoft.com/en-us/windows/console/allocconsole),即可获得要输出到的控制台。
0赞 Ninotter 11/15/2023
@SimonMourier 巧合的是,这正是我:)寻找的东西。谢谢,与 .NET Framework 相比,我猜 Visual Studio 不会在 .NET Core 中将输出窗口分配为 STD_ERROR_HANDLE。

答:

1赞 Ale_Bianco 11/14/2023 #1

在 .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);
    }
}

评论

0赞 Ninotter 11/15/2023
谢谢你的信息,我不知道。我不确定这有多准确,因为在使用 PInvoke 时,我可以设法在控制台应用程序中输出到控制台,但不能在 Windows 应用程序中输出(其中日志记录是在 Visual Studio 输出中完成的)。无论如何,你都是救命恩人!
0赞 Simon Mourier 11/15/2023
“在 .NET Core 中,控制台应用程序默认情况下没有与之关联的控制台窗口”不是 true。Windows 应用程序也是如此,顺便说一句,默认情况下,OpenStandardError 会给你一个 nullstream,在道德上等同于 null STD_ERROR_HANDLE句柄。