Instalando .Net Framework 3.0 Beta2 (WinFx)

A alguna gente, como yo, le ha resultado problemática la instalación del WinFx.


El problema:


Si descargas el runtime http://www.microsoft.com/downloads/details.aspx?FamilyId=4A96661C-05FD-430C-BB52-2BA86F02F595&displaylang=en desde http://msdn.microsoft.com/windowsvista/downloads/products/getthebeta/


Es probable, tal como leí en algún foro de MSDN, que instalandolo recibas a los 22MB de 32MB un error irrecuperable. Mirando lo que va en el error se puede leer:


EventType : wap10setup     P1 : 13165     P2 : 3.0.03906.22_waprc0_x86_sfx    
P3 : gen     P4 : inst     P5 : f     P6 : dlmgr_verificationerror     P7 : 5008
P8 : 5008     P9 : type_badcertificate     P10 : –


En la CPT de Junio es a los 26MB 😉


Anteriormenente yo había instalado el WFC de la página de MSDN subscriber, pero lo había eliminado con la herramienta de desinstalación especial de WinFx. Puede que sea un efecto colateral de eso.


La solución:


Madhu Ponduru (MSFT, MCSD.NET) indica que puedes descargarte TODO el WinFX sin utilizar «El servicio de descarga inteligente de segundo plano».


Los enlaces son…


 http://download.microsoft.com/download/1/A/6/1A6A4DE6-ADBA-448B-BFE1-A6E6094D734A/winfxrc.exe      (Beta 2)


http://download.microsoft.com/download/1/D/A/1DA1A5D6-4DF6-45AA-9C2F-E87EFF6F6F78/dotnetfx3.exe (CPT Junio2006)


Una vez instalado ya solo os queda meter el Windows SDK, el «Orcas» .Net Framework development tools, y si os animais las extensiones de Windows Workflow Foundation para Visual Studio 2005


Si teneis problemas instalando los componentes de «orcas» para VS2005, porque no os detecta que habeis instalado el WinFX. Podeis probar a instalarlo con:


msiexec /i vsextwfx.msi WRC_INSTALLED_OVERRIDE=1


 

Sobre la independencia del Gestor de Base de Datos (SGBD) en nuestros proyectos

Seguro que muchas veces habeis oído que la persistencia de nuestros objetos del modelo debería ser totalmente independiente del Sistema Gestor de Base de Datos (SGBD). En este post se discuten las implicaciones de lograr la «total independencia» frente a otros enfoques como lograr una «baja dependencia». Es una discusión abierta, así que a ver si os animais y aportais vuestra opinión, ideas y experiencias.


Conozco una amplia corriente de mi antigua Facultad de Informática que defiende a muerte la independencia total del SGBD en una aplicación con acceso a datos.
Llamémosla corriente independiente. El enfoque de dicha corriente es que la lógica de negocio se fundamente sobre todo en el proceso y menos en el dato (process driven design). Primero se definen los procesos y lo de menos es su representación persistente. Es el dato el que se adecua al proceso.


En la mayoría de sistemas de producción que conozco hay un enfoque al dato como elemento central de la lógica de negocio. Primero se trabaja con los datos los datos (model driven design) y luego se construyen los procesos sobre ellos. De esta situación se aprovechan los principales fabricantes de SGBDs que incluyen características propias y únicas en sus productos que obligan al cliente depender de él y complica la migración a otros productos de la competencia.


Ambos enfoques tienen pros y contras.


Ser totalmente independiente del SGBD no es una característica a infravalorar, pero suele implicar un esfuerzo mayor de desarrolo o la necesidad de definir un nuevo nivel de indirección. La consulta de datos del modelo persistente, fuera de los procesos de la aplicación, puede ser realmente complicada y oscura. Tareas aparentemente sencillas, como generar claves subrogadas, puede ser tediosa y complicada. 
El segundo enfoque suele ser más productivo al integrarse fácilmente con el entorno de desarrollo y se disponen de ayudas y características no disponibles en el enfoque independiente. Por contra, se depende totalmente del producto de un fabricante y si en el futuro surge un SGBD mejor, la migración no suele ser viable por su complejidad.


