2012-03-07 19 views
21

Por lo tanto, el código siguiente solía funcionar en .NET 4 para obtener un objeto System.Net.Mail.MailMessage como un MemoryStream, sin embargo, con el lanzamiento de .NET 4.5 beta se produce una excepción de tiempo de ejecución.Obtención de System.Net.Mail.MailMessage como un MemoryStream en .NET 4.5 beta

Assembly assembly = typeof(SmtpClient).Assembly; 
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); 
using (MemoryStream stream = new MemoryStream()) 
{ 
    ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null); 
    object mailWriter = mailWriterContructor.Invoke(new object[] { stream }); 
    MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic); 
    sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null); 

    ..... 
} 

La excepción de tiempo de ejecución se produce en sendMethod.Invoke().

+0

Cuál es la excepción (seguimiento de la pila puede ayudar)? –

+0

La excepción fue esto: System.Reflection.TargetParameterCountException: desajuste en el conteo de parámetros. – dimoss

+0

¿Ha comparado el código de .NET 4 con .NET 4.5 para ver si han dejado caer una sobrecarga de 'Enviar'? Sin duda, esto podría manejarse más simplemente con el uso de '' dinámico ''. –

Respuesta

29

Se las arregló para averiguar cómo hacer que esto vuelva a funcionar en .NET 4.5 beta. El método privado API Send() en MailMessage ha cambiado a: interno void Enviar (BaseWriter escritor, bool sendEnvelope, bool allowUnicode)

Encuentra el código actualizado a continuación.

Assembly assembly = typeof(SmtpClient).Assembly; 
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); 
using (MemoryStream stream = new MemoryStream()) 
{ 
    ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null); 
    object mailWriter = mailWriterContructor.Invoke(new object[] { stream }); 
    MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic); 
    sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null); 

    ..... 
} 
+0

Eso es lo que obtienes para acceder a métodos no públicos :) –

+7

Por lo que pude investigar, no hay otra manera de obtener el MemoryStream de un MailMessage. Incluso las publicaciones anteriores aquí usan el método API privado, e incluso los ejemplos de proveedores externos utilizan este método API privado. Si alguien sabe de una manera de hacer esto usando APIs públicas sería una gran ayuda compartirlo :) – dimoss

+0

Hola, tengo el mismo problema pero para closeMethod.Invoke (mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new objeto [] {}, nulo); ... alguna pista? Gracias – VAAA

0

La solución propuesta con el TRUE extra funciona muy bien.

Comencé a obtener el error al ejecutar mi proyecto en VS2012 aunque no estoy usando .net 4.5 pero 4.0 en todas mis bibliotecas.

El error solo ocurre en la máquina donde ha instalado VS2012, parece que VS2012 hace referencia a .NET 4.5 mientras está depurando. Cuando implementa y ejecuta la aplicación en clientes que ejecutan .net 4.0 todo funciona bien.

Por lo tanto: Si ejecuta 4.0 - no agregue el valor TRUE extra, si ejecuta 4.5 agréguelo.

+0

Sí, me encontré con esto también. Tuvimos que poner los condicionales del compilador para que podamos depurar el código y hacer que funcione en producción. – efbenson

12

Esto podría ser útil si no quieres ir con hacks no soportados y no te molesta el rendimiento extra.

public static class MailMessageExtensions 
    { 
    public static string RawMessage(this MailMessage m) 
     { 
     var smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory }; 

     using (var tempDir = new TemporaryDirectory()) 
      { 
      smtpClient.PickupDirectoryLocation = tempDir.DirectoryPath; 
      smtpClient.Send(m); 
      var emlFile = Directory.GetFiles(smtpClient.PickupDirectoryLocation).FirstOrDefault(); 
      if (emlFile != null) 
       { 
       return File.ReadAllText(emlFile); 
       } 
      else 
       return null; 
      } 
     return null; 
     } 

    } 

class TemporaryDirectory : IDisposable 
    { 
    public TemporaryDirectory() 
     { 
     DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); 
     Directory.CreateDirectory(DirectoryPath); 
     } 

    public string DirectoryPath { get; private set; } 

    public void Dispose() 
     { 
     if (Directory.Exists(DirectoryPath)) 
      Directory.Delete(DirectoryPath, true); 
     } 
    } 
+0

Buena solución, y es probable que funcione a través de otras versiones de API. ¡Gracias! – antlersoft

9

para comprobar si booleano adicional que utilizo:

If _sendMethod.GetParameters.Length = 2 Then 
    _sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True}, Nothing) 
Else 
    _sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True, True}, Nothing) 
End If 
+1

Esto funcionó para mí con .NET 4.0 y 4.5. ¡Gracias! – LuckyStrike

Cuestiones relacionadas