c# - ¿Puede un CryptoStream dejar la base abierta?
objectdisposedexception (5)
Creo un MemoryStream
, lo paso a CryptoStream
para escribir. Quiero que CryptoStream
, y deje el MemoryStream
abierto para que luego lo lea en otra cosa. Pero tan pronto como se CryptoStream
, también se elimina el MemoryStream
.
¿Puede CryptoStream
dejar abierto el MemoryStream
base de alguna manera?
using (MemoryStream scratch = new MemoryStream())
{
using (AesManaged aes = new AesManaged())
{
// <snip>
// Set some aes parameters, including Key, IV, etc.
// </snip>
ICryptoTransform encryptor = aes.CreateEncryptor();
using (CryptoStream myCryptoStream = new CryptoStream(scratch, encryptor, CryptoStreamMode.Write))
{
myCryptoStream.Write(someByteArray, 0, someByteArray.Length);
}
}
// Here, I''m still within the MemoryStream block, so I expect
// MemoryStream to still be usable.
scratch.Position = 0; // Throws ObjectDisposedException
byte[] scratchBytes = new byte[scratch.Length];
scratch.Read(scratchBytes,0,scratchBytes.Length);
return Convert.ToBase64String(scratchBytes);
}
A partir de .NET 4.7.2, hay un segundo constructor con un parámetro bool agregado llamado leaveOpen
. Si se establece en verdadero, el método de disposición de CryptoStream
no llamará a disposición en la secuencia subyacente.
Además, el otro constructor sin el parámetro leaveOpen
simplemente reenvía los parámetros al nuevo constructor con leaveOpen
configurado en false
.
Como segunda solución, puede crear un objeto WrapperStream que simplemente pase todas las llamadas a lo largo, excepto Dispose / Close. Haga un envoltorio alrededor de su flujo de memoria, entregue el envoltorio al flujo criptográfico, y ahora, al cerrar el flujo criptográfico no toque el flujo de memoria.
Mi solución simple:
class NotClosingCryptoStream : CryptoStream
{
public NotClosingCryptoStream( Stream stream, ICryptoTransform transform, CryptoStreamMode mode )
: base( stream, transform, mode )
{
}
protected override void Dispose( bool disposing )
{
if( !HasFlushedFinalBlock )
FlushFinalBlock();
base.Dispose( false );
}
}
Puede, pero no podrá utilizar declaraciones utilizando. Deberá administrar manualmente la eliminación del objeto y también deberá llamar a FlushFinialBlock()
para asegurarse de que todos los datos se escribieron en la secuencia subyacente antes de trabajar en él.
Una vez que haya terminado de trabajar con la transmisión, puede disponer de todos los recursos que estaba esperando en el bloque final al final.
MemoryStream scratch = null;
AesManaged aes = null;
CryptoStream myCryptoStream = null;
try
{
scratch = new MemoryStream();
aes = new AesManaged();
// <snip>
// Set some aes parameters, including Key, IV, etc.
// </snip>
ICryptoTransform encryptor = aes.CreateEncryptor();
myCryptoStream = new CryptoStream(scratch, encryptor, CryptoStreamMode.Write);
myCryptoStream.Write(someByteArray, 0, someByteArray.Length);
//Flush the data out so it is fully written to the underlying stream.
myCryptoStream.FlushFinalBlock();
scratch.Position = 0;
byte[] scratchBytes = new byte[scratch.Length];
scratch.Read(scratchBytes,0,scratchBytes.Length);
return Convert.ToBase64String(scratchBytes);
}
finally
{
//Dispose all of the disposeable objects we created in reverse order.
if(myCryptoStream != null)
myCryptoStream.Dispose();
if(aes != null)
aes.Dispose();
if(scratch != null)
scratch.Dispose();
}
Resulta que no hay necesidad de separar el bloque de uso {} en try {} finalmente {} ... En última instancia, solo tienes que usar FlushFinalBlock () dentro de la declaración de uso, y anidar cualquier otra cosa dentro como necesario.
using (MemoryStream scratch = new MemoryStream())
{
using (AesManaged aes = new AesManaged())
{
// <snip>
// Set some aes parameters, including Key, IV, etc.
// </snip>
ICryptoTransform encryptor = aes.CreateEncryptor();
using (CryptoStream myCryptoStream = new CryptoStream(scratch, encryptor, CryptoStreamMode.Write))
{
myCryptoStream.Write(someByteArray, 0, someByteArray.Length);
myCryptoStream.FlushFinalBlock();
scratch.Flush(); // not sure if this is necessary
byte[] scratchBytes = scratch.ToArray();
return Convert.ToBase64String(scratchBytes);
}
}
}