jueves, 26 de junio de 2008

"El Truco del Almendruco" en la depuración con "vsjitdebugger.exe"

En más de una ocasión hemos tenido que depurar de una forma un tanto compleja, o al menos no de la forma habitual, es decir ni depurando directamente desde Visual Studio ni atachando procesos, ¡seguro que si!  Más concretamente, y para el caso que nos ocupa pondré un para de ejemplos muy prácticos.

Durante estas semanas junto con un compañero de trabajo hemos estado realizando Setups "más o menos complejos" y, bueno, así es como uno se da cuenta de los problemas y de como surgen las necesidades, jejeje...

Durante estos Setups las "Customs Acctions", clases específicas para el tratamiento de acciones durante la instalación/desinstalación, surge la necesidad de depurarlas, y la pregunta es ¿cómo?. Alguno dirá; pues con MessageBox.Show(....), Console.WriteLine(...), etc.. si este mecanismo siempre funciona, pero es bastante más tedioso.

Pues nada de eso, a partir de ahora ¡y claro, para aquel que no conozca este truquito!, será mucho más fácil.

Pasos:

Debería quedar así:

Esta configuración hará que nuestro depurador; "Visual Studio Just-In-Time Debugger" se inicie cada vez que se ejecute el comando Msiexec.exe

Si ahora sobre nuestro proyecto de Setup de Visual Studio, hacemos "click" con el botón derecho de ratón y pulsamos "Install",  "Tachaaaaannnnnn" ¡a depurar!, claro, previamente estableceremos los puntos de ruptura (Break Points) adecuados, :-D.

De la misma forma, si queremos instalar un Servicio Windows en la consola de servicios y utilizamos "InstallUtil" repetiremos los pasos sustituyendo MsiExec.exe por "InstallUtil.exe".

Una vez que hayamos finalizado la depuración, Modificar la key "MsiExed.exe" para que no se ejecute siempre, de manera que quede algo como; " _***_MsiExec.exe". De forma similar para InstallUtil.exe, por ejemplo _***_InstallUtil.exe.

Tambíen podríamos sustituir  nuestro depurador "vsjitdebugger.exe" por cualquier otro disponible en nuestra máquina.

 

Por supuesto, gracias a Javier,(si, el de las cervecitas, jejeje...) por tal descubrimiento.

 

¡Buscando la fácil senda en el desarrollo!
Juanlu

Etiquetas: , ,


martes, 24 de junio de 2008

WCF & MSMQ

Hace unas semanas hablaba con un compañero acerca de como implementar una aplicación con acceso  a MSMQ y tras una larga discusión, en un intento de conseguir pensar en el mejor camino de lograrlo, pensé en hacerlo con WCF, y,  ¿Que mejor forma de probarlo que haciendo un ejemplo o proyecto? , pues, e aquí el motivo de este post.

Os dejo el conjunto de pasos seguidos:

Pasos:

[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay
= true, Action = "*")]
void Send(MsmqMessage<Persona> msg);
}




public class Service1 : IService1
{
[OperationBehavior(TransactionScopeRequired
= true, TransactionAutoComplete = true)]
public void Send(MsmqMessage<Persona> message)
{
Persona msg
= (Persona)message.Body;
Console.WriteLine(
"Persona: [{0} {1}; ({2})] ", msg.Nombre, msg.Apellidos, msg.OtraCosaMariposa);
}
}




static void Main(string[] args)
{
string msmqPath = ConfigurationManager.AppSettings["baseAddress"];

if (!MessageQueue.Exists(msmqPath))
MessageQueue.Create(msmqPath);

Uri baseAddress
= new Uri(String.Format("msmq.formatname:DIRECT=OS:{0}", msmqPath));

using (ServiceHost serviceHost = new ServiceHost(typeof(Service1), baseAddress))
{
serviceHost.Authorization.PrincipalPermissionMode
= System.ServiceModel.Description.PrincipalPermissionMode.None;

serviceHost.Open();

Console.WriteLine(
"Servicio listo.");
Console.WriteLine(
"Pulsa <INTRO> para finalizar.");
Console.ReadLine();

serviceHost.Close();
}
}




<appSettings>
<add key="baseAddress" value=".\private$\MyTest33"/>
</appSettings>

<system.serviceModel>
<bindings>
<msmqIntegrationBinding>
<binding name="NewBinding0" exactlyOnce="false" useSourceJournal="false"
useMsmqTracing
="true">
<security mode="None">
<transport msmqAuthenticationMode="None" />
</security>
</binding>
</msmqIntegrationBinding>
</bindings>
<services>
<service name="WcfMsmqHost.Service1">
<endpoint address="" binding="msmqIntegrationBinding" bindingConfiguration="NewBinding0"
contract
="WcfMsmqIntegration.IService1" />
</service>
</services>
</system.serviceModel>



string msmqPath = ConfigurationManager.AppSettings["baseAddress"];

MsmqIntegrationBinding binding
= new MsmqIntegrationBinding();
EndpointAddress address
= new EndpointAddress(
String.Format(
@"msmq.formatname:DIRECT=OS:{0}", msmqPath));
ChannelFactory
<IService1> channelFactory = new ChannelFactory<IService1>(binding, address);

// None, porque MQM no esta en Active Directory
binding.Security.Mode = MsmqIntegrationSecurityMode.None;

IService1 channel
= channelFactory.CreateChannel();

Persona msg
= new Persona();
msg.Nombre
= "Juan Luis";
msg.Apellidos
= "Guerrero";
msg.OtraCosaMariposa
= "elGuerre";

MsmqMessage
<Persona> message = new MsmqMessage<Persona>(msg);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
channel.Send(message);
scope.Complete();
}


<configuration>
<appSettings>
<add key="baseAddress" value=".\private$\MyTest33"/>
</appSettings>
</configuration>

No olvidar incluir los espacios de nombre:



using System.ServiceModel;
using System.Messaging;
using System.ServiceModel.MsmqIntegration;


Y ni que decir tiene que la clase Persona (Persona.cs) contiene  tres propiedades públicas sin más; Nombre, Apellidos y OtraCosaMariposa, incluso, no será necesario establecer el atributo DataContract.


 


Por otro lado y durante la configuración del ejemplo nos podemos encontrar con alguno errores y que detallo a continuación:


Caso 1:


serviceHost.Open: Binding validation failed because the binding's ExactlyOnce property is set to true while the destination queue is non-transactional. The service host cannot be opened. Resolve this conflict by setting the ExactlyOnce property to false or creating a transactional queue for this binding.


Este problema es debido a la propiedad "exactlyOnce" tiene el valor "true" y la cola no es transaccional para solucionarlo:



  1. Si la cola es transaccional entonces asignarle el valor "true" a esta propiedad.
  2. si la cola NO es transaccional asignarle el valor "false".

Caso 2:


Binding validation failed because the binding's MsmqAuthenticationMode property is set to WindowsDomain but MSMQ is installed with Active Directory integration disabled. The channel factory or service host cannot be opened.


En este caso la solución es evitar que el valor del modo de seguridad del binding sea "WindowsDomain", puesto que la integración de MSMQ con Active Directory no ha sido instalada. Bastará, y si estamos en un dominio, con instalar esta integración desde los componentes de windows. Luego para nuestro caso, tanto cliente como servidor/host deberán establecer algunos valores:


Cliente en modo programático:



binding.Security.Mode = MsmqIntegrationSecurityMode.None;


Servidor mediante fichero de configuración:


<endpoint address="" binding="msmqIntegrationBinding"
bindingConfiguration
="NewBinding0" contract="WcfMsmqIntegration.IService1" />

El inconveniente de utilizar transacciones, si es que es un inconveniente, es el MSTC (Microsoft Transsaction Coordinator), que nos asegura la transaccionalidad de la operación de forma automática, pero que sin embargo, y dependiendo del entorno, es decir de la distribución entre servidores, podría resultar muy pesado e incluso no deseable. En caso de no hacer uso de la transaccionalidad, bastará con invocar de nuevo a nuestro "Sender" (envío de mensajes a MSMQ con WCF) desde el propio servidor/host y establecer un tratamiento adecuado y con esto estaría solucionado.


Finalmente y por ahora, he decir, que la ventaja de utilizar WCF para el tratamiento de colas MSMQ radica principalmente en la posibilidad de cambiar los bindings sin problema alguno, la trazabilidad totalmente automática, la seguridad de que todo va a funcionar a la primera, :-D


"Lo malo" puede ser que nos encontremos con que ya tenemos un sistema de colas MSMQ en un entorno de producción y que no podamos crear ningún servicio WCF, en tal caso, siempre podremos optar por crear un cliente WCF (ClientBase<T>), incluso también optar por el caso contrario, un servidor WCF y un cliente no WCF (System.Messaging).


http://www.cogin.com/mq/download.php


Un nuevo camino para el tratamiento de colas MSMQ.


saludos
Juanlu

Etiquetas: , ,


This page is powered by Blogger. Isn't yours?