3

我有一个超级简单的程序。我的意图是将标准输入复制到标准输出。这是源代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LALA
{
    class LALA
    {
        static void Main()
        {
            int bufferSize = 40;
            Console.OpenStandardInput().CopyTo(Console.OpenStandardOutput(), bufferSize);
        }
    }
}

如果我设置bufferSize为 40,那么对于任何输入,我只会看到“C on”、“C on s”或“C onsol e”等。如果我设置bufferSize为 41,那么一切都很好。

我的问题是我做错了什么,并且意外高于 41 的值有效吗?

为了澄清,这是我输入 asd 时的输出:

c:\some_path>ConsoleApplication2.exe
asd
C o n
4

1 回答 1

1

惊喜!这似乎是内部缓冲区溢出,我将分享我的发现,尽管它还不是一个真正的证据,它对我来说是有意义的。

Stream类的方法代码为CopyTo:(带反射器)

public void CopyTo(Stream destination, int bufferSize)
{
    //bunch of non relevant validations...

    this.InternalCopyTo(destination, bufferSize);
}

现在的代码InternalCopyTo是:

private void InternalCopyTo(Stream destination, int bufferSize)
{
    byte[] array = new byte[bufferSize];
    int count;
    while ((count = this.Read(array, 0, array.Length)) != 0)
    {
        destination.Write(array, 0, count);
    }
}

控制台流实例的类型__ConsoleStream(System.IO 中的密封内部类)及其Read方法代码:

public override int Read([In] [Out] byte[] buffer, int offset, int count)
{
    //bunch of non relevant validations...

    int errorCode = 0;
    int num = __ConsoleStream.ReadFileNative(this._handle, buffer, offset, count, 0, out errorCode);
    if (num == -1)
    {
        __Error.WinIOError(errorCode, string.Empty);
    }
    return num;
}

最后ReadFileNative是 __ConsoleStream 的代码:

private unsafe static int ReadFileNative(SafeFileHandle hFile, byte[] bytes, int offset, int count, int mustBeZero, out int errorCode)
{
    if (bytes.Length - offset < count)
    {
        throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition"));
    }
    if (bytes.Length == 0)
    {
        errorCode = 0;
        return 0;
    }
    __ConsoleStream.WaitForAvailableConsoleInput(hFile);
    int result;
    int num;
    fixed (byte* ptr = bytes)
    {
        num = __ConsoleStream.ReadFile(hFile, ptr + (IntPtr)offset / 1, count, out result, Win32Native.NULL);
    }
    if (num != 0)
    {
        errorCode = 0;
        return result;
    }
    errorCode = Marshal.GetLastWin32Error();
    if (errorCode == 109)
    {
        return 0;
    }
    return -1;
}

ReadFile方法是低级调用:

[DllImport("kernel32.dll", SetLastError = true)]
private unsafe static extern int ReadFile(SafeFileHandle handle, byte* bytes, int numBytesToRead, out int numBytesRead, IntPtr mustBeZero);

我在这一点上的假设是,在幕后,40 个字节被“保留”到内部数据的某个地方,所以如果缓冲区不超过这个,您将看到保留的数据,在这种情况下,控制台应用程序是进程名称。

当我有更多时间并尝试重现时,我将继续调查此问题,这种情况非常特殊,因为两个流都指向同一个“文件”,因此您可以在阅读时写入它。

于 2012-12-23T22:54:58.127 回答