Pasar parámetros de una aplicación una que esta en ejecución

Después de este titulo tan largo esta un problema que se me planteo la semana pasada y era la necesidad de pasar información a una aplicación WPF que se encontraba en ejecución desde otra aplicación, en este caso eran los parámetros de llamada.

Lo primero que se me vino a la cabeza era realizarlo a través de WCF teniendo un host en la aplicación que esperaba la información y utilizando el binding NetNamedPipeBinding, pero pensaba que se podía realizar de diferente manera.Después de unas cuantas horas pensando, navegando… vi la luz.

Lo primero vamos a definir una clase en este caso ApplicationParametersProxy, lo importante es que esta clase debe de heredar de MarshalByRefObject, si recordamos, esta clase según viene en la MSDN

“MarshalByRefObject es la clase base de los objetos que se comunican a través de los límites de los dominios de aplicación que utilizan un proxy para intercambiar mensajes. Los objetos que no heredan de MarshalByRefObject tienen implícitamente valor de resolución. Cuando una aplicación remota hace referencia a un objeto con valor de resolución, se pasa una copia del objeto a través de los límites del dominio de la aplicación.

Es posible obtener acceso a los objetos MarshalByRefObject directamente dentro de los límites del dominio local de la aplicación. La primera vez que una aplicación en un dominio de aplicación remoto obtiene acceso a un MarshalByRefObject, se pasa un proxy a la aplicación remota. El cálculo de referencias de las siguientes llamadas que se realizan en el proxy, volverá a tener lugar en el objeto que reside en el dominio de la aplicación local.”

Esta clase nos va a permitir comunicarnos entre diferentes dominios que es nuestro caso

No debemos de olvidarnos dar a esta clase permisos de FullTrust a través del atributo

 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]

A esta clase le añadimos la propiedad estática

public static bool ExistsApplication{ get; internal set; }

Esta propiedad indicara si ya existe una aplicación que ha registrado la clase. Añadimos otra propiedad en la que almacenaremos los argumentos

  public static string[] CommandLineArgs { get; internal set; }

Una vez definida esta clase, lo siguiente que haremos será definir una clase que herede de EventArgs para definir los argumentos que vamos a necesitar para lanzar un evento que mas adelante veremos.

1 public class InstanceCallbackEventArgs : EventArgs 2 { 3 4 internal InstanceCallbackEventArgs(bool existsApplication, string[] commandLineArgs) 5 { 6 ExistsApplication = existsApplication, ; 7 CommandLineArgs = commandLineArgs; 8 } 9 10 11 public bool ExistsApplication { get; private set; } 12 13 14 public string[] CommandLineArgs { get; private set; } 15 }

El siguiente paso es definir la clase donde vamos a controlar si la aplicación esta lanzada y pasarle los parámetros desde la segunda aplicación. Esta clase deberá ser estática y con un método que nos permitirá crear una única instancia utilizando la clase ApplicationParametersProxy.

 

1 public static bool CreateSingleInstance(string name, EventHandler<InstanceCallbackEventArgs> callback) 2 { 3 EventWaitHandle eventWaitHandle = null; 4 string eventName = string.Format("{0}-{1}", Environment.MachineName, name); 5 6 ApplicationParametersProxy.IsFirstInstance = false; 7 ApplicationParametersProxy .CommandLineArgs = Environment.GetCommandLineArgs(); 8 9 try 10 { 11 // intentamos abrir el evento para ver si existe 12 // si no existe excepción 13 eventWaitHandle = EventWaitHandle.OpenExisting(eventName); 14 } 15 catch 16 { 17 // si no existe es la primera vez que se va a crear 18 ApplicationParametersProxy.IsFirstInstance = true; 19 } 20 21 if (ApplicationParametersProxy.IsFirstInstance) 22 { 23 // iniciamos el handle 24 eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); 25 26 // Lo registramos 27 ThreadPool.RegisterWaitForSingleObject(eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false); 28 eventWaitHandle.Close(); 29 30 // Registramos el tipo que vamos a compartir 31 RegisterRemoteType(name); 32 } 33 else 34 { 35 // si ya existe una instancia le pasamos los parametro 36 UpdateRemoteObject(name); 37 38 39 if (eventWaitHandle != null) eventWaitHandle.Set(); 40 41 42 // matamos este proceso 43 Environment.Exit(0); 44 } 45 46 return InstanceProxy.IsFirstInstance; 47 }

 

El método OpenExisting intenta abrir un evento del sistema con nombre existente. Si el evento del sistema no existe, este método produce una excepción en lugar de crear el evento del sistema, lo que nos indica que es la primera instancia y debemos registrarlo, Para ello utilizaremos el método RegisterRemoteType.

 

1 private static void RegisterRemoteType(string uri) 2 { 3 // registra el canal remoto (net-pipes) 4 var serverChannel = new IpcServerChannel(Environment.MachineName + uri); 5 ChannelServices.RegisterChannel(serverChannel, true); 6 7 // registra el tipo compartido 8 RemotingConfiguration.RegisterWellKnownServiceType( 9 typeof(ApplicationParametersProxy), uri, WellKnownObjectMode.Singleton); 10 11 // cierra el canal cuando el proceso muera 12 Process process = Process.GetCurrentProcess(); 13 process.Exited += delegate { ChannelServices.UnregisterChannel(serverChannel); }; 14 }

Si es la segunda instancia el método UpdateRemoteObject se encargara de pasar los parámetros de la segunda instancia a la primera.

 

1 private static void UpdateRemoteObject(string uri) 2 { 3 // registramos el canal net-pipe c 4 var clientChannel = new IpcClientChannel(); 5 ChannelServices.RegisterChannel(clientChannel, true); 6 7 // cogemos el objeto compartido por el otro proceos 8 var proxy = 9 Activator.GetObject(typeof(ApplicationParametersProxy), 10 string.Format("ipc://{0}{1}/{1}", Environment.MachineName, uri)) as ApplicationParametersProxy; 11 12 // pasamos los parametroe 13 if (proxy != null) 14 proxy.SetCommandLineArgs(ApplicationParametersProxy.IsFirstInstance, ApplicationParametersProxy.CommandLineArgs); 15 16 // cerramos el canal 17 ChannelServices.UnregisterChannel(clientChannel); 18 } 19 20

 

Por ultimo el método WaitOrTimerCallback invocara el evento en el primer proceso para indicarle que ya se han pasado los argumentos.

 

1 private static void WaitOrTimerCallback(object state, bool timedOut) 2 { 3 4 var callback = state as EventHandler<InstanceCallbackEventArgs>; 5 if (callback == null) return; 6 7 // lanzamos el evento en el otro proceso 8 callback(state, 9 new InstanceCallbackEventArgsApplicationParametersProxy.IsFirstInstance, 10 ApplicationParametersProxy.CommandLineArgs)); 11 }

Espero que os sirva como a mi.

 

     

Un comentario en “Pasar parámetros de una aplicación una que esta en ejecución”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *