为什么跨越AppDomains从SslStream读取成功但返回空缓冲区?

时间:2012-06-20 06:25:17

标签: c# attributes marshalling appdomain sslstream

使用Read(byte[] buffer, int offset, int count)方法从SslStream正常阅读时,我会得到预期的结果。

但是,如果我将SslStream对象移动到新的AppDomain中,则读取仍然可以正常运行(即返回正确读取的字节数),但是buffer数组是空的。

为什么会这样?

2 个答案:

答案 0 :(得分:6)

some investigation之后,似乎参数数组的内容未在AppDomains之间进行编组(可能是出于性能原因)。

因此,buffer参数中的数据仅以单向传递。本地AppDomain中的调用者不会看到对远程AppDomain中的数组的修改。

强制要返回的数组参数中的数据的方法是将[Out]属性添加到参数中。

要解决问题中陈述的问题,请为SslStream创建一个包装类,然后使用它:

[Serializable]
internal class SslStreamWrapper : SslStream
{
    public SslStreamWrapper(
        Stream innerStream,
        Boolean leaveInnerStreamOpen,
        RemoteCertificateValidationCallback validationCallback,
        LocalCertificateSelectionCallback selectionCallback)
     : base(innerStream, leaveInnerStreamOpen, validationCallback, selectionCallback)
    {
    }

    // Add the [Out] attribute to the 'buffer' parameter.
    public override Int32 Read([In, Out] Byte[] buffer, Int32 offset, Int32 count)
    {
        return base.Read(buffer, offset, count);
    }
}

该类具有[Serializable]属性,允许它在AppDomains之间传递,并且包含隐式[In]参数以与其他Stream类保持一致。

继承自Stream的许多其他.NET类(例如MemoryStreamBufferedStream - 甚至Stream本身)包含[In, Out] {的buffer属性Read()方法中的{1}}参数。

我想知道在SslStream中省略它们是否是故意的选择...这适用于所有版本的.NET。

答案 1 :(得分:0)

这是@JonSkeet在对已接受答案的评论中建议的使用合成代替继承的替代解决方案的代码:

[Serializable]
public class CrossAppDomainStreamWrapper : Stream
{
    public CrossAppDomainStreamWrapper(Stream stream) => Stream = stream;

    public Stream Stream { get; }
    public override bool CanRead => Stream.CanRead;
    public override bool CanSeek => Stream.CanSeek;
    public override bool CanWrite => Stream.CanWrite;
    public override long Length => Stream.Length;
    public override long Position
    {
        get => Stream.Position;
        set => Stream.Position = value;
    }

    public override void Flush() => 
       Stream.Flush();
    public override long Seek(long offset, SeekOrigin origin) =>
        Stream.Seek(offset, origin);
    public override void SetLength(long value) =>
       Stream.SetLength(value);
    public override int Read([In, Out] byte[] buffer, int offset, int count) =>
       Stream.Read(buffer, offset, count);
    public override void Write(byte[] buffer, int offset, int count) =>
       Stream.Write(buffer, offset, count);
}