Mi opinión es bastante práctica y gallega: «Ni una cosa ni la otra».


El esfuerzo de desarrollo de una aplicación para poder hacer migraciones de SGBD totalmente automáticas no suele compensar. Es mejor buscar un bajo acoplamiento con el SGBD encaminado a minimizar las operaciones manuales de migración. Reducireis los tiempos de desarrollo al no buscar a toda costa la independencia total y el tiempo de migración a otro gestor gracias al bajo acoplamiento.
Aun así, valorad siempre si el hacer el desarrollo independiente de SGBD va a ser más costoso en tiempo y recursos que las operaciones de migración en el caso de tener que hacer una a los principales SGBD del mercado.


Para aclararnos: si por conseguir independencia total retrasais unos meses más el tiempo de proyecto, lo único que lograis es aumentar costes de cara al cliente que poco va a entender que dentro de 5 años, cuando otro gestor sea más adecuado, pueda cambiar a él. Por desgracia, lo más probable, es que quiera cambiar gran parte de la aplicación porque ya no se adapta a su modelo de negocio e incluso habrán cambiado las tecnologías de desarrollo.
Además, es muy dificil, por no decir imposible,  que aun realizando una aplicación independiente de SGDB, no haya ningún aspecto que os obligue a realizar tareas de migración manuales.


Preguntémonos ¿Cada cuánto cambia de SGBD una organización?
La verdad es que muy poco. De hecho es más probable que cambien antes de sistema operativo que de SGBD. No deja de ser un argumento un poco capcioso y circular porque la corriente independiente defiende que en la variedad está el gusto y si tienes la facilidad de cambiar probablemente lo acabes haciendo. Sin embargo me cuesta pensar que aun teniendo la posibilidad, una organización esté cambiando de SGBD cada año.

Personalmente me parece una pérdida de tiempo, de esfuerzo, de recursos… el buscar la independencia de SGBD a toda costa y defendiendo radicalmente que es un requisito imprescindible, haciendo capas y capas de indirección y archivos de configuración. Sobre todo para proyectos pequeños o medios.


Desde mi punto de vista y hoy por hoy, el camino está en hacer nuestros modelos de datos menos dependientes del gestor, menos acoplados, valorando siempre el tiempo de migración a los principales SGBD. No suele llevar mas de un par de días de trabajo y te ahorrará disgustos en el futuro.


A continuación os doy algunos consejos para lograr bajo acoplamiento:


Consejos para conseguir modelos de datos menos acoplados


Utiliza una herramienta como ErWin o Embarcadero para generar el modelo de datos. Automáticamente pueden generarte las sentencias SQL para crear y modificar un modelo de datos entre distintos gestores. Concretamente con ErWin hemos migrado esquemas entre Informix (7.61), SQL Server 2000 y Oracle (8-10) sin prácticamente ningún problema. Ambos son de pago (y no son baratos), desconozco si existe alguna solución opensource o gratuíta.
Si no dispones de ese tipo de herramientas, mantén siempre un archivo SQL con el esquema de tú modelo, con comentarios etc… de forma que sea muy fácil recrearlo.
No confíes en que un EXPORT del SGBD te lo va a generar completo directamente o cómo tu quieres.


Utiliza tipos de datos que sepas que son compatibles entre los principales SGBD.


No uses procedimientos almacenados. No hay dos lenguajes de SPs iguales. Si tienes que usarlos que sean mínimos e intenta utilizar palabras clave que sepas que existen en otros gestores o tienen mecanismos análogos. Desde luego, implementar la lógica de negocio en ellos es un diseño que pertenece al pasado, además de atarte irremediablemente al SGBD al complicar la migración haciéndola prácticamente inviable. Análogamente no deberías usar ensablados en el SGBD (característica de SQL Server 2005).


Cuidado con la generación de claves subrogadas. En algunos gestores se utilizan secuencias (Oracle) y en otros columnas de identidad con autoincremento (SqlServer, Informix).

En general, no utilices ninguna característica propia del SGBD
. Suelen ser atractivas a la hora de programar pues resuelven algún problema tedioso, pero te ligan al SGBD.

No definas una política complicada de permisos.
No suele ser compatible entre gestores y son de díficil migración. En la mayoría de escenarios basta con un usuario administrador, otro con permiso de escritura y lectura de tuplas (writer) y un último usuario con permisos de solo lectura (reader). Además, usar pocos usuarios beneficia a entornos con concurrencia optimista (p.e. desarrollos web) porque el pool de conexiones puede reutilizarlas al coincidir las cadenas de conexión.


Utilizar un mapeador de objetos relacional. Como por ejemplo Hibernate, (NHibernate para .Net). Te pueden aislar totalmente del SGBD. Te mapean la persistencia de tus objetos del modelo, que deberían ser clases POCO, POJO (Plain older C# Objects, Plain Order Java Objects), incluso con soporte para herencia contra los principales SGBD del mercado. Es decir, te evita hacer la parte más tediosa de los DAOs. Dispone de lenguaje propio de consultas (HQL) parecido a SQL. Soporta generación idependiente de claves subrogadas y gestión de transacciones. En el framework de java es capaz de trasladar esquemas (modelos) entre gestores, haciendo parte del trabajo de herramientas mencionadas en el primer punto. Direis: «Vaya, si es tan fantástico… ¿porqué no lo usa todo el mundo?». Existen varias razones: Una es que no es trivial su uso. Es una tecnología relativamente reciente y se necesita cierto background en SGBD para ponerlo a funcionar y solucionar problemas eficientemente. Si tienes poca experiencia, puedes hacer mapeados aparentemente correctos que son totalmente ineficientes. Añade un nivel de indirección más para el acceso a datos. Para proyectos pequeños o medianos puede ser inviable. Dejo para un post futuro un análisis más profundo de NHibernate con ejemplos.


Una discusión sin final


Se pueden estar horas y días discutiendo los puntos anteriores. Sin embargo solo vosotros, como responsables de proyectos, sois los únicos que podeis valorar cada situación y una solución perfecta para un escenario puede ser catastrófica en otro.


Si vuestro trabajo es ofrecer soluciones a un cliente (suele ser el tipo de trabajo más común para un informático), un último consejo: Buscad siempre la productividad a costes razonables.


Los plazos de un proyecto están para cumplirlos. Si no llegamos porque nos complicamos la vida con una tecnología o enfoque elegante y novedosísimo, al cliente le va a dar igual y te exigirá responsabilidades, sobre todo si las ventajas de aplicar dicha tecnología o enfoque no aporta sustancialmente nada para el cliente.


Otro punto que debeis valorar es la capacidad de aprendizaje y adaptación de vuestro equipo de desarrollladores a nuevas tecnologías. Frameworks como Hibernate requieren un tiempo de aprendizaje previo. Si la mayoría del equipo no lo ha usado o no ha hecho pruebas podeis estar asumiendo un riesgo altísimo.


 


Hoy en día, y sobre todo si desarrollais con VisualStudio y .NET,  SQLServer 2005 es una de las mejores opciones como SGBD. Sobre todo si pensais que oracle es demasiado grande, caro y dificil de administar (instalación de parches, etc.). Actualmente SQLServer se integra mejor que Oracle en Windows y VisualStudio, escala muy bien y no se emplea exlusivamente para proyectos pequeños o medios. Podeis empezar con SqlServer Express Edition que es gratuito y ofrece las principales características de SQLServer 2005. Si vuestro proyecto crece os podeis actualizar a SQLServer 2005.
Las herramientas para administrar el gestor y realizar consultas, son más cómodas que las que he utilizado en Oracle. Aunque las de este último no las he valorado a fondo ni he buscado alternativas aparte del DBVisualizer.


Por último os dejo un diálogo del film «La vida de Brian» que ilustra sarcásticamente esta discusión.




Unos activistas del Frente del Pueblo de Judea están reunidos en un circo romano …
Stan: Todo hombre tiene derecho a tener bebés,si los quiere.
Reg: Pero tú no puedes tener bebés.
Stan: No me oprimas.
Reg: No te estoy oprimiendo, Stan; tú no tienes una matriz. ¿Dónde se va a gestar el feto? ¿Vas a mantenerlo en una caja?
(Stan comienza a llorar.)
Judith: ¡Ya! Tengo una idea. Supongamos que ustedes están de acuerdo en que él efectivamente no puede tener bebés, sin tener una matriz, lo que no es culpa de nadie, ni siquiera de los romanos, pero el puede tener el *derecho* a tener bebés.
Francis: Buena idea, Judith. Combatiremos a los opresores por tu derecho a tener bebés, hermano. Perdón, hermana.
Reg: ¿Cuál es el motivo?
Francis: ¿Qué?
Reg: ¿Cuál es el motivo de combatir por su derecho a tener bebés, cuando él no puede tener bebés?
Francis: Es un símbolo de nuestra lucha contra la opresión.
Reg: Es un símbolo de su lucha contra la realidad.

Activistas en “La vida de Brian”, film de los Monty Python. Gran Bretaña, 1979, diálogo extraído de http://www.zonamoebius.com/004/brian.htm

InfoArp: Invocar al Api Win32, crear un servidor TCP y montar un servicio de windows con .Net

Hola!


Tal como os había prometido, aquí teneis un primer post que os cuenta cómo se realizó InfoArp. Un pequeño proyecto en C# que invoca a funciones del API Win32, crea un servidor TCP y monta un servicio de windows.


Motivación
En mi trabajo se precisaba de un servicio de Windows que devolviese la tabla ARP (normalmente MACs asociadas a IPs) del propio servidor. Esta información podría obtenerse con SNMP pero fue un encargo por compatibilidad con un software de monitorización propio.
Los aspectos interesantes del proyecto son:



  • ¿Cómo consulto la tabla ARP de un equipo desde .NET?

  • ¿Cómo monto un server TCP desde .NET?

  • ¿Cómo hago que se ejecute en un servicio de Windows?

Vista general
En este diagrama de clases estático vemos la disposición general del proyecto. Para la consulta de la tabla ARP tenemos una clase adaptador, ArpAdapter, su método getArpTable() es invocado por  InfoArpServer que actúa de server TCP esperando conexiones entrantes en un puerto configurable. InfoArpService es la clase que implementa el servicio de Windows, lanza desde el método OnStart a InfoArpServer como thread. La clase ArpInfoServiceInstaller es necesaria para el instalador del servicio.



Obtener la tabla ARP desde .NET
El framework 1.1 carece de métodos o clases para ello. Iván González me sugirió que podría haber algo en la parte de Windows  Instrumentation, y aunque descubrí un montón de cosas, tampoco encontramos nada.  Al final, en el bendito MSDN, descubrí una función del API de win32 llamada GetIpNetTable en IP Helper functions (Win32 and Com development), que se encuentra en la dll del sistema IpHlpApi.dll. El siguiente paso a resolver fue cómo invocarla desde C#.
La invocación a funciones de dlls (win32, com, etc.) fuera del framework implica la ejecución del código no administrado. Los tipos de datos de entrada y de salida no se corresponden a ningún tipo común del framework.  Hay que hacer un marshalling y un unmarshalling de los parámetros de entrada y salida. Fue un agradable flashback a la programación en C y C++.  Gracias a la ayuda de Nicholas Paldino (.NET, C# MVP) y las clases de System.Runtime.InteropServices el trabajo se simplificó mucho. La clase ArpAdapter resuelve los problemas anteriormente mencionados.
Para referenciar a una función del API de Win32 no es necesario referenciar la DLL en nuestro proyecto basta con incluir en una clase:


[DllImport(«IpHlpApi.dll»)]
[return: MarshalAs(UnmanagedType.U4)]
private static extern int GetIpNetTable( IntPtr pIpNetTable,
 [MarshalAs(UnmanagedType.U4)] ref int pdwSize, bool bOrder);


Fijaos que GetIpNetTable tiene un puntero a una tabla con las entradas ARP. Es necesario conocer a priori el tamaño de la tabla para poder recuperarla. Esto se hace con el siguiente código:


public MIB_IPNETROW[] getArpTable(bool sorted)
{   
 // The table
 MIB_IPNETROW[] table = null;


 // The number of bytes needed.
 int bytesNeeded = 0;


 // The result from the API call.
 int result = GetIpNetTable(IntPtr.Zero, ref bytesNeeded, sorted);


 // Call the function, expecting an insufficient buffer.
 if (result != ERROR_INSUFFICIENT_BUFFER)
 {
  // Throw an exception.
  throw new Win32Exception(result);
 }
   
 // Allocate the memory, do it in a try/finally block, to ensure
 // that it is released.
IntPtr buffer = IntPtr.Zero;


 try
 {
  // Allocate the memory.
  buffer = Marshal.AllocCoTaskMem(bytesNeeded);


  // Make the call again. If it did not succeed, then
  // raise an error.
  result = GetIpNetTable(buffer, ref bytesNeeded, false);


  // If the result is not 0 (no error), then throw an exception.
  if (result != 0)
  {
   // Throw an exception.
   throw new Win32Exception(result);
  }


  // Now we have the buffer, we have to marshal it. We can read
  // the first 4 bytes to get the length of the buffer.
  int entries = Marshal.ReadInt32(buffer);
 
  // Increment the memory pointer by the size of the int.
  IntPtr currentBuffer = new IntPtr(buffer.ToInt64() +      System.Runtime.InteropServices.Marshal.SizeOf(typeof(int)));


  // Allocate an array of entries.
  table = new MIB_IPNETROW[entries];


  // Cycle through the entries.
  for (int index = 0; index < entries; index++)
  {
   // Call PtrToStructure, getting the structure information.
   table[index] = (MIB_IPNETROW) Marshal.PtrToStructure(new
    IntPtr(currentBuffer.ToInt64() + (index *
    Marshal.SizeOf(typeof(MIB_IPNETROW)))),
     typeof(MIB_IPNETROW));
  }
}
 finally
 {
  // Release the memory.
  Marshal.FreeCoTaskMem(buffer);
}
 return table;
}


Puede parecer complicado pero una vez mostrado el camino, si lo estudiáis con algo de detenimiento, veréis que no hace nada extraño.  Una vez que tenemos resuelto el primer problema, vamos a por el  segundo: el servidor TCP.


Servidor TCP
El servidor TCP es trivial. Está basado en el código de servidor TCP simple de hora y fecha de “How I do… “ de gotdot.net.
Ojo, este código es extremadamente simple y no es válido para todos los servidores  TCP.  Fijaos que lanza un único TCPListener que atiende la petición, lo que implica que solo se puede atender a un cliente simultáneamente. Si se recibiese otra petición, ésta tendría que esperar a que se resolviese la anterior.  Puesto que  no hay diálogo entre el cliente y el servidor  y la información a devolver es pequeña uno puede permitirse esta licencia. Sin embargo, en un entorno con diálogo entre cliente y servidor y múltiples peticiones concurrentes, habría que crear un pool de threads con TCPListeners para atender nuevas conexiones mientras se resuelven otras.


TcpListener tcpl = new TcpListener(serverAdress, port);
tcpl.Start();
   
while (true)
{
 // Accept will block until someone connects
 Socket s = tcpl.AcceptSocket();
  
 // Security check
 if (restrictedClientAddress ==
((IPEndPoint)s.RemoteEndPoint).Address)
 {     
  // Convert the string to a Byte Array and send it
  Byte[] byteDateLine = ASCII.GetBytes( getArpTable(
outputFormat).ToCharArray());
  s.Send(byteDateLine, byteDateLine.Length, 0);     
 }        
 s.Close();
}


Se crea un TcpListener  en una IP del servidor (0.0.0.0 si queréis cualquier interface) y un puerto.  A continuación entra en un bucle infinito aceptando conexiones entrantes. En nuestro caso, por añadirle algo de seguridad, solo se contestan las peticiones de una IP determinada.  Observad que podemos comparar directamente las IPs porque IPAddress implementa equals. Acto seguido se construye el buffer de envío y se manda al cliente.


Crear un servicio de Windows.
Realmente fácil. Con Visual Studio simplemente tenéis que crear un proyecto de Servicio de Windows. En nuestro caso la clase del servicio se llama InfoArpService y la plantilla de VS ya nos evita teclear bastante código.
Lo primero es darle un nombre al servicio:


private void InitializeComponent()
{
 components = new Container();
 this.ServiceName = «InfoArp»;
}


Lo segundo es implementar los métodos que responden a los eventos de inicio (OnStart) y parada (OnStop) del servicio. Se invoca un método privado local, ExecuteMain(), como thread.


public InfoArpService()
{
 InitializeComponent();   
 serverThread = new Thread(new ThreadStart(ExecuteMain));
 serverThread.IsBackground = true;
}


/// <summary>
/// Run the service
/// </summary>
/// <param name=»args»>arguments</param>
protected override void OnStart(string[] args)
{
 serverThread.Start();
}
 
/// <summary>
/// Stop the service
/// </summary>
protected override void OnStop()
{
 serverThread.Interrupt();
}


El método ExecuteMain() debe interceptar la excepción  ThreadInterruptedException que se recibe cuando se desea parar el servicio. Aunque parezca extraño es la forma correcta de finalizar un thread externamente.



private void ExecuteMain()
{
 try
 {   
  InfoArpServer.Launch(
   IPAddress.Parse( ConfigurationSettings.AppSettings[«ServerIP»]),
   Int32.Parse( ConfigurationSettings.AppSettings[«ServerPort»] ),
IPAddress.Parse(  ConfigurationSettings.AppSettings[«RestrictedClientIP»]),
   ConfigurationSettings.AppSettings[«ArpOutputFormat»]);
 }
 catch (ThreadInterruptedException)
 {
  // Simply exit.
 }
 catch(ConfigurationException ce)
 {
  EventLog.WriteEntry(
String.Format(«Can’t read configuration. Service stopped. n {0}»,
     ce.Message), EventLogEntryType.Error);
 }
 catch(Exception e)
 {
  EventLog.WriteEntry(
String.Format(«Unexpected exception. Service stopped.n {0}n{1}»,
    e.Message, e.StackTrace), EventLogEntryType.Error);
 } 
}



Las excepciones no esperadas y las derivadas de la configuración son registradas en el registro de sucesos.
También es necesario crear una clase instaladora, ArpInfoServiceInstaller, imprescindible para el proceso de instalación de nuestro servicio.


[RunInstallerAttribute(true)]
public class ArpInfoServiceInstaller : Installer
{
 private ServiceInstaller serviceInstaller1;
 private ServiceProcessInstaller processInstaller;


 public ArpInfoServiceInstaller()
 {
  // Instantiate installers for process and services.
  serviceInstaller1 = new ServiceInstaller();
  processInstaller = new ServiceProcessInstaller();


  // The services are started automatically.
  serviceInstaller1.StartType = ServiceStartMode.Automatic;


  // The service run under the system account.
  processInstaller.Account = ServiceAccount.LocalSystem;


  // ServiceName must equal those on ServiceBase derived classes.           
  serviceInstaller1.ServiceName = «InfoArp»;


  // Add installers to collection. Order is not important.
  Installers.Add(serviceInstaller1);
  Installers.Add(processInstaller);


   }
}
Se indica el modo de arranque por defecto (manual, automático, …) y como quién se va a ejecutar. Es importante que el nombre del servicio coincida con el definido en InfoArpService.


Instalación
Generamos la solución en release y abrimos la carpeta binRelease de nuestro proyecto. Deberíamos encontrar un ejecutable y el archivo de configuración que modificaremos según nuestro entorno.
Para instalar el servicio tenemos que ejecutar la utilidad installutil que viene con el framework. Desde la línea de comandos hacemos:



C:windowsMicrosoft.NETFrameworkv1.1.4322installutil InfoArpService.exe


Generará diversos archivos de log además de la salida por la consola.


Ejecutando una instalación de transacción.
Iniciando la fase de instalación dentro de la instalación.
Consulte el contenido del archivo de registro sobre el progreso del ensamblado binreleaseinfoarpservice.exe.
El archivo está ubicado en binreleaseinfoarpservice.InstallLog.
Instalando ensamblado ‘binreleaseinfoarpservice.exe’.
Los parámetros afectados son:
   assemblypath = binreleaseinfoarpservice.exe
   logfile = binreleaseinfoarpservice.InstallLog
Instalando el servicio InfoArp…
El servicio InfoArp se ha instalado correctamente.
Creando el origen de EventLog InfoArp en el registro Application…
La fase de instalación finalizó correctamente y la fase de confirmación está empezando.
Consulte el contenido del archivo de registro sobre el progreso del ensamblado binreleaseinfoarpservice.exe.
El archivo está ubicado en binreleaseinfoarpservice.InstallLog.
Confirmando ensamblado ‘binreleaseinfoarpservice.exe’.
Los parámetros afectados son:
   assemblypath = binreleaseinfoarpservice.exe
   logfile = binreleaseinfoarpservice.InstallLog


Si vamos al inicio > panel de control > herramientas administrativas > servicios veremos que ha añadido nuestro servicio. Inicialmente está parado y se ejecutará en el próximo inicio del sistema. Si lo queremos lanzar inmediatamente haremos clic sobre él y clic en el botón iniciar del cuadro de diálogo de propiedades del servicio.
Para desinstalar el servicio tendremos que ejecutar  installutil con las opción /u.  Si el servicio está corriendo intentará pararlo primero.


C:windowsMicrosoft.NETFrameworkv1.1.4322installutil /u InfoArpService.exe


Para probar el servicio haced un telnet a la IP especificada del servidor  y puerto correspondiente. En el archivo de configuración también podéis especificar el formato de salida.


C:>telnet 193.144.53.xx 3333


ARP 193.144.53.1x 00:0A:AF:xx:xx:xx
ARP 193.144.53.1x 00:D0:95:xx:xx:xx
ARP 193.144.53.x4 00:03:E8:xx:xx:xx
ARP 193.144.53.2x 00:01:02:xx:xx:xx
ARP 193.144.53.x1 00:04:75:xx:xx:xx
ARP 193.144.53.3x 00:01:E7:xx:xx:xx
ARP 193.144.53.1x 08:00:09:xx:xx:xx
ARP 193.144.53.1x 00:04:75:xx:xx:xx
ARP 193.144.53.x3 00:0A:5E:xx:xx:xx
ARP 193.144.53.x6 00:00:E8:xx:xx:xx
ARP 193.144.53.x4 00:0D:61:xx:xx:xx
ARP 193.144.53.x7 00:0F:B0:xx:xx:xx
ARP 193.144.53.x3 00:04:75:xx:xx:xx
ARP 193.144.53.2x 00:50:FC:xx:xx:xx
ARP 193.144.53.x1 00:50:FC:xx:xx:xx
ARP 193.144.53.x3 00:A0:D2:xx:xx:xx
END



Se ha perdido la conexión con el host.


 


Pues eso es todo por ahora. Espero que os anime a crear servicios de windows. Es realmente fácil desde VS.

A las muy buenas ;-)

Hola geeks!!!


Soy Eduardo Quintás y trabajo habitualmente con VS2003 y VS2005 en C#. Estoy encantado con este espacio que me brindan los administradores de geek.ms y espero publicar posts del interés de todos y a la altura de otros que he leído por aquí.


Estoy preparando posts sobre dos temas en los que he trabajado últimamente.



  1. El primer tema trata de una aplicación de gestión que se ha realizado en .NET 1.1 y C# utilizando el framework de NHibernate y el framework ActiveRecord de Castle Project. Probablemente de para más de un post y creo que hay aspectos realmente interesantes.

  2. El segundo tema es un pequeñísimo proyecto que monta un servicio de windows que sirve en un puerto información sobre la tabla ARP del equipo (era necesario para un tema de monitorización desde una aplicación perl en unix). El punto interesante es cómo llamar al API de windows y ejecutar código no administrado con marshalling y unmarshalling de parámetros.

Posts en breve…