Caja transparente Caja transparente
Transcripción
Caja transparente Caja transparente
nº14 abril 2005 • 6,00 € (España) Visual Basic.NET • C# • Delphi • ASP.NET • ADO.NET • .NET Framework • Windows Server System dotNetManía www.dotnetmania.com Dedicada a los profesionales de la plataforma .NET PDF Caja transparente Entrevista a Brian Goldfarb Technical Product Manager de la Developer Division de Microsoft Seguridad en los servicios Web • Manipulación de imágenes con ADO.NET • Sistemas distribuidos en .NET con Remoting (y III) • Interoperabilidad no administrada y migración (y III) ToDotNet Q&A Consideraciones de rendimiento Comunidad.net dotNetSolidario, el lado humano de la tecnología Legal Protección formal de un programa de ordenador MVP Online MZ-Tools dnm.editorial dotNetManía Eventos a la vista Vol. II •Número 14 • Abril 2005 Precio: 6€ (España) Editor Paco Marín ([email protected]) Administración Pilar Pérez ([email protected]) Asesor Técnico/Coordinación Marino Posadas ([email protected]) Redactores Antonio Quirós, Dino Esposito, Guillermo 'guille' Som, Jorge Serrano, José Manuel Alarcón, Luis Miguel Blanco,y Pedro Pozo. Colaboradores habituales Ángel Esteban, Braulio Díez, Eladio Rincón, Erich Bühler, Fernando Nogueras, Jorge Crespo Cano, José Miguel Torres, Manuel Imaz, Miguel Egea, Miguel Katrib Mora (Grupo Weboo), Octavio Hernández, Pablo Abbate, Pepe Hevia, Rodrigo Corral y Salvador Ramos. Además colabora en este número Javier Aragonés Miranda (Suárez de la Dehesa Abogados) Edición y Suscripciones .netalia c/ Robledal, 135 28529 Rivas-Vaciamadrid (Madrid) Tf. (34) 91 666 74 77 Fax (34) 91 499 13 64 Publicidad Mediadev Sophie Mancini ([email protected]) Tf. 93 426 22 57 - 670 99 74 64 Fax. 93 423 11 40 Imprime Gráficas Vallehermoso www.graficasvallehermoso.com ISSN 1698-5451 Depósito Legal M-3.075-2004 << Bienvenidos al número 14 de dotNetManía de abril de 2005. En este mes no tenemos grandes noticias que publicar, pero, en cambio, sí tenemos un par de eventos que anunciarle, muy importantes para los desarradores de la plataforma .NET. Por un lado, los Solid Quality Learning University Summits, que se impartirán en Madrid entre el 25 y 29 de abril y en Barcelona entre el 2 y 6 de mayo con la mayoría de sesiones en castellano; y, por otro, el evento más importante para desarrolladores que Microsoft Ibérica celebra cada año: el DevDays, que este año se celebrará en el mes de mayo y donde tendremos ocasión de escuchar, entre otros, a los Regional Directors de España. ¡Será cuestión de hacerse un hueco en la agenda! Para este número hemos preparado una estupenda entrevista con Brian Goldfarb, Technical Product Manager de la Developer Division de Microsoft Corp., y con el que tuvimos la ocasión de charlar en Madrid, adonde vino con ocasión del ASP.NET 2.0 Tour. Con él hablamos largo y tendido de la nueva versión de ASP.NET. A destacar, el artículo de Braulio Díez sobre el formato de los archivos PDF. Enfocado a quien necesite incluir en sus programas la generación de este tipo de archivos, y lo hace desde tres puntos de vista diferentes: de quien quiera currárselo desde cero –hay que ver lo que nos gusta eso a todos–; de los que opten por el uso de una librería gratuita y de código abierto; y de los que quieran rascarse el bolsillo y comprar alguna librería. Hay una opción más en la sección dnm.laboratorio.net, para los que no queden conformes con lo expuesto. Y aún así quedan muchas opciones en el mercado, pero nada más gratificante que reinventar la rueda y pegarse con el formato de los archivos PDF y hacérselo uno mismo ¿verdad?. Tan bonito, como improductivo, por otra parte. Aprovecho para agradecer a Fran Rem de TallComponents BV por su inestimable colaboración en la preparación de este artículo. Y nos queda más, cómo no: los últimos artículos de las series de Sistemas distribuidos con .NET Remoting, de Rodrigo Corral, y de Interoperabilidad no administrada y Migración de José Miguel Torres, a los que liberamos para que nos escriban sobre otros temas que seguro que le van a interesar. Y los artículos, como siempre con buenos fundamentos, de Pepe Hevia sobre Seguridad en los servicios Web y de Luis Miguel Blanco sobre Manipulación de imágenes con ADO.NET. Por último, no olvide hacer el crucigrama que estrenamos este mes. Parece que los autores del Grupo Weboo se han empeñado en volvernos realmente dotnetmaníacos. Espero que le guste. <<dotNetManía Dedicada a los profesionales de la plataforma .NET 3 14 dnm.sumario Protección formal de un programa de ordenador 8-9 En este artículo se intentarán exponer brevemente las principales opciones que tiene un programador cuando se plantea proteger su creación. Entrevista a Brian Goldfarb 10-13 Con motivo del ASP.NET 2.0 Tour que impartieron por toda Europa Eric Rudder y Brian Goldfarb, tuvimos ocasión, durante su estancia en Madrid, de entrevistar a éste último, en calidad de Product Manager de la próxima versión de ASP.NET, y aprovechamos para preguntarle por los pormenores de esta nueva versión. Seguridad en los servicios Web 14-18 Mucho se ha hablado de la seguridad de las páginas ASP.NET. Pero más importante aún es el hecho de asegurar nuestros servicios de negocio. Veremos como proteger nuestros servicios con modelos de autenticación / autorización y cómo escribir nuestros clientes para diseñar entornos distribuidos seguros. dnm.sumario Manipulación de imágenes con ADO.NET 19-24 Cualquier desarrollador que se haya acercado a la tecnología .NET, en su vertiente de acceso a datos, dominará la arquitectura de ADO.NET cuando se trata de crear un proceso de mantenimiento de datos típico, utilizando los tipos de datos habituales. Pero, ¿qué ocurre cuando entran las imágenes como parte del mantenimiento?, ¿cómo agregamos estos elementos a una gestión de datos? En el este artículo describimos los recursos que nos ofrece .NET, y las técnicas a nuestra disposición para integrar las imágenes como parte de los procesos de manipulación de datos. Sistemas distribuidos en .NET con Remoting (y III) 25-28 Último artículo sobre Sistemas distribuidos en .NET con Remoting. Una vez visto cómo configurar las opciones de los objetos, los canales que usaremos y la ubicación de éstos mediante RemotingConfiguration, veremos cómo configurar .NET Remoting mediante archivos. Finalmente veremos cómo utilizar un servicio o IIS como host en lugar de una aplicación de consola como hemos venido haciendo hasta ahora. Interoperabilidad no administrada y migración (y III) 29-33 En esta última parte describiremos cómo llamar a funciones externas con un ejemplo a una API de Windows. También describiremos la utilización de TypeLibConverter, en el proceso de exportación e importación de la biblioteca de tipos, clave para llevar acabo la interoperabilidad COM. PDF. Caja transparente 36-44 Para la mayoría de la gente, el formato PDF es un tipo de documento universal que se puede ver desde cualquier máquina sin perder detalle. ¿Cómo se crea un PDF? Herramientas especiales, conversores…, en definitiva: “cajas negras”. En este artículo vamos a convertir dicha caja opaca, en transparente, estudiando su formato y viendo las diversas formas de generarlo; para ello usaremos C#. dnm.mvp.online 46-49 MZ-Tools de Carlos Quintero. dnm.comunidad.net 50-51 dotNetSolidario. El lado humano de la tecnología. dnm.todotnet.qa 52-54 Consideraciones de rendimiento. dnm.laboratorio 55-56 ASP.NET Version Switcher, Localization Manager, ISAPI URL Mapper y Sharp PDF dnm.biblioteca.net 57 La Cara Oculta de C# (Ian Maartens) Windows Forms Programming in C# (Chris Sells) dnm.desvan 58 dnm.noticias 6 noticias.noticias.noticias.noticias.noticias.noticias << dotNetManía << dnm.noticias Microsoft presenta las nuevas ediciones de SQL Server 2005 y Visual Studio 2005 Microsoft ha presentado las nuevas ediciones de SQL Server 2005 y Visual Studio 2005, con las que la compañía espera responder a las necesidades de cada segmento de clientes Ediciones de SQL Server 2005 y nueva versión CTP pre-beta 3 Las ediciones previstas así como sus precios (en dólares, aún no tenemos los precios en euros para España) son: • SQL Server 2005 Enterprise Edition está pensada para aplicaciones de negocio de misión crítica de grandes empresas.Esta edición ofrecerá partición de datos, alta disponibilidad avanzada con capacidades de database mirroring, analíticas complejas e integración, generación de informes ad hoc con Report Builder, etc. Disponible a un precio de venta estimado de 24.999$ por procesador, 13.499$ por servidor (25 CAL). • SQL Server 2005 Standard Edition está diseñada para las medianas empresas e infraestructuras que requieren sistemas altamente disponibles. Esta edición proporcionará funcionalidad mejorada que antes sólo estaba incluida en SQL Server 2000 Enterprise Edition, tales como alta disponibilidad avanzada con capacidades de database mirroring y clustering y soporte integrado de 64 bit. Esta edición soporta hasta cuatro procesadores, base de datos de tamaño ilimitado y sistema de memoria ilimitado. También incluye SQL Server Integration Services, SQL Server Analysis Services y SQL Server Reporting Services, lo que aporta a los clietnes una potente funcionallidad de business intelligence sin coste adicional. Disponible a un precio estimado de 5.999$ por procesador ó 2.799$ por servidor (10 CAL). • SQL Server 2005 Workgroup Edition es el producto más novedoso disponible tanto para SQL Server 2000 como SQL Server 2005, y proporciona una solución de base de datos asequible, fácil de usar y sencilla de gestionar, diseñada específicamente para las pequeñas y medianas organizaciones. Workgroup Edition soporta hasta dos procesadores, base de datos de tamaño ilimitado y 2 Gb de memoria. Disponible a un precio de venta estimado de 3.899$ por procesador y 739$ por servidor. • SQL Server 2005 Express Edition es una versión gratuita y redistribuible del motor de base de datos de SQL Server 2005, diseñada para construir sencillas aplicaciones de base de datos, que sustituye a MSDE (Microsoft Data Engine) para SQL Server 2000. Los clientes podrán incrustar y redistribuir SQL Server 2005 Express Edition con sus aplicaciones. Se puede descargar gratuitamente de la Web e incluye una herramienta gráfica de gestión, controles y un asistente para informnes, replicación, un cliente SQL Service Broker, encriptación de base de datos nativa, soporte XML y CLR. Y ya está disponible la tercera CTP de SQL Server 2005, posiblemente la última antes de la beta 3, que incluye nuevas características, de las que quizá la más significativa es la incorporación de Report Builder, un nuevo componente de SQL Server 2005 Enterprise Edition, que se basa en la tecnología adquirida de ActvieViews en el 2004. Gracias a esta herramienta el reporting empresarial será más fácil para los usuarios finales. Studio 2005, que estarán disponibles a finales de año junto con SQL Server 2005 -según Prashant Sridharan, Lead Program Manager del equipo de desarrollo de Visual Studio 2005-, así como la nueva CTP de la edición Professional disponible para suscriptores MSDN y betatesters. Las ediciones de Visual Studio 2005 serán: • Visual Studio 2005 Team System. Con herramientas integradas para la gestión del ciclo de vida de desarrollo de software. • Visual Studio 2005 Professional Edition, una edición asequible destinada a los desarrolladores profesionales que trabajan en pequeños grupos de trabajo. • Visual Studio 2005 Standard Edition, una edición para desarrolladores a tiempo parcial o aplicaciones de negocio que le permitirá desarrollar aplicaciones Windows, Web o para dispositivos móviles. Esperamos una beta 2 en castellano de esta edición para el mes de abril. • Visual Studio 2005 Tools for the Microsoft Office System, la edición para el desarrollador profesional que construya soluciones para Microsoft Office System usando Excel, Word e Infopath. • Ediciones Visual Studio 2005 Express: – Visual Web Developer 2005 Express Edition, una herramienta ligera para la construcción de sitios Web dinámicos y servicios Web. – Visual Basic 2005 Express Edition, Visual C# 2005 Express Edition, Visual C++ 2005 Express Edition y Visual J# 2005 Express Edition. Igualmente ediciones ligeras para ayudar a los programadores que empiezan a construir aplicaciones para Windows. Ediciones de Visual Studio 2005 y nueva versión CTP pre-beta 2 En breve estarán disponibles las versiones beta 2 públicas de estas ediciones que incluiremos en el número correspondiente de dotNetManía (mayo o junio) de forma gratuita. Asimismo, Microsoft Corp. ha anunciado las ediciones de Visual << dnm.noticias Los desarrolladores pueden probar por primera vez la nueva generación de programación orientada a servicios Microsoft Corp. ha anunciado la primera CTP (Community Technology Preview) de Indigo, el próximo subsistema de comunicaciones y que estará disponible para su descarga para Ari Bixhorn los suscriptores a MSDN, el 31 de marzo, dato que al cierre de esta revista no hemos podido comprobar y no sabemos si finalmente se hará disponible para el “38 de marzo” como Eric Rudder, Vicepresidente Senior de la División de Servidores y Herramientas de Microsoft Corp, dijo, bromeando -creemos, en el pasado VSLive! de San Francisco en el que ya anunciaba estas versiones jun- to con Ari Bixhorn, Lead Product Manager of Web services Strategy de Microsoft. Ari Bixhorn ha publicado una pequeña entrevista en el sitio de prensa de Microsoft Corp. y que puede leer en http://www.microsoft.com/presspass/features/2005/mar05/03-16WebServices Preview.asp. Junto con esta versión de Indigo, también se liberará la segunda CTP de Avalon, el susbsitema de presentación de Longhorn y que, igualmente, estará disponible para suscriptores MSDN... el 31 de marzo. Si desea más información sobre Indigo y, en general, sobre todo el nuevo entorno de programación de Longhorn, WinFX, puede visitar: http://winfx.msdn.microsoft.com. Nuevo Service Pack 2 de SQL Server 2000 Reporting Services Desde su lanzamiento, en enero de 2004, se han descargado más de 153.000 copias de SQL Server 2000 Reporting Services y 116.000 copias adicionales del Service Pack 1. Junto con la seguridad y las mejoras en el producto, este SP2 incluirá dos SharePoint Web Parts, que permitirán a los usuarios explorar y ver informes localizados en un servidor de informes a través de Windows SharePoint Fernando Bocigas Services o SharePoint Portal Server. Los Web Parts facilitarán a los clientes, la creación de portales de business intelligence con Sharepoint que incluyan informes de Reporting Services. SP2 también permitirá al cliente la impresión de informes directamente desde Internet Explorer. Microsoft lanza también SQL Server Report Pack para IIS Logs. Este Report Pack facilitará a los clientes el desarrollo de report al proporcionar a los usuarios plantillas de los informes de uso más común de SQL Server Reporting Services, que estos pueden modificar fácilmente para crear sus propios informes personalizados de acuerdo con sus propias necesidades. El Report Pack está disponible desde finales de marzo para ser descargado de la Web. “Nuestro objetivo desde que introdujimos capacidades de business intelligence en SQL Server ha sido proporcionar a tantos usuarios como sea posible un conocimiento más amplio de sus negocios y del mercado, para que tomen las mejores decisiones, y Reporting Services es el componente principal para conseguirlo.”, ha declarado Fernando Bocigas, responsable de negocio de SQL Server en Microsoft Ibérica. Otras noticias Microsoft Developer Day 2005 El 17 de mayo se celebrará en el cine Kinepolis de Pozuelo de Alarcón en Madrid el Microsoft Developer Day, un clásico en eventos orientados a la comunidad de desarrolladores, reproducido en la mayoría de los países a lo largo del mundo. Este evento está patrocinado tradicionalmente por los Regional Directors, pero en esta ocasión también participarán algunos partners de Microsoft. Será un evento técnico, para desarrolladores, en el que se verán las novedades las novedades más importantes en la próxima versión de la plataforma de desarrollo Visual Studio 2005 y también de SQL Server 2005. Aún es pronto, pero por lo que hemos podidos saber, se hará un repaso de las mejoras introducidas en cada uno de los pilares que forman ambos productos. Aún no tiene una direción definitiva pero podrá encontrarlo en la web de MSDN España en: www.microsoft.com/spain/msdn SQLU 2005 Summit Solid Quality Learning viene impartiendo a lo largo de ciudades de todo el mundo los eventos formativos “SQL Quality Learning University Summits”. Después de pasar por Viena y Buenos Aires, pasarán por Madrid entre el 25 y el 29 de abril y por Barcelona entre el 2 al 6 de mayo. La mayoría de los ponentes serán de habla castellana, entre los que se encuentran Miguel Egea, Eladio Rincón, Fernando Guerrero, Antonio Soto, Guillermo “El Guille” Som, Jordi Rambla, Daniel Seara, Pablo Peláez, Alejando Leguízano, Michael Hotek y Ron Talmage. También contarán con Cesar Galindo-Legaria, Manager del SQL Server Query Optimizer Group de Microsoft Corp. Más información: http://www.solidqualitylearning.com/view.aspx?source=SQLUMadrid2005 Summit y en Barcelona: http://www.solidqualitylearning.com/view.aspx?source=SQLUBarcelona 2005Summit. Service Pack 1 para Visual Studio .NET 2002 Las correcciones incluidas en este Service Pack están relacionadas con las mejoras de estabilidad y seguridad realizadas en muchas áreas del producto. La lista completa de revisiones solicitadas por los usuarios desde el lanzamiento de Visual Studio .NET 2002 e incluidas en este Service Pack se puede encontrar en: http://support.microsoft.com/ kb/837234. Para descargar el Service Pack 1 para Visual Studio 2002 debe ir a: http://msdn.microsoft.com/vstudio/downloads/updates/sp dnm.noticias << dotNetManía Microsoft anuncia la disponibilidad de la primera CTP de Indigo y la segunda de Avalon 7 dnm.legal Javier Aragonés Miranda Protección formal de un programa de ordenador En este artículo se intentarán exponer brevemente las principales opciones que tiene un programador cuando se plantea proteger su creación. << En primer lugar, hay que dejar claro que el mero hecho de crear un programa de ordenador, sin necesidad de ningún otro requisito más que el de la originalidad, ya supone que dicho programa se encuentra protegido por la legislación de propiedad intelectual. Sin embargo, para hacer valer esa protección legal, conviene utilizar algún procedimiento que permita probar que dicho programa fue creado por una determinada persona en un determinado momento, y es ahí donde se plantean diferentes alternativas que sirven para acreditar la autoría del programador. Las opciones a las que nos referimos son las siguientes: • Inscripción de la obra en el Registro de la Propiedad Intelectual y • Depósito del programa ante Notario Junto a estas dos opciones disponemos de otros medios que también constituirán prueba de la autoría, y que son complementarias de las dos vías de protección que hemos citado, como son las denominadas marcas de agua o la introducción de líneas de código neutras. Inscripción de la obra en el Registro de la Propiedad Intelectual Javier Aragonés Miranda es abogado de Suárez de la Dehesa Abogados, despacho especializado en Propiedad Intelectual e Industrial y Nuevas Tecnologías. http://www.suarezdeladehesa.com Con respecto a la inscripción de derechos de la obra en el Registro de la Propiedad Intelectual, lo primero que hay que decir es que la misma es posible siempre que se trate de un programa original cuyos derechos sean de titularidad de la persona a cuyo favor se inscriban. Por el carácter declarativo del Registro de la Propiedad Intelectual, el principal beneficio que se obtiene de la inscripción de derechos relativos a una obra en el mismo es la creación de una presunción de titularidad a favor de la persona o empresa a cuyo nombre está registrada. En cuanto al procedimiento de inscripción, este puede resultar un poco laborioso, porque junto al impreso oficial de solicitud, ha de presentarse en el Registro la siguiente documentación: 1. La totalidad del código fuente en papel debidamente encuadernado y paginado incluyendo el título y nombre y apellidos del autor o titular originario, salvo que por la extensión del mismo el registro exija que se aporte en formato CD-ROM o disquete. Por el carácter declarativo del Registro de la Propiedad Intelectual, el principal beneficio que se obtiene de la inscripción de derechos relativos a una obra en el mismo, es la creación de una presunción de titularidad a favor de la persona o empresa a cuyo nombre está registrada << dnm.legal 2. 1. Un ejecutable del programa, que se aportará también en CD-ROM o disquete. 3. Justificante del abono de la tasa correspondiente. 4. Opcionalmente, aunque es recomendable, puede presentarse una memoria que contenga: • Una breve descripción del programa de ordenador. • El lenguaje de programación. • El entorno operativo. • Un listado de ficheros. • El diagrama de flujo. • En su caso, número de depósito legal. Como opción complementaria o sustitutoria a la inscripción en el Registro, puede realizarse el depósito notarial del programa de ordenador, pues requiere menos formalismos y documentación y con el mismo se obtienen prácticamente los mismos efectos que proporciona el Registro intelectual realizada por los trabajadores. Si son asalariados será suficiente presentar el contrato de trabajo y justificante de pago de las cuotas a la Seguridad Social. Como se aprecia, la inscripción de derechos de un programa de ordenador en el Registro de la Propiedad Intelectual requiere mucha documentación, lo que puede suponer un inconveniente para el titular, ya que ha de aportar copia compulsada de toda la documentación presentada, y eso puede encarecer bastante el coste de la inscripción, sobre todo en el supuesto de que el titular sea una empresa, porque la compulsación se puede llevar a cabo por los funcionarios del Registro o por un Notario, y de ambos, es más económico hacerlo ante Notario. Junto al coste y documentación necesaria, otro inconveniente que puede presentar la inscripción en el Registro es la obligación de entregar la totalidad del código fuente, ya que ello suele ser una traba muy grande para muchas empresas que, lógicamente, prefieren mantener el mismo en secreto, pues constituye su activo más preciado. Depósito del programa ante Notario Es por este segundo inconveniente que, como opción complementaria o sustitutoria a la inscripción en el Registro, puede realizarse el depósito notarial del programa de ordenador, pues requiere menos formalismos y documentación, y con el mismo se obtienen prácticamente los mismos efectos que proporciona el Registro, que no son otros que fehaciencia en cuanto a la titularidad y al tiempo. El procedimiento para depositar el programa ante Notario podrá variar ligeramente según la Notaría que se elija pero, en general, todos tiene en común que se requiere al Notario para que custodie en un sobre sellado y lacrado un CD-ROM que contenga el código del programa, lo que ellos se encargan de hacer durante el tiempo que se acuerde por una cantidad de dinero que no resulta excesivamente alta, pues suele tener un coste aproximado de unos 150 o 200 euros por CD depositado. 5. Y, en el caso de ser una empresa la titular del programa, se ha de presentar también: • Los datos identificativos de la misma, incluyendo el CIF. • El título que acredite su personalidad jurídica (escritura de constitución, por ejemplo) y • Los documentos acreditativos de la cesión de los derechos de propiedad Así pues, como conclusión y de manera general, si el titular del programa es una persona física puede ser preferible y más económico proceder a la inscripción en el Registro de la Propiedad Intelectual; mientras que si el titular es una empresa, puede resultar más conveniente proceder al depósito ante Notario. Pero la elección final variará dependiendo del caso y de las preferencias del titular del programa que se quiere proteger. <<dotNetManía Conclusión 9 dnm.directo.entrevistas Marino Posadas Entrevista a Brian Goldfarb Con motivo del ASP.NET 2.0 Tour que impartieron por toda Europa Eric Rudder y Brian Goldfarb, tuvimos ocasión, durante su estancia en Madrid, de entrevistar a éste último, en calidad de Product Manager de la próxima versión de ASP.NET, y aprovechamos para preguntarle por los pormenores de esta nueva versión. << ¿Cuál es tu papel exactamente en la promoción de ASP.NET? Básicamente, el de recoger las necesidades de los desarrolladores, organizarlas y trasladarlas al equipo de desarrollo, de forma que dispongamos de ese feedback tan importante para nosotros. ¿Hasta qué punto es necesario para ti conocer el producto? Hasta sus más íntimos detalles, si quieres hacerlo bien. No se trata de un producto de consumo masivo, ni de una herramienta de producción, sino de una herramienta de desarrollo. Y los desarrolladores tienen un perfil muy técnico y esperan respuestas detalladas y concretas. A un desarrollador no pue- des hablarle de “valores de negocio” ni de “oportunidades estratégicas” porque es algo que no le importa. Lo que quiere saber es qué hace el producto, cómo lo hace, y qué tiene de nuevo respecto a la versión anterior. Antes de hablar acerca de las características de la nueva interfaz de usuario y los nuevos controles de servidor, me interesan especialmente los cambios en la arquitectura. ¿Podrías resumirlos y explicarlos brevemente? En el nivel más básico del sistema podemos encontrar todo lo que llamamos Servicios de Aplicación. Cosas que ayudan a reducir el código necesario para un desa- Marino Posadas es asesor técnico y redactor de dotNetManía, MVP de C# y formador de Alhambra-Eidos Brian Goldfarb << dnm.directo.entrevistas De Izquierda a derecha: Marino Posadas, Brian Goldfarb y David Carmona creación de granjas de servidores que suministren altas prestaciones. Por ejemplo, hemos hecho un gran esfuerzo respecto a la implantación de ASP.NET 2.0 en máquinas de 64 bits. Otra gran novedad que se presenta, es la posibilidad de construir sitios Web sin la presencia de IIS en la máquina donde se desarrolla. ¿Eso se va a extender desde la fase de desarrollo a la de implantación? desarrollo, al menos dentro de la beta a la que hemos tenido acceso. ¿Va a seguir esto siendo así en la versión final? Como parte de la idea de conseguir que Visual Studio 2005 fuera una herramientas más amigable, necesitábamos algo así en el producto. Por eso damos soporte a cuatro formas de creación de sitios Web: como un sistema local, como una aplicación tradicional gestionada bajo IIS, mediante al soporte de FTP y Visual Studio 2005 incluye un servidor Web dentro. Esto te permite no tener que configurar el IIS dentro de la máquina de desarrollo, o que existan varios desarrolladores depurando sobre la misma máquina, y no tener que dar a los del equipo de desarrollo permisos especiales Visual Studio 2005 incluye un servidor Web dentro del paquete de software. Esto te permite no tener que configurar el IIS dentro de la máquina de desarrollo, o que existan varios desarrolladores depurando sobre la misma máquina, y no tener que dar a los del equipo de desarrollo permisos especiales, por ejemplo. No obstante, desde el punto de vista de la implantación, sí que se necesita una instancia de IIS: un servidor Web real. Aún así, se trata de un gran cambio, porque evita pasar por los problemas de configuración de IIS. Resulta notable la presencia de un programa FTP dentro del entorno de también, mediante la posibilidad de crear sitios Web directamente sobre una URL remota, vía HTTP. Otra cuestión relativa a la arquitectura tiene que ver con la presencia de nuevos directorios predeterminados asociados con el sitio Web: data , code , resources, localresources, webreferences, browsers y themes. El nuevo modelo, incluyendo todas las mejoras que queríamos incluir, requería de la presencia de estos directorios. Cuando planteamos esta situación, tuvimos en cuenta la posibilidad de que otras aplicaciones utilizasen ya estos nombres de directorios, para evi- <<dotNetManía rrollo, como la gestión de credenciales, control de roles de aplicación, personalización, localización, etc. Esos servicios básicos constituyen la base de todas las mejoras. Podríamos considerar cuatro pilares principales: El primero, sería la productividad de cara al desarrollador. Desglosado en dos conceptos. Uno es simplificación de los conceptos de codificación necesarios; y el segundo, mejorar la calidad de la herramienta de desarrollo Web. El segundo pilar es la extensibilidad. Si lo que el desarrollador necesita es la capacidad de crear aplicaciones de multicapa, debemos suministrarle esa posibilidad de la forma más natural posible, y poco adelantamos con permitir que se arrastre una tabla sobre una página y cree una serie de elementos para programar esa tabla, porque nadie hace las cosas de esa manera. Necesitamos elementos diferentes que suministren esas posibilidades, que en este caso son suministradas mediante lo que llamamos el patrón de proveedores. Tenemos que ser capaces de modificar y descargar esquemas, y ser capaces de adaptar esos esquemas a la forma en que todo esto funciona realmente. El tercer pilar es la capacidad de mantenimiento y gestión. Uno de los problemas a resolver era facilitar la administración e implantación de las aplicaciones. Dando soporte mejorado de la implantación, añadiendo posibilidades que faciliten la labor de los administradores, el seguimiento de los errores, etc. Y el cuarto pilar es el rendimiento. Y las consideraciones de escalabilidad de las aplicaciones, que tienen mucho que ver con esto. Conseguir que sea fácil la 11 << dnm.directo.entrevistas tar las posibles colisiones. Así que decidimos finalmente llamar así a los directorios, pero haciéndolos preceder del prefijo app_. De esa forma, tendremos, al final, app_data, app_code, etc. Dinos algo acerca del nuevo modelo de compilación. de código de marcado y un grupo de DLL asociadas funcionalmente con ese código. Esa es otra cuestión importante. Parece que ahora vamos a tener un conjunto de DLL diferentes en lugar de una sola con todo el código fuente. <<dotNetManía La manera en que se almacenan y transportan las credenciales, de forma encriptada, supone que ahora no hay forma de que un hacker pueda hacer ingeniería inversa de las contraseñas, o de los números de una tarjeta de crédito 12 Es muy interesante y ofrece un montón de beneficios al desarrollador. En el nuevo modelo, podemos aprovechar una nueva característica denominada tipos parciales. Esto nos permite separar la clase que estamos construyendo en múltiples ficheros. Es una situación muy similar a la que planteará XAML para la construcción de aplicaciones para Longhorn. En ASP.NET 2.0 tendremos la opción de precompilar incluso el código de marcado (la parte HTML, por decirlo así). En la beta 2, ya tendremos la oportunidad de compilar en un ensamblado todo el código de marcado, o mantener el mismo modelo existente hoy, con un conjunto de ficheros Efectivamente, esta es una cuestión que nos lleva de nuevo al modelo de compilación. En esta forma, podemos manejar mejor los contenidos del proyecto, añadiendo un factor de granularidad que no estaba presente antes. Permite que diferentes desarrolladores puedan trabajar de forma independiente sin propagación mutua de los errores producidos, manteniendo cada uno su versión del código generado y evitando colisiones. Esto nos permite realizar cambios y ver los resultados con un simple refresco de la página en el navegador, reflejando mucho mejor la forma de trabajar del programador Web. ¿Qué hay de nuevo desde el punto de vista del acceso a datos? En ASP.NET 1.1, el trabajo con datos conllevaba una buena cantidad de código fuente de soporte. Así que hemos asumido un nuevo modelo de enlace a datos basado en dos objetos que introduce esta versión. Por ejemplo, el nuevo control DataSource. Este control permite vincular elementos diferentes de una página al mismo control DataSource y mantener una vista de datos actualizada sin necesitar ningún código fuente de soporte adicional. Eso significa también la posibilidad de edición, ordenaciones, paginaciones, etc., gestionada automáticamente por ese componente. Cada DataSource se convierte en un elemento específico que vincula un origen de datos: AccessDataSource, SQLDataSource, XMLDataSource, etc. El segundo punto crítico en cuanto a esto tiene que ver con lo que llamamos Object DataBinding. Estos objetos, llamados ObjectSources, permiten vincular un elemento de la interfaz de usuario o de la capa intermedia a cualquier origen, pudiendo ser éste un servicio Web, un objeto de acceso a datos u otro objeto de la capa de negocio, sin la necesidad de escribir código alguno. Esto permite diseñar sistemas de 3 capas reales con objetos de negocio fuertemente tipados, que pueden vincularse directamente a los objetos de negocio. Antes de pasar a comentar características de la interfaz de usuario, coméntanos qué novedades tenéis acerca de la seguridad. Hemos hecho todo lo que estaba en nuestras manos para mejorar este aspecto. Una de las novedades más interesantes es la forma en que funciona la pertenencia a grupos (membership) en esta versión. La manera en que se almacenan y transportan las credenciales, de forma encriptada, supone que ahora no hay forma de que un hacker pueda hacer ingeniería inversa de las contraseñas, o de los números de una tarjeta de crédito. Eso ha supuesto miles de líneas de código adicional para construir un sistema de alta seguridad suministrado por defecto. De la misma forma, ahora, por defecto, todo tiene lugar en un contexto de ejecución de confianza media y no de confianza total (FullTrust) como sucedía antes. << dnm.directo.entrevistas Ya que hablamos de la herramienta, hay una pregunta que todos nos hacíamos desde hace mucho tiempo. Por fin, parece que el editor de código HTML preserva la posición de las líneas editadas por el desarrollador sin cambiarlas aleatoriamente. La cuestión es: ¿Era eso tan difícil de hacer? Sin duda era una de las quejas que figuraban entre las 3 primeras solicitudes de cambio por parte de todos los desarrolladores. Eso ya está arreglado, como consecuencia de un trabajo intensivo, para permitir esta característica. No se trata de arreglar un bug sino de añadir algo nuevo. Para darte una idea de la dificultad, 3 desarrolladores han estado a tiempo completo trabajando esta capacidad durante un año y medio. Utiliza un sistema de triple búfer mucho más difícil de implementar de lo que parece a primera vista, sin que produzca colisiones con el resto del producto. Se ha hablado mucho de los casi 60 nuevos controles de servidor que van a estar disponibles para esta versión. Dentro de lo que llamamos los nuevos Servicios de Aplicación (Application Services), existe un paquete impresionante de nuevos controles. Por ejemplo, Login, permite el control de acceso, recuperación de contraseñas, registro de usuarios, etc. Después, tenemos que considerar los nuevos controles para datos. El GridView, que reemplaza al antiguo DataGrid, y que incluye la posibilidad de hacer todas las manipulaciones asociadas a un mantenimiento de forma automática y directa: “Añadir”, “Borrar”, “Modificar”, “Reordenar”, “Paginación”, etc. El control para las vistas de detalle (DetailsView), etc. Un conjunto muy completo para trabajar con la presentación y manipulación de datos. Además tenemos otro conjunto nuevo de controles para gestionar la navegación por páginas que se puede vincular con la estructura de un sitio Web, con facilidades nuevas para la construcción de TreeViews, Menús, etc. Y por último, todas las nuevas capacidades de personalización tanto de la apariencia, como del comportamiento funcional. ¿Y que hay de las nuevas Master Pages? Son una forma de facilitar la construcción de sitios basados en elementos fijos en la página principal (o en otras secciones de la Web), que incluyen variaciones en la zona central dependiendo de la selección del usuario. Y además, se obtiene soporte en tiempo de diseño gracias a Visual Studio, con una total personalización del aspecto para adecuarse a cualquier tipo de presentación. Y en todo momento, en la ventana de pre-visualización podemos ver cómo va a ser el resultado. Suministra un sistema fácil, robusto y que además, incluye muchas mejoras en el rendimiento. ¿Qué es exactamente Visual Web Developer 2005 Express Edition? Es parte del nuevo conjunto de productos que forma parte de la familia Visual Studio. Su propósito es ese gran número de estudiantes, aficionados y entusiastas que quieren conocer la tecnología sin entrar en las dificultades que conlleva una herramienta profesional. Aportan una forma de evaluar la tecnología como punto de entrada para valorar las posibilidades. Por eso, existen otras versiones similares para los otros lenguajes, como C++, C#, VB.NET y J#. ¿Va a existir alguna versión mejorada de WebMatrix? No. No va haber una nueva versión de WebMatrix. Y Visual Web Developer 2005 Express Edition será el sustituto de este producto, con un precio muy agresivo. Y una última cuestión acerca del rendimiento. La forma en que se cree a partir de ahora un nuevo sitio Web (máquina local, IIS, FTP o HTTP), ¿va a tener algún tipo de impacto en el tiempo de ejecución? El nuevo modelo de compilación implica una mínima penalización la primera vez que se solicita una página, siempre que ésta no esté precompilada. Pero es tan sencillo de evitar como realizar un paso de precompilación. De hecho, eso es lo que se va a recomendar oficialmente, que se aprovechen esas nuevas características, que mejorarán muchísimo el rendimiento respecto a la situación actual. <<dotNetManía El nuevo modelo de compilación implica una mínima penalización la primera vez que se solicita una página, siempre que ésta no esté precompilada. Pero es tan sencillo de evitar como realizar un paso de pre-compilación. De hecho, eso es lo que se va a recomendar oficialmente 13 dnm.asp.net Pepe Hevia Seguridad en los servicios Web Mucho se ha hablado de la seguridad de las páginas ASP.NET. Pero más importante aún es el hecho de asegurar nuestros servicios de negocio.Veremos como proteger nuestros servicios con modelos de autenticación / autorización y cómo escribir nuestros clientes para diseñar entornos distribuidos seguros. << Dos asuntos a tratar: Uno es el de la seguridad de los servicios Web integrada con Windows y su implicación en un entorno cliente/servidor;.y dos, el de poder extender a nuestro gusto, el mensaje de intercambio de datos SOAP para enriquecer lo que ya tenemos, o adaptarlo a nuestras necesidades, por ejemplo, la de la seguridad. En este artículo, pretendo enseñaros el cómo escribir aplicaciones cliente/servidor basadas en servicios Web seguros, empleando por un lado la autenticación de la que tanto se ha hablado en número anteriores y, por el otro, una herramienta muy potente del estándar WSDL/SOAP: la posibilidad de extender las cabeceras SOAP para extender el protocolo en base a nuestras necesidades, por ejemplo, montar un sistema de seguridad “extendido” para pasar algo más que simples credenciales. Seguridad integrada con Windows Pepe Hevia colabora habitualmente con dotNetManía. Es arquitecto de software senior en AlhambraEidos.Tiene las certificaciones de MCAD, MCSD.NET y MCT en .NET Framework. Ha escrito varios libros sobre la plataforma .NET (lalibreriadigital.com) Éste es el punto más sencillo. De artículos anteriores hemos sacado en claro que la seguridad integrada de IIS es extremadamente sencilla de configurar. Se basa en asegurar un directorio virtual empleando los mecanismos de autenticación, o bien emplear el descriptor XML, web.config, para sobrescribir el comportamiento del propio IIS cuando nos referimos a un contexto Web de aplicación determinado –con la ventaja de no tener que andar cacharreando con sus opciones–. Si realizamos un sencillo servicio que ofrezca un proceso de negocio simple como, por ejemplo, convertir de pesetas a euros, el reto será el de garantizar que sólo aquellos clientes autenticados correctamente, puedan acceder a las funciones del servicio. Para ello, realizaremos 3 sencillas operaciones: a) Crear el proyecto de un servicio Web –como ya seguro que a estas alturas hemos hecho cientos de veces– al que llamaremos ServiciosMonetarios. b) Crear un nuevo servicio Web en este proyecto al que denominaremos WSConversor. Su misión, ofrecer diversos mecanismos de conversión de divisas (ver fuente 1). Imports System.Web.Services <System.Web.Services.WebService(Namespace := _ "http://tempuri.org/ServiciosMonetarios/WSConversor")>_ Public Class WSConversor Inherits System.Web.Services.WebService <WebMethod()> Public Function ConvertirEuros(_ ByVal pesetas As Long) As Double Return pesetas / 166.386 End Function <WebMethod()> Public Function ConvertirPesetas(_ ByVal euros As Double) As Long Return Convert.ToInt64(Math.Round(euros * 166.386)) End Function End Class Fuente 1 c) Asegurar el servicio empleando el descriptor web.config de nuestro proyecto. Configurando la autenticación integrada de Windows, para evitar que nadie no autenticado, pueda consumir nuestros servicios (ver fuente 2). << dnm.asp.net <?xml version=”1.0” encoding=”utf-8” ?> <configuration> <system.web> <authentication mode=”Windows” /> <authorization> <deny users=”?” /> <!— Permitir a todos los usuarios —> </authorization> … </system.web> </configuration> Fuente 2 Si todo está bien configurado, veremos que el intento de probar el servicio desde el propio WebServices Explorer –que cariñosamente nos genera el propio IIS–, nos anima amablemente a autenticarnos con cuentas Windows, o bien a decirnos de una forma parca y sencilla que nos dediquemos a otras cosas, porque se nos ha denegado el acceso. ASP.NET en un navegador cuando a lo mejor, están empleando WinForms. Queda claro. Ahora viene la parte curiosa. La que nos lleva a consumir el servicio desde un cliente que no tiene por qué ser Web. Vamos a utilizar un cliente WinForm que nos ofrezca un interfaz sencillo y discreto a la par que funcional. Para ello crearemos un nuevo pro- Figura 2 Como podréis observar, la autenticación es extremadamente sencilla cuando empleamos los objetos ICredential de .NET. Acoplando las credenciales a nuestro consumidor, conseguiremos superar las barreras de seguridad y obtener nuestro preciado resultado. Como veis he optado por emplear la clase System.net.CredentialCache que nos proporciona la información de la cuenta activa del usuario. Pero si alguno necesita especificarlo de un modo más explícito, podéis usar la clase System.net.NetworkCredentials. figura 1 yecto y, tras una esmerada ventana como la que se ve en la figura 2, pasaremos a importar una referencia al servicio Web remoto. Tras autenticarnos y recoger el WSDL y haberse generado el correspondiente proxy SOAP, ya sólo nos queda implementar la funcionalidad de los eventos clic de los botones “A Euros” y “A Pesetas”, quedando tal y como se e en la figura 2 (ver también fuente 3). Figura 3 Para probar qué pasa si no tenéis permisos de acceso, siempre podéis denegar el acceso a todo quisqui desde el web.config o bien, especificar <<dotNetManía Es importante que tengamos en cuenta, que la única autenticación que no podemos usar de las ofrecidas por ASP.NET, es la que está basada en WebForms. Esto es lógico, pues estamos hablando de un modelo de servicios, en los que seguro no cabrá la posibilidad de abrir un WebForm, o mejor aún, como no sabemos qué tecnología o framework va a acceder al servicio, no podemos presumir de obligar al consumidor a usar 15 << dnm.asp.net Public Class FrmConversor Inherits System.Windows.Forms.Form (...) Private Sub btnAEuros_Click( ByVal sender As System.Object,_ ByVal e As System.EventArgs) _ Handles btnAEuros.Click Dim conversor As New localhost.WSConversor 'si queremos recoger la autenticacion actual del sistema conversor.Credentials = System.Net.CredentialCache.DefaultCredentials 'si queremos especificarla de modo explicito emplear 'la clase New NetworkCredentials("usuario","password"[,"dominio"]) lbResultado.Text = conversor.ConvertirEuros(Int64.Parse(txtCantidad.Text)) & " €" End Sub Private Sub BtnPesetas_Click( ByVal sender As System.Object,_ ByVal e As System.EventArgs) _ Handles BtnPesetas.Click Dim conversor As New localhost.WSConversor 'si queremos recoger la autenticacion actual del sistema conversor.Credentials = System.Net.CredentialCache.DefaultCredentials 'si queremos especificarla de modo explicito emplear 'la clase New NetworkCredentials("usuario","password"[,"dominio"]) lbResultado.Text = conversor.ConvertirPesetas(Double.Parse(txtCantidad.Text)) &" Pts" End Sub End Class <<dotNetManía Fuente 3 16 unas credenciales a piñón falsas. Al final, saltará una pedazo de Exception que dejará muy claras las cosas. Pero hasta aquí lo sencillo. Y seguro que lo conocido. Pero esto tiene un pequeño inconveniente: que nos limitamos a la seguridad de credenciales de Windows y nada más. Si queremos recoger más información acerca del usuario (grupo, máquina, rol, teléfono de la hermana, etc.) estaremos limitados a pasar dicha información como atributos del servicio (parámetros, para llevarnos bien). Y eso, no es una solución elegante. O bien comernos el tarro para integrarnos con un Active Directory, emplear los objetos de DirectoryServices de .NET y recoger toda la información de usuario de su cuenta AD. Pero esto, que es muy interesante –y que veremos en futuros artículos– ahora se nos sale de los límites de los objetivos y, en muchos casos, de las necesidades reales de un proyecto. Pero ahora viene una solución alternativa. Cabeceras SOAP personalizadas Como hemos podido ver, es extremadamente sencillo y transparente asegurar un servicio Web empleando la seguridad ASP.NET –y, por extensión, la de IIS–. Pero ¿qué ocurre si es requisito del proyecto el montar un sistema de seguridad de aplicación casero que no dependa de nadie más que de nosotros? El problema que se nos plantea es que sea el propio servicio Web el que valide la seguridad en su mismo código. Y para ello debemos establecer que algunos de sus parámetros especifiquen las credenciales. Pero eso no es del todo cómodo y complica las especificaciones de los servicios, ya que nos vemos “copiando/pegando” las definiciones en todas las llamadas. Pero hay una alternativa muy interesante… Porqué no hacer que sea el propio protocolo de comunicaciones de los servicios web (SOAP) el que propague unas credenciales que hemos diseñado nosotros. Y que luego cada servicio recoja del protocolo dichas credenciales, como si fueran una parte integrante más de la clase del servicio. Es decir, que se nos permite extender el esquema del mensaje SOAP para que agreguemos nuevas clases, que serán recuperadas por los mismos mecanismos que generan dicho SOAP: la serialización automática del propio servicio web. Muy práctico. La idea: diseñar un sistema de seguridad. Necesidad: diseñar la clase que representará el modelo de información de las credenciales de seguridad. Objetivo: integrar ese modelo de información en el mensaje SOAP e integrarlo con la lógica de negocio. La extensibilidad de SOAP en .NET es especialmente transparente. El modelo SOAP corresponde a los interfaces que expone el servicio. Es decir, los métodos que establece como públicos y bajo la directiva WebMethod. El serializador, pues, genera el esquema WSDL y de éste el mensaje SOAP en el momento de la acción. Pues bien, extender las cabeceras es un proceso similar. Agregamos un modelo de información a la clase del servicio Web (en cristiano, un nuevo tipo de clase), y la exponemos como extensión al mismo en los métodos que lo requieran (en cristiano, definimos una variable miembro que actuará de objeto ASP.NET nos ofrece muchos mecanismos de seguridad y control de privilegios que pueden ser reutilizados en los servicios web. La mayoría pasan por ajustar correctamente el archivo web.config y preparar los clientes para contemplar esta posibilidad << dnm.asp.net cuando se requiera). Para ello emplearemos la directiva SoapHeader en cada servicio Web. De esta manera, la nueva cabecera será serializada por el mismo proceso, evitándonos todo ese trabajo que implica. Agregando un nuevo servicio Web a nuestro proyecto, que he llamado WSConversorExtendido –bajo un nuevo proyecto de servicio Web, denominado ServiciosMonetariosEx–, mirar cómo queda el código, que es muy claro (fuente 4). La flexibilidad del modelo SOAP es tal que podremos adecuarlo a nuestro gusto definiendo cabeceras personalizadas, a modo de sencillas clases persistentes que se serializarán de modo automático Imports System.Web.Services Imports System.Web.Services.Protocols 'Modelo de informacion que extiende las cabeceras SOAP Public Class CredencialesSOAP Inherits SoapHeader Public Login As String Public Password As String Public Grupo As String End Class <System.Web.Services.WebService(Namespace:=_ "http://tempuri.org/ServiciosMonetariosEx/WSConversorExtendido")> Public Class WSConversorExtendido Inherits System.Web.Services.WebService 'Variable miembro serializa extension SOAP Public Credenciales As CredencialesSOAP <WebMethod(), SoapHeader("Credenciales")> _ Public Function ConvertirEuros(ByVal pesetas As Long) As Double If (Credenciales.Grupo = "AccesoEuros") Then Return pesetas / 166.386 Else Throw New Exception("Error: Acceso denegado a Paso a Euros") End If End Function <WebMethod(), SoapHeader("Credenciales")> _ Public Function ConvertirPesetas(ByVal euros As Double) As Long If (Credenciales.Grupo = "AccesoEuros") Then Return Convert.ToInt64(Math.Round(euros * 166.386)) Else Throw New Exception("Error: Acceso denegado a Paso a Pesetas") End If End Function End Class En nuestro ejemplo, en este nuevo caso, nos aseguraremos de permitir el acceso anónimo al servicio Web desde el motor ASP.NET o del IIS, ya que la responsabilidad de autenticación recae en el propio servicio. Aunque si alguien quiere reforzarlo, no tiene más que dejar la seguridad como lo especificamos en el ejemplo anterior. Pero sería redundante, ¿no?. Antes de continuar, aprovecho para hacer el inciso. Sería estupendo que accedierais al esquema WSDL del nuevo servicio. Para evitar que el editor me mate mostrando el WSDL, no lo pondremos aquí. Pero si os fijáis encontrareis cómo la serialización integra nuestra clase “cabecera SOAP” en todos los mensajes. Muy bonito y elegante. De hecho, es impresionante ver cómo nuestra única misión en el desarrollo ha sido establecer el formato de los datos (clase CredencialesSOAP) y qué servicio va a disponer de ella, empleando una sencilla directiva para proceder a usar el objeto; ya que se nos garantiza su instancia a través del propio SOAP del servicio (ver figura 4). El último paso es ver cómo quedaría la implementación del cliente. Aprovechando el mismo ejemplo anterior, en este caso la política no sería la de autenticarse con los objetos ICredential de .NET, sino aprovechar los mecanismos que la clase Proxy de nuestro nuevo Como podéis observar, es extremadamente fácil de implementar. En este sencillo ejemplo, me limitaré a comprobar un nombre de grupo para autenticar al usuario. Ni qué decir tiene que no es más que la punta del iceberg, ya que tenéis la posibilidad de utilizar estos datos a vuestro antojo (bases de datos, servicios de seguridad, integración con AD, etc.). figura 4 <<dotNetManía Fuente 4 17 << dnm.asp.net Public form1Extendido Inherits Form (…) Private Sub btnAEuros_Click( ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnAEuros.Click Dim conversor As New localhost.WSConversorExtendido Dim creds As New localhost.CredencialesSOAP creds.Login = "noseusa" creds.Password = "noseusa" creds.Grupo = txtGrupoSeguridad.Text conversor.CredencialesSOAPValue = creds Try lbResultado.Text = conversor.ConvertirEuros(Int64.Parse(txtCantidad.Text)) & " €" Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub Private Sub BtnPesetas_Click( ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnPesetas.Click Dim conversor As New localhost.WSConversorExtendido Dim creds As New localhost.CredencialesSOAP creds.Login = "noseusa" creds.Password = "noseusa" creds.Grupo = txtGrupoSeguridad.Text conversor.CredencialesSOAPValue = creds Try lbResultado.Text=conversor.ConvertirPesetas(Double.Parse(txtCantidad.Text))&" Pts" Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub Fuente 6 servicio nos proporciona a partir del nuevo esquema WSDL. El modelo del nuevo formulario quedará ahora, tras agregar un campo nuevo en el que pondremos el nombre del “grupo de seguridad”, como en las figuras 5. Figura 6 ha especificado un grupo distinto para cada operación de conversión –ver figura 6 y 7–). Conclusiones Hemos podido ver en este artículo lo sencillo y transparente que es diseñar servicios Web seguros. Bien sea a través de los servicios que nos ofrecen los propios sistemas de Microsoft, o bien a través de invenciones nuestras; lo que está claro es que se ha notado la preocupación del desarrollo de aplicaciones seguras. Y se demuestra que .NET de nuevo está al pie del cañón, ofreciendo multitud de potentes e interesantes alternativas. Deciros que las cabeceras SOAP no se limitan sólo a la seguridad de los servicios Web, sino a otros diseños más generales. Aprovechad esta potencia para poder especificar reglas de enrutado, extensión de ficheros añadidos al mensaje SOAP, información extra del servicio, y un largo etcétera. Ya sabéis en qué mecanismos se basan los nuevos WSE de Microsoft. Ya no tenéis excusa para no asegurar vuestros servicios. El código fuente lo tenéis disponible en la Web de la revista (http://www.dotnetmania.com) o en mi página Web personal ( http:www.heviatec.net). Para la prueba de la demo de Cabeceras SOAP, emplear para cada botón el grupo “AccesoEuros” o “AccesoPesetas”. Cualquier otra combinación os dará la excepción de error de seguridad. Muy chulo, hasta la próxima. <<dotNetManía Figura 5 18 El código –tras terminar las “referencias Web” oportunas– de los eventos clic puede verlo en el fuente 6. Y funcionando. Mirar qué pasa si lo hacemos bien o si nos equivocamos en el nombre del grupo oportuno (se Figura 7 dnm.ado.net Luis Miguel Blanco Manipulación de imágenes con ADO.NET Cualquier desarrollador que se haya acercado a la tecnología .NET, en su vertiente de acceso a datos, dominará la arquitectura de ADO.NET cuando se trata de crear un proceso de mantenimiento de datos típico, utilizando los tipos de datos habituales. Pero, ¿qué ocurre cuando entran las imágenes como parte del mantenimiento?, ¿cómo agregamos estos elementos a una gestión de datos? En el este artículo describimos los recursos que nos ofrece .NET,y las técnicas a nuestra disposición para integrar las imágenes como parte de los procesos de manipulación de datos. mantenimiento de datos Luis Miguel Blanco es redactor de dotNetManía. Es consultor en Alhambra-Eidos. Ha escrito varios libros y decenas de artículos sobre la plataforma .NET (lalibreriadigital.com) Cuando nos enfrentamos al desarrollo de un mantenimiento de datos típico en VB.NET contra un proveedor de datos ADO.NET, pongamos como caso SQL Server, todos sabemos, al realizar por ejemplo una inserción en una tabla, el modo de pasar los datos tradicionales a las columnas de la nueva fila, entendiendo como datos tradicionales los correspondientes a cadenas, números y fechas. Sin embargo, la cuestión ya no resulta tan obvia cuando entran en juego tipos de datos como las imágenes, que salen del marco de trabajo de los tipos habituales, por lo que la pregunta a formular sería la siguiente: ¿cómo integrar una imagen en nuestra gestión de datos? La solución más directa y sencilla pasaría por tener nuestras imágenes en archivos ubicados en una o varias rutas del servidor, y una tabla en la base de datos con un campo de tipo carácter, que contuviera la ruta del archivo. Esta forma de resolver el problema es ampliamente utilizada y resulta una excelente solución. No obstante, en este artículo, nos hemos propuesto complicarnos un poco la vida, y nuestra pretensión no es guardar una cadena con la ruta del archivo que contiene la imagen, sino el contenido de la propia imagen en la base de datos, lo que nos llevaría al siguiente interrogante: ¿cómo grabar una imagen en una base de datos? El tipo Image de SQL Server La pregunta que acabamos de plantear nos lleva a adentrarnos en la información que existe acerca de los tipos de datos en la documentación de SQL Server (sistema gestor de bases de datos que utilizaremos a lo largo del artículo), en donde encontramos un elemento específicamente diseñado para nuestros propósitos: el tipo Image, que consiste en un tipo de datos compuesto por información binaria de longitud variable, desde 0 hasta 231-1 (2.147.483.647) bytes. Llegados al punto actual ya sabemos dónde hemos de depositar el contenido de una imagen en una base de datos SQL Server, pero ahora surge una nueva incógnita: ¿cómo conseguirlo? ¿Qué es una imagen? Ciñéndonos a la problemática planteada sobre la manipulación de imágenes dentro de la plataforma .NET Framework, podemos definir una imagen como una secuencia de datos binarios o un array de bytes. Detengámonos especialmente en la definición que acabamos de dar: “secuencia de datos binarios”, y su correspondencia con los elementos que la plataforma .NET pone a nuestra disposición para su manejo. Dentro de la jerarquía de tipos de .NET, la clase que nos permite el trabajo con secuencias o flujos de datos es Stream, como clase abstracta, y todas las clases concretas que derivan de ella (FileStream, MemoryStream, etc.). Por lo tanto, si lo que necesita- <<dotNetManía << Manipulando una imagen dentro de un 19 << dnm.ado.net mos es extraer de un archivo gráfico la secuencia de bytes que contiene, abriremos el archivo con un objeto FileStream y volcaremos su contenido en un array de tipo Byte; este array será en última instancia lo que utilizaremos para grabar la imagen en la base de datos. <<dotNetManía El modo de gestión de los datos 20 ADO.NET es una tecnología que permite el tratamiento de la información bajo una filosofía conectada o desconectada de la fuente de datos con la que trabajemos, en función de las clases que empleemos para dichas tareas de mantenimiento. En este artículo abordaremos ambos modos de trabajo, de manera que el lector pueda tener una idea de cuál de los dos esquemas conviene mejor a sus necesidades, siendo incluso posible desarrollar un sistema que mezcle ambos. Como ejemplos ilustrativos de todas las operaciones de mantenimiento de datos a realizar con imágenes y objetos de ADO.NET, hemos desarrollado un proyecto que contiene todos los casos que iremos exponiendo a lo largo de este artículo. Dicho proyecto puede encontrarlo en los materiales de apoyo disponibles para este artículo en la dirección www.dotnetmania.com. El mencionado proyecto consiste en una aplicación Windows con interfaz MDI, que contendrá dos formularios hijos, uno para los ejemplos con operaciones en modo conectado, y el otro para los ejemplos con operaciones desconectadas. En el código de ambos deberemos importar los siguientes espacios de nombre: System.Data.SqlClient y System.IO, que nos permitirán utilizar los objetos de ADO.NET para el proveedor de SQL Server y los diferentes tipos de stream respectivamente. El objetivo de la aplicación consiste en almacenar y gestionar las fotos que tomamos con una cámara digital, y que tenemos repartidas en un numeroso conjunto de archivos. Para lo que crearemos una base de datos SQL Server que llamaremos InfoViajes, y que contendrá una tabla con el nombre Fotos y la estructura mostrada en la siguiente tabla. Campo Tipo IDFoto int Nombre varchar(50) Foto image Tabla 1 Una vez que hemos entrado en situación, comenzaremos con el formulario frmModoConectado, que usaremos para grabar un archivo gráfico en la base de datos, y posteriormente recuperar una de esas imágenes almacenadas. La siguiente figura muestra una porción de este formulario. Insertar imágenes en una base de datos utilizando un objeto Command Tras introducir un número como identificador de foto en la caja de texto del formulario, pulsaremos su botón “Grabar imagen en BD”, que ejecutará el código mostrado en el fuente 1. El proceso que llevamos a cabo al ejecutar este código es el siguiente: tras pasar las oportunas validaciones que comprueban si hemos tecleado un código para la imagen a grabar, abrimos un control OpenFileDialog que nos facilita la tarea de seleccionar el archivo gráfico. Una vez elegido éste, lo abriremos haciendo uso de una secuencia representada mediante un objeto FileStream, y dimensionaremos un array de bytes con un tamaño igual a la longitud del stream. Seguidamente leeremos el contenido del stream y lo volcaremos en el array, con lo que ya tendremos la información binaria de la imagen en un formato que nos permita tratarlo para su inserción en la base de datos. A continuación, construiremos la conexión, el comando, y la cadena con la sentencia SQL a enviar a la base de datos. También definiremos tantos objetos Parameter como campos a grabar; en el caso concreto de la imagen, observemos que el parámetro lo creamos especificando como tipo de dato SqlDbType.Image, correspondiente a la enumeración que nos devuelve los tipos disponibles figura 1 para SQL Server; el valor que pasamos al parámetro será el array de bytes que contiene la información binaria de la imagen. Por último, abrimos la conexión, ejecutamos el comando y cerramos la conexión. Si todo ha funcionado correctamente, tendremos la imagen grabada en un registro de la tabla. Para comprobarlo podemos ejecutar una consulta hacia dicha tabla desde el Analizador de consultas de SQL Server, obteniendo un resultado como el que vemos en la figura 2. figura 2 << dnm.ado.net Private Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Sub btnGrabar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGrabar.Click nIDFoto As Integer oFileStream As FileStream xDlgResult As DialogResult aBytImagen() As Byte cnConexion As SqlConnection cmdComando As SqlCommand parIDFoto As SqlParameter parNombre As SqlParameter parFoto As SqlParameter sSQL As String oFileInfo As FileInfo ‘ validaciones If Me.txtIDFoto.Text = String.Empty Then MessageBox.Show(“Introducir identificador de foto”) Return End If Try nIDFoto = Integer.Parse(Me.txtIDFoto.Text) Catch oException As Exception Me.txtIDFoto.Text = String.Empty MessageBox.Show(“El identificador debe ser numérico”) Return End Try ‘ cuadro de diálogo para seleccionar archivo Me.dlgAbrirArchivo.InitialDirectory = Application.StartupPath & “\ArchivosFotos” xDlgResult = Me.dlgAbrirArchivo.ShowDialog() If xDlgResult = DialogResult.OK Then ‘ abrir el archivo con un objeto stream oFileStream = New FileStream(Me.dlgAbrirArchivo.FileName, FileMode.Open) ‘ crear un array byte que tenga el tamaño del archivo aBytImagen = New Byte(oFileStream.Length - 1) {} ‘ leer con el stream el contenido del archivo ‘ y volcarlo al array oFileStream.Read(aBytImagen, 0, oFileStream.Length - 1) oFileStream.Close() oFileInfo = New FileInfo(Me.dlgAbrirArchivo.FileName) sSQL = “INSERT INTO Fotos VALUES (@IDFoto, @Nombre, @Foto)” cnConexion = New SqlConnection(“data source=localhost;initial catalog=InfoViajes;uid=sa;pwd=’’;”) cmdComando = New SqlCommand cmdComando.Connection = cnConexion cmdComando.CommandType = CommandType.Text cmdComando.CommandText = sSQL parIDFoto = New SqlParameter(“@IDFoto”, SqlDbType.Int) parIDFoto.Value = Convert.ToInt32(Me.txtIDFoto.Text) cmdComando.Parameters.Add(parIDFoto) parNombre = New SqlParameter(“@Nombre”, SqlDbType.VarChar, 50) parNombre.Value = oFileInfo.Name cmdComando.Parameters.Add(parNombre) ‘ para pasar la imagen a la base de datos ‘ definimos un parámetro de tipo Image ‘ y le pasamos el array byte que contiene ‘ la información binaria de la imagen parFoto = New SqlParameter(“@Foto”, SqlDbType.Image) parFoto.Value = aBytImagen cmdComando.Parameters.Add(parFoto) MessageBox.Show(“Imagen grabada en la base de datos”) End If End Sub Fuente 1 <<dotNetManía cnConexion.Open() cmdComando.ExecuteNonQuery() cnConexion.Close() 21 << dnm.ado.net Obviamente no veremos en el campo Foto la imagen tal y como estamos acostumbrados, sino como una sucesión de datos binarios, pero que son suficientemente indicativos de que el contenido del archivo ha sido grabado. ¿Y si utilizamos el proveedor de OLEDB? Aunque este artículo está orientado hacia uso del proveedor de datos para SQL Server, si el lector se encuentra ante la necesidad de gestionar imágenes residentes en un origen de datos para el que deba de usar el proveedor de OLEDB, la mecánica a seguir sería la misma que acabamos de explicar, utilizando como es natural, los objetos de ADO.NET específicos para este proveedor de datos: OleDbConnection, OleDbCommand, OleDbParameter, etc. Veamos en el fuente 2 una adaptación del ejemplo anterior pero usando el proveedor de OLEDB. '.... 'apertura archivo con FileStream y volcado a array byte '.... sSQL = _ "INSERT INTO Fotos VALUES (@IDFoto, @Nombre, @Foto)" cnConexion = New OleDbConnection(_ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=” &_ ”C:\Pruebas\InfoViajes.mdb;Password=;") parIDFoto = New OleDbParameter("@IDFoto", _ OleDbType.Integer) parIDFoto.Value = 5 cmdComando.Parameters.Add(parIDFoto) Try nIDFoto = Integer.Parse(Me.txtIDFoto.Text) Catch oException As Exception Me.txtIDFoto.Text = String.Empty MessageBox.Show(“El identificador debe ser numérico”) Return End Try ‘ crear sentencia, conexión y comando para obtener la ‘ imagen de la base de datos sSQL = “SELECT Nombre, Foto FROM Fotos WHERE IDFoto = “ & _ Me.txtIDFoto.Text cnConexion = New SqlConnection(_ “data source=localhost;initial catalog=InfoViajes;uid=sa;pwd=’’;”) cmdComando = New SqlCommand cmdComando.Connection = cnConexion cmdComando.CommandType = CommandType.Text cmdComando.CommandText = sSQL drFotos.Close() cnConexion.Close() If IsNothing(sNombreFoto) Then MessageBox.Show(“No hay foto con ese identificador”) Return End If parNombre = New OleDbParameter("@Nombre", _ OleDbType.VarChar, 50) parNombre.Value = "karnak1.jpg" cmdComando.Parameters.Add(parNombre) parFoto = New OleDbParameter("@Foto", _ OleDbType.LongVarBinary) parFoto.Value = aBytImagen cmdComando.Parameters.Add(parFoto) cnConexion.Open() cmdComando.ExecuteNonQuery() cnConexion.Close() Fuente 2 Recuperar imágenes desde una base de datos mediante los objetos Command y DataReader <<dotNetManía ‘ validaciones If Me.txtIDFoto.Text = String.Empty Then MessageBox.Show(“Introducir identificador de foto”) Return End If cnConexion.Open() drFotos = cmdComando.ExecuteReader(CommandBehavior.SingleRow) If drFotos.Read() Then sNombreFoto = drFotos(“Nombre”) ‘ recuperar datos binarios de la foto aBytImagen = drFotos(“Foto”) End If cmdComando = New OleDbCommand cmdComando.Connection = cnConexion cmdComando.CommandType = CommandType.Text cmdComando.CommandText = sSQL 22 Private Sub btnRecuperar_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnRecuperar.Click Dim nIDFoto As Integer Dim xDlgResult As DialogResult Dim sSQL As String Dim cnConexion As SqlConnection Dim cmdComando As SqlCommand Dim drFotos As SqlDataReader Dim sNombreFoto As String Dim aBytImagen() As Byte Dim oFileStream As FileStream Dim oMemoryStream As MemoryStream Dim bmpImagen As Bitmap Una vez que hemos grabado varias imágenes en la base de datos, vamos a efectuar la operación inversa, por lo que tras introducir un número identifica- ‘ abrir cuadro de diálogo para grabar la foto en un archivo Me.dlgGrabarArchivo.InitialDirectory = Application.StartupPath & _ “\FotosRecuperadasBD\” Me.dlgGrabarArchivo.FileName = sNombreFoto xDlgResult = Me.dlgGrabarArchivo.ShowDialog() If xDlgResult = DialogResult.OK Then ‘ crear un objeto stream de tipo archivo y escribir en él ‘ el array byte que contiene los datos binarios de la imagen oFileStream = New FileStream(Me.dlgGrabarArchivo.FileName, _ FileMode.CreateNew, FileAccess.Write) oFileStream.Write(aBytImagen, 0, aBytImagen.Length) oFileStream.Close() End If ‘ crear un objeto stream en memoria conteniendo los datos ‘ de la imagen, crear un bitmap con el stream y ‘ visualizar la imagen en un control PictureBox oMemoryStream = New MemoryStream(aBytImagen) bmpImagen = New Bitmap(oMemoryStream) Me.picFoto.Image = bmpImagen End Sub Fuente 3 << dnm.ado.net dor de foto en la caja de texto del formulario, pulsaremos su botón “Recuperar imagen de BD”, que ejecutará el código fuente 3. En esta ocasión, el proceso ejecutado consiste en crear una conexión y comando, cuya sentencia SQL contenga el identificador de fila a recuperar, que habremos introducido en el formulario. Observe el lector que al crear el comando, lo configuramos utilizando la enumeración CommandBehavior para optimizar su ejecución, de modo que devuelva un DataReader compuesto por una única fila. Una vez obtenido el DataReader, su campo Foto contendrá una serie de bytes que representan los datos binarios de la imagen, y que volcaremos en un array, también de tipo byte. A partir de aquí podemos tratar la imagen como necesitemos; en este ejemplo realizamos dos operaciones: en primer lugar pasamos el array a un objeto FileStream, creando de esta manera un archivo gráfico, ayudándonos de un cuadro de diálogo SaveFileDialog; en segundo lugar creamos un objeto MemoryStream con el array, y a partir del stream, creamos un objeto Bitmap, que asignamos al control PictureBox del formulario para visualizar la imagen; si no necesitamos transferir la imagen a un archivo, es mucho más efectivo tratarla en memoria con el stream disponible a tal efecto. La siguiente figura muestra este formulario en ejecución. [ Para poder grabar una imagen a un campo de la base de datos, debemos pasar primeramente dicha imagen a un array de tipo byte ] objeto en función de su natural operativa (conectada o desconectada). El ejemplo con DataSet que acompañamos a continuación, además de las operaciones de edición de imágenes, contiene la capacidad de navegación por las filas de la tabla, constituyendo un factor adicional que le diferencia del anterior ejemplo basado en comandos; el resultado conseguido consiste en un visualizador de imágenes, que aunque sencillo, cumple correctamente su misión. El formulario de ejemplo encargado de tratar con el DataSet es frmDesconectado, que podemos ver en la figura 4. figura 4 Llegados a este punto, podemos dar por concluida la fase de gestión de imágenes en ADO.NET desde un enfoque conectado a la fuente de datos; es hora pues de abordar una técnica diferente: el modo desconectado. Manipulando imágenes mediante un DataSet DataSet representa el objeto central sobre el cual se sustenta la arquitectura de gestión de datos desconectados en ADO.NET. La forma de tratar las imágenes es igual que con los objetos Command y DataReader, salvando claro está, las diferencias entre estos tipos de [ Al igual que para el resto de tipos de datos, podemos abordar el mantenimiento de imágenes con ADO.NET tanto desde una filosofía conectada como desconectada del origen físico de los datos. ] <<dotNetManía figura 3 En este artículo asumimos que el lector conoce la mecánica básica para crear y llenar un DataSet con datos, por lo que obviaremos aquellas líneas de código pertenecientes a este formulario encargadas de esta tarea y de la navegación de registros, centrándonos exclusivamente en los procesos que competen al tratamiento de imágenes con este objeto. Debemos aclarar también, que para facilitar la programación de las operaciones en el formulario, algunos elementos tales como el DataSet, las variables de control de la posi- 23 << dnm.ado.net ción de fila, filas totales, etc., han sido declarados con ámbito a nivel de la clase. Tras poblar el DataSet con el contenido de la tabla Fotos en el evento Load del formulario, llamaremos al método CargarDatos, que toma la fila en la que el DataSet se encuentra posicionado, y la muestra en los controles del modo que vemos en el código fuente 4. Private Dim Dim Dim Dim Sub CargarDatos() drFila As DataRow aBytImagen() As Byte bmpImagen As Bitmap oMemoryStream As MemoryStream ‘ obtener el objeto DataRow de la fila actual, ‘ y recuperar los valores de sus campos drFila = Me.dsInfoViajes.Tables(_ “Fotos”).Rows(nFilaActual) Me.txtIDFoto.Text = CType(drFila(“IDFoto”), String) Me.txtNombre.Text = CType(drFila(“Nombre”), String) ‘ el campo de imagen consiste en información ‘ binaria (bytes), volcarlo a un array de tipo byte aBytImagen = CType(drFila(“Foto”), Byte()) ‘ crear un objeto stream en memoria a partir ‘ del array byte oMemoryStream = New MemoryStream(aBytImagen) ‘ crear un objeto bitmap a partir del stream bmpImagen = New Bitmap(oMemoryStream) ‘ pasar el objeto bitmap al control PictureBox ‘ del formulario Me.picFoto.Image = bmpImagen oMemoryStream.Close() End Sub Fuente 4 Como acabamos de comprobar, la operación realizada para obtener la imagen es la misma que para un DataReader: pasamos el contenido del campo de la tabla a un array de tipo byte, y utilizamos éste para crear un stream en memoria; finalmente, creamos un bitmap con el stream, y asignamos la imagen a un control PictureBox. A partir de aquí, al pulsar los botones de navegación, se actualizará el número de fila de la tabla del DataSet a mostrar, volviendo a llamar a este mismo método para visualizar la imagen, como vemos en la siguiente figura: del formulario. Después de teclear el número identificador y la ruta del archivo, pulsaremos el botón “Grabar”, que ejecutará el siguiente código fuente: Private Sub btnGrabar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGrabar.Click Dim oFileStream As FileStream Dim aBytImagen() As Byte Dim drFila As DataRow ‘ abrir el archivo con un stream y volcar en un array oFileStream = New FileStream(Me.txtArchivo.Text, FileMode.Open) aBytImagen = New Byte(oFileStream.Length - 1) {} oFileStream.Read(aBytImagen, 0, oFileStream.Length - 1) oFileStream.Close() ‘ crear un objeto DataRow, asignar valores a sus campos ‘ y añadirlo al dataset drFila = dsInfoViajes.Tables(“Fotos”).NewRow() drFila(“IDFoto”) = Integer.Parse(Me.txtIDFoto.Text) drFila(“Nombre”) = Path.GetFileName(Me.txtArchivo.Text) drFila(“Foto”) = aBytImagen dsInfoViajes.Tables(“Fotos”).Rows.Add(drFila) ‘ actualizar el dataset en la base de datos ‘ y volverlo a llenar para que el orden de las ‘ filas sea el adecuado cnConexion.Open() daAdaptador.Update(dsInfoViajes, “Fotos”) dsInfoViajes.Clear() daAdaptador.Fill(dsInfoViajes, “Fotos”) cnConexion.Close() ‘ actualizar las variables de control del número de filas nFilasTotales = Me.dsInfoViajes.Tables(“Fotos”).Rows.Count - 1 nFilaActual = nFilasTotales Me.ControlesNavegar() Me.CargarDatos() End Sub Fuente 5 La manera de grabar el archivo gráfico sobre la base de datos es, en esencia, la misma que en el ejemplo con objetos conectados, ya que abrimos el archivo con un FileStream, y volcamos éste a un array de bytes; a continuación creamos un nuevo objeto DataRow y asignamos los valores para sus campos, añadiendo este objeto a la colección de filas de la tabla del DataSet. Finalmente actualizaremos la base de datos física con el DataSet y rellenaremos este último de nuevo, para que el orden de filas sea el correcto durante la navegación de registros por el formulario. Y llegamos al final <<dotNetManía figura 5 24 En lo que respecta a la grabación de un archivo gráfico empleando este DataSet, pulsaremos el botón “Nuevo” para habilitar los controles correspondientes En efecto estimado lector, por mucho que nos pese toda narración tiene su fin, y nuestro artículo no iba a ser menos, esperamos que el tema tratado en esta ocasión le sea de utilidad si se encuentra ante la tesitura de crear un mantenimiento de datos en el que intervengan imágenes. dnm.plataforma.net Rodrigo Corral Sistemas distribuidos en .NET con Remoting (y III) Último artículo sobre Sistemas distribuidos en .NET con Remoting. Una vez visto cómo configurar las opciones de los objetos, los canales que usaremos y la ubicación de éstos mediante RemotingConfiguration, veremos cómo configurar .NET Remoting mediante archivos. Finalmente veremos cómo utilizar un servicio o IIS como host en lugar de una aplicación de consola como hemos venido haciendo hasta ahora. Hasta ahora hemos estado configurando las opciones de comportamiento de nuestros objetos, los canales que vamos a usar y la ubicación de los objetos remotos mediante la llamada en el código a funciones de la clase RemotingConfiguration. Este enfoque nos obliga a cambiar el código y recompilar siempre que cambiemos alguno de estos parámetros, lo que resta flexibilidad a la solución. .NET Remoting permite la configuración mediante archivos, lo que nos permite una total flexibilidad a la hora de desplegar nuestra aplicación. Los archivos de configuración de .NET Remoting tienen formato XML. Podemos usar un archivo de configuración para el servidor (host) y otro archivo de configuración para el cliente. Como todos los archivos de configuración de .NET, los archivos de configuración de .NET Remoting comienzan con la etiqueta <configuration>. A continuación se muestran los elementos básicos que pueden aparecer en un archivo de configuración: Rodrigo Corral colabora habitualmente con dotNetManía. Es Microsoft MVP y analista de Sisteplant, empresa líder en el sector de las aplicaciones de gestión industrial <configuration> <system.runtime.remoting> <application> <lifetime /> <chanels /> <service /> <client /> <application /> <system.runtime.remoting/> <configuration /> <lifetime>: Configurando el tiempo de vida Usaremos la etiqueta <lifetime> para configurar las propiedades por defecto de tiempo de vida de nuestro objeto. Para modificar estas propiedades usaremos los atributos de la etiqueta <lifetime> que describimos a continuación: ATRIBUTO DESCRIPCIÓN LeaseTime Indica el tiempo de vida por defecto de nuestro objeto. El valor por defecto es 5 minutos. sponsorshipTimeout Es el tiempo que tiene un sponsor para responder antes de que sea descartado. El valor por defecto es 2 minutos. RenewOnCallTime Es el tiempo que se añade cada vez que un método del objeto es llamado. El valor por defecto es 2 minutos. Es el intervalo de tiempo que transcurrirá entre comprobaleaseManagerPoolTime ciones del tiempo de vida del objeto. El valor por defecto es 10 segundos. Tabla 1 Todos los atributos de la etiqueta <lifetime> son opcionales. Para expresar las unidades de tiempo usaremos, “D” para días, “H” para horas, “M” para minutos, “S” para segundos y “MS” para milisegundos. <<dotNetManía << Configurar .NET Remoting mediante archivos 25 << dnm.plataforma.net Ejemplo de la configuración de objetos con un tiempo de vida exagerado: <lifetime leaseTime = “100D” sponsorshipTimeout = “1D” renewOnCallTime = “1D” /> ATRIBUTO DESCRIPCIÓN Ref Referencia a un canal predefinido (“tcp” o “http”) o a un canal de usuario definido en un archivo de configuración. DisplayName Nombre con el que se mostrará el canal en la herramienta de configuración de .NET Framework. Type Este atributo es obligatorio. Contiene el tipo exacto (namespace, nombre de clase, asemmbly) que corresponde a la implementación del canal. Port Es el puerto que se usará para escuchar en la parte servidora. Si queremos permitir que nuestros clientes puedan recibir callbacks debemos especificar como puerto el 0 en la parte cliente. Name Nombre del canal; por defecto es “http”, cuando registramos varios canales este nombre tiene que ser único o “”. Priority Es un valor numérico entero. Cuanto mayor sea, mayores posibilidades hay de que sea éste, el canal usado para la comunicación. clientConnectionLimit Número de conexiones simultáneas que se pueden abrir con un determinado servidor. Por defecto es 2. ProxyName Nombre del servidor proxy. ProxyPort Puertos del servidor proxy. UseIPAddress Especifica que se usará la dirección IP en lugar del nombre del host. Puede ser “true” o “false”. El valor por defecto es “true”. BindTo Dirección IP por la que escuchará el servidor; se usa en equipo con más de una red. MachineName Nombre del equipo host. Si se especifica se ignora el valor de useIPAdrress. rejectRemoteRequests Si se establece a “true”, el canal sólo responderá a peticiones desde la máquina local. Sólo se aplica a canales TCP. <channels>: Configurando los canales de comunicación La etiqueta <channels> contrendrá información sobre uno o más canales; sólo sirve como contenedor de etiquetas <channel> y no tiene atributos asociados a ella. La etiqueta <channel> nos permite especificar las características del canal, como el puerto de escucha y el tipo de canal. En el cliente no es necesario especificar los canales si usamos los estándares (HTTP o TCP), porque estos canales son registrados por defecto por el framework. En el servidor debemos especificar al menos un puerto en el que escuchará el canal. En la tabla 2 aparece una descripción de los atributos que pueden acompañar a la etiquete <channel>. Dentro de cada etiqueta <channel> podemos especificar el formateador que deseamos utilizar. A continuación se muestra un ejemplo de un canal HTTP usando el formateador binario: <channels> <channel ref=”http” port=1234> <serverProviders> <formatter ref=”binary” /> <serverProviders/> <channel/> <channels/> En la parte cliente será necesario especificar: <channels> <channel ref=”http” port=1234> <clientProviders> <formatter ref=”binary” /> <clientProviders/> <channel/> <channels/> <<dotNetManía <service>: Configurando el com- 26 portamiento de los objetos El contenido de la etiqueta <service> permite registrar los objetos que nuestro Tabla 2 host va a exponer, así como especificar el comportamiento de estos objetos. La estructura de esta entrada es: <service> <wellknown/> <activated/> <service/> Usaremos la etiqueta <wellknown> en el archivo de configuración de nuestro servidor cuando queramos configurar nuestro objeto como SAO. La etiqueta <wellknown> es equivalente a la llamada RemotingConfiguration.RegisterWellKno wnServiceType(). En la tabla 3 describimos los atributos que se pueden aplicar a la etiqueta <wellknown>. A continuación se muestra un ejemplo de cómo configurar un objeto: <service> <wellknown mode=”Singleton” type=“namespace.classname,assembly” objectUri=”NuestraUri” /> <service/> << dnm.plataforma.net ATRIBUTO DESCRIPCIÓN type Información sobre la clase que queremos publicar como remota en la forma “namespace.classname,assembly” mode Indica el modo de funcionamiento del objeto. Puede ser “ingleton” o “SingleCall” objectUri Especifica el ubicación del objeto. Cuando el objeto se aloja en IIS el uri debe teminar en .soap o .rem para que sea correctamente procesado. displayName Atributo opcional que especifica el nombre con el que aparecerá el objeto en las herramientas de configuración del .NET Framework Tabla 3 Usaremos la etiqueta <activated> cuando queramos configurar nuestro objeto como CAO. Como en el caso de los objetos CAO, la URI del objeto está determinada por el nombre de aplicación. El único atributo que tenemos que especificar es el tipo del objeto remoto que queremos publicar. ATRIBUTO DESCRIPCIÓN type Información sobre la clase que queremos publicar como remota en la forma “namespace.classname,assembly” ATRIBUTO DESCRIPCIÓN url La url del servidor, es obligatoria si usamos objetos CAO displayName Atributo opcional que especifica el nombre con el que aparecerá el objeto en las herramientas de configuración del .NET Framework Tabla 5 La etiqueta <wellknown> se usa para registrar los objetos SAO en el cliente y así poder usar el operador new para instanciar objetos remotos. Los atributos de la etiqueta <wellknown> son equivalentes a los parámetros de la función Activator.GetObject(). A continuación describimos los atributos que se pueden aplicar a la etiqueta <wellknown> (tabla 6): ATRIBUTO DESCRIPCIÓN url La URL completa al objeto remoto type Información sobre la clase que queremos utilizar como remota en la forma "namespace.classname,assembly" displayName Atributo opcional que especifica el nombre con el que aparecerá el objeto en las herramientas de configuración del .NET Framework Tabla 6 Tabla 4 <service> <activated type=“namespace.classname,assembly” /> <service/> <client>: Configurando el cliente La etiqueta <client> es el equivalente a la etiqueta <service> en el lado cliente. La estructura de esta entrada es: <client> <wellknown/> <activated/> <client/> Cuando usamos objetos CAO, debemos especificar en la etiqueta <client> la ubicación (URL) del servidor, que será común para todas las entradas <activated>. A continuación describimos los atributos de la etiqueta <client> (tabla 5): A continuación se muestra un ejemplo de cómo configurar un objeto SAO para que podamos utilizar el operador new: <client> <wellknown url=”tcp://hosname:1234/object.tcp” type=“namespace.classname,assembly” /> <client/> La etiqueta <activated> es el equivalente en el cliente a la etiqueta <activated> en la parte servidora. Se utiliza para registrar objetos CAO. Como la ruta al servidor ya se ha especificado en el atributo url de la etiqueta <client>, el único atributo a especificar es el tipo del objeto remoto. Los datos de esta etiqueta nos van a permitir usar el operador new para instanciar objetos CAO (tabla 7). ATRIBUTO DESCRIPCIÓN type Información sobre la clase que queremos utilizar como remota en la forma “namespace.classname,assembly” Tabla 7 <<dotNetManía A continuación se muestra un ejemplo de cómo configurar un objeto CAO: 27 << dnm.plataforma.net A continuación se muestra un ejemplo de cómo configurar un objeto CAO para poder usar el operador new: • Los objetos remotos corren en el contexto de seguridad del usuario que ha iniciado sesión. <client> <activated type=“namespace.classname,assembly” /> <client/> Todos estos problemas se pueden evitar utilizando un servicio como host. Ahora que sabemos utilizar archivos de tos remotos con sólo cambiar el archivo de configuración. Ejemplo: ServiceHostSample1 Utilizar IIS como host de nuestros objetos remotos Desplegar nuestros objetos con IIS como host es realmente sencillo. Los pasos a seguir son: 1) Crear un nuevo directorio virtual en IIS. 2) Copiar el assembly del objeto en el subdirectorio BIN del directorio virtual. 3) Poner la configuración de los objetos en el archivo web.config en el directorio virtual raíz de nuestra aplicación. private void frmMain_Load(object sender, System.EventArgs e) { RemotingConfiguration.Configure(“RCSClient.exe.config”); m_RCSObj = new RCSObject(); m_RCSObj2 = new RCSObject2(); //Aseguramos que sea un objeto remoto System.Diagnostics.Debug.Assert( RemotingServices.IsTransparentProxy(m_RCSObj)); } Fuente 1 Cargar la configuración desde el archivo Una vez escritos los archivos de configuración, tanto del cliente como del servidor, será necesario cargarlos. Esto se consigue con una simple llamada a la función RemotingConfiguration.Configure() que toma como parámetro la ruta del archivo de configuración (fuente 1). Ejemplo: RemotingConfigSample1 <<dotNetManía Utilizar un servicio como host de nuestros objetos remotos 28 Hasta ahora hemos utilizado aplicaciones de consola como host de nuestros servicios; esto no es ningún problema, e incluso es el enfoque adecuado para depurar la aplicación, pero tiene algunas limitaciones: • Hemos de arrancar “a mano” la aplicación que expone nuestros objetos. • La aplicación aparece en el escritorio. • Cualquiera puede cerrar la aplicación. • Cualquiera puede arrancar la aplicación. • Para que la aplicación funcione un usuario debe iniciar una sesión. 1 configuración, alojar nuestros objetos en un servicio será algo tan simple como puede verse en el fuente 2. Si elegimos usar IIS como host debemos conocer algunas cosas: • En el archivo web.config no se puede usar la opción <debug>. /// <summary> /// Cuando iniciamos el servicio, cargamos la configuración de los objetos remotos. /// </summary> protected override void OnStart(string[] args) { RemotingConfiguration.Configure( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); } Fuente 2 El archivo de configuración contiene las definiciones de los objetos que vamos a exponer desde el servicio (fuente 3). • Sólo podemos utilizar el canal HTTP. • El protocolo siempre será SOAP. <configuration> <system.runtime.remoting> <application> <lifetime leaseTime=”1M” sponsorshipTimeout=”1M” renewOnCallTime=”1M” /> <channels> <channel ref=”http” port=”1234” /> <channel ref=”tcp” port=”1235” /> </channels> <service> <wellknown mode=”Singleton” type=”RCSObjects.RCSObject, RCSObjects” objectUri=”RCSObject.rem” /> </service> </application> </system.runtime.remoting> </configuration> Fuente 3 Un mismo servicio nos servirá siempre que queramos exponer obje- Una de las ventajas es que podemos usar la autenticación IIS y el protocolo SSL. Los ejemplos pueden descargarse del material de apoyo de este artículo en http://www.dotnetmania.com/Articulos/014 dnm.plataforma.net José Miguel Torres Interoperabilidad no administrada y migración (y III) En esta última parte describiremos cómo llamar a funciones externas con un ejemplo a una API de Windows.También describiremos la utilización de TypeLibConverter, en el proceso de exportación e importación de la biblioteca de tipos, clave para llevar acabo la interoperabilidad COM. << Interoperabilidad con funciones no administradas Hasta ahora hemos visto un tipo de interoperabilidad concreto: entre componentes COM y .NET, lo que es conocido como COM Interop. A continuación veremos la interoperabilidad con métodos de librerías de vínculos dinámicos DLL. En versiones de Visual Basic 6.0 hacíamos uso de este tipo de programación, por ejemplo, al llamar a ciertas APIs de Windows, incluso Visual Basic venía con un importador de APIs las cuales declarábamos y utilizábamos en nuestros proyectos. Con .NET, muchas funciones que sólo encontrábamos en las APIs de Windows están disponibles sin necesidad de hacer llamadas externas, como por ejemplo la manipulación de procesos en System.Threading. Para las funciones que no se encuentran en BCL, .NET ofrece un servicio llamado invocación de plataforma que encontraremos a menudo como P/Invoke. Dicho servicio se basa en los metadatos para el cálculo de referencias estándar y llamar a dichas funciones. P/Invoke localiza el archivo DLL que nosotros le hemos indicado desde código. Como veremos más adelante no es necesario indicarle la ubicación abso- DWORD RasEnumConnections( LPRASCONN lprasconn, LPDWORD lpcb, LPDWORD lpcConnections ); Fuente 1 Figura 1. Estructura de directorios y entrada del Registro para implementación privada. En la misma página nos muestra la definición de los parámetros, así que podemos saber la utilidad y observaciones de cada uno. La estructura RASCONN la obtenemos del citado MSDN Library (ver fuente 2). <<dotNetManía José Miguel Torres colabora habitualmente con dotNetManía. Es técnico superior en desarrollo de aplicaciones informáticas y trabaja como arquitecto de software en el departamento de tecnologías de la información de MRW luta. Cuando localiza el fichero DLL lo carga en memoria e inserta sus parámetros en la pila realizando los cálculos de referencia necesarios y es la función no administrada la que se ejecuta. Crearemos un ejemplo y podremos ver cómo implementar una llamada a una función externa desde código .NET. Para el ejemplo utilizaremos una llamada a la función RasEnumConnections del archivo RASAPI32.DLL. Dicha función devolverá todas las conexiones RAS (Remote Access Service) abiertas. Existen cantidad de funciones en KERNEL32.DLL , USER32.DLL o GDI32.DLL más fáciles de implementar, pero con ésta veremos un ejemplo algo más amplio, como el paso por referencia o paso de estructuras y, aunque a priori no es tan simple, será un ejemplo más provechoso. En primer lugar, deberemos obtener la definición de dicha función. En el MSDN Library podemos encontrarla pero con el inconveniente que muestra la definición como prototipo C++. La definición que nos muestra es la siguiente: 29 << dnm.plataforma.net typedef struct _RASCONN { DWORD dwSize; HRASCONN hrasconn; TCHAR szEntryName[RAS_MaxEntryName + 1]; #if (WINVER >= 0x400) TCHAR szDeviceType[ RAS_MaxDeviceType + 1 ]; TCHAR szDeviceName[ RAS_MaxDeviceName + 1 ]; #endif #if (WINVER TCHAR DWORD #endif #if (WINVER GUID #endif #if (WINVER DWORD LUID #endif } RASCONN ; Campo BestFitMapping Desactiva las mejores asignaciones. CallingConvention Especifica las convenciones de llamada utilizadas al pasar argumentos de método. El valor predeterminado es WinAPI, lo que corresponde a __stdcall en las plataformas de 32 bits de Intel. CharSet Controla la mutilación de nombres y la forma en que se deben calcular las referencias a los argumentos de cadena en la función. El valor predeterminado es CharSet.Ansi. EntryPoint Especifica el punto de entrada del archivo DLL que se va a llamar. ExactSpelling Controla si un punto de entrada debe modificarse para que se corresponda con el juego de caracteres. El valor predeterminado cambia según el lenguaje de programación. PreserveSig Controla si el prototipo administrado del método debería transformarse en un prototipo no administrado que devuelva un resultado HRESULT y que pueda tener un argumento adicional [out, retval] para el valor devuelto. El valor predeterminado es true (el prototipo no debe transformarse). SetLastError Permite al llamador utilizar la función Marshal.GetLastWin32Error de la función de API para determinar si se produjo un error durante la ejecución del método. En Visual Basic el valor predeterminado es true; en C# y C++, es false. >= 0x401) szPhonebook [ MAX_PATH ]; dwSubEntry; >= 0x500) guidEntry; >= 0x501) dwFlags; luid; Fuente 2 La obtención e identificación de la función de este caso es muy simple y sólo debemos echar mano del MSDN Library que ofrece Microsoft. Si utilizáramos una DLL propia con funciones específicas, deberíamos aplicar la misma metodología según su definición. En el siguiente paso creamos un proyecto –en nuestro caso un proyecto de consola con Visual C#– que llamaremos RasEnum. A continuación creamos una clase que contenga la función que hemos visto para que la utilización en nuestro proyecto, por ejemplo, que se adapte a la programación orientada a objetos, es decir, que para la llamada a la función externa RasEnumConnection la hagamos a través de un método público de la clase que hemos creado. Podemos crear una clase para cada función así como agrupar de manera lógica varias funciones para evitar la sobrecarga. De todas formas, la utilización de una clase facilitará la llamada a dicha función. Para implantar la función, debemos declarar el espacio de nombres System.Runtime.InteropServices. El código referente a la implantación es el siguiente: <<dotNetManía [DllImport("RASAPI32",SetLastError=true, CharSet=CharSet.Auto)] private static extern int RasEnumConnections( [In, Out] RASCONN[] lprasconn, ref int lpcb, ref int lpcConnections); 30 En primer lugar indicamos mediante la clase sellada DllImport que se trata de una función externa, por ello le indicamos el fichero DLL, en este caso RASAPI32.DLL, y a continuación se declaran una serie de atributos opcionales que pueden ser los de la tabla 1. La accesibilidad no es obligatoria. El hecho de que la haya declarado como privada, es para que sólo se pueda acceder mediante un método público de la clase que vamos a crear. Por definición, todas son de acce- Descripción Controla el inicio de una excepción en un carácter Unicode que no se ThrowOnUnmappableChar puede asignar y que se convierte a un carácter ANSI “?”. Tabla 1.Atributo de la clase sellada DllImport so estático y se deben declarar con la palabra reservada extern, ya que se implementa externamente. Hasta aquí está la parte de .NET que implementa la función, pero nos hemos saltado un paso, ¿cómo hemos pasado de la definición de C++ que hemos visto, al que se ha implementado en C#? En la definición vemos tipos de datos tales como DWORD como retorno LPDWORD, TCHAR, etcétera, en los parámetros de la función. Todos estos tipo son los llamados Windows Data Types y son los que se utilizan en Visual C++, y es por ello que deberemos saber cuál << dnm.plataforma.net es su homólogo en C#. En el mismo MSDN Library existen definiciones que nos describen cada tipo y a partir de ahí podemos saber que: • DWORD: 32 bit unsigned integer, (equivale a uint) • LPDWORD : Puntero a DWORD (equivale a ref uint) • TCHAR: 16 bit de caracteres UNICODE (equivale a string) De donde también deducimos que LPRASCONN es un puntero a la estructura RASCONN. De ahí que hayamos indi- cado el primer parámetro, el que hace referencia a la estructura RASCONN como In-Out, y los dos siguientes parámetros como ref uint, aunque también lo hubiéramos declarado como ref int. Ahora hace falta implementar la estructura, pero antes debemos implementar cinco constantes. Si nos fijamos bien en la definición de la estructura, vemos como nos indica que, por ejemplo, el miembro szEntryName, que hace referencia al nombre de la conexión, tiene una capacidad máxima de RAS_MaxEntryName + 1. Estos valores los podemos obtener, por ejemplo, del fichero de encabezado Ras.h de Visual C++. Los demás son: Fíjense que utilizamos [MarshalAs...] para indicarle que se trata de una cadena de tipo string de longitud fija y la longitud la pasamos mediante las constantes. A parte, la estructura posee el atributo StructLayoutAttribute para organizar en memoria la estructura. Normalmente CLR se encarga de esta operación pero es muy frecuente en las llamadas a funciones externas. Ya tenemos implementada la función y ahora crearemos un método público y estático para que podamos llamar a dicha función (fuente 5). Observemos que después de crear e inicializar el array de RASCONN asignamos al miembro dwSize el tamaño de la estructura RASCONN mediante Marshal tal y como indica en la definición de la estructura de la función RasEnumConnection del MSDN Library. Normalmente esto indica la versión de la función que debe utilizar para la manipulación de la estructura. Posteriormente lo multiplicaremos por el tamaño de la matriz, resultado el cual pasaremos por parámetro en lpcb por referencia. Tal y como nos public static void representar() { //comentario no implementado aún uint ret; uint conns = 0; RASCONN[] rarr = new RASCONN[256]; rarr.Initialize(); rarr[0].dwSize = (uint) Marshal.SizeOf(typeof(RASCONN)); uint lr = rarr[0].dwSize * (uint)rarr.Length; // comentario no implementado aún ret = RasEnumConnections(rarr, ref lr, ref conns); if (ret != 0) throw new System.ComponentModel.Win32Exception((int) ret); // comentario no implementado aún for(int i=0;i<conns;i++) { // comentario no implementado aún RASCONN r = rarr[i]; // comentario no implementado aún if (r.hrasconn == IntPtr.Zero) continue; // comentario no implementado aún Console.WriteLine(“CONEXIÓN RAS {0}:”, i+1); Console.WriteLine(“\nNombre : {0}”, r.szEntryName); Console.WriteLine(“\nDispositivo : {0}”, r.szDeviceName); Console.WriteLine(“\nTipo dispositivo : {0}”, r.szDeviceType); Console.WriteLine(“\nLibro de llamadas : {0}”, r.szPhonebook); Console.ReadLine(); private const int MAX_PATH = 260; private const int RAS_MaxDeviceType = 16; private const int RAS_MaxPhoneNumber = 128; private const int RAS_MaxEntryName = 256; private const int RAS_MaxDeviceName = 128; } Fuente 3 } Fuente 5. Método de llamada a la función externa RasEnumConnections. [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto)] private struct RASCONN { public int dwSize; public IntPtr hrasconn; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=RAS_MaxEntryName+1)] public string szEntryName; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=RAS_MaxDeviceType+1)] public string szDeviceType; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=RAS_MaxDeviceName+1)] public string szDeviceName; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=MAX_PATH)] public string szPhonebook; public int dwSubEntry; } Fuente 4 indica en la referencia del MSDN Library sobre RasEnumConnections, si éste devuelve 0 todo ha ido bien; en caso contrario, ha habido un error. Éste ha sido sólo un ejemplo de utilización de función externa. A partir de aquí puede probar en utilizar en lugar de los tipos uint que provienen de DWORD, utilizar int, verá que no tiene porqué haber ningún tipo de problema. Utilice int también en lugar de IntPtr , también funcionará. Si, por ejemplo, utilizáramos la función externa RasHangUp para cerrar una conexión, <<dotNetManía Así que la estructura quedará: 31 << dnm.plataforma.net ésta sólo recibe un parámetro hrasconn. Si declaramos en nuestra clase dicha función con el parámetro de tipo IntPtr, entonces lo que tenemos codificado hasta ahora servirá para finalizar una conexión, sin embargo, también podemos utilizarla con el tipo int, cambiándoselo tanto a RasHangUp como a RasEnumConnection. La diferencia es que IntPtr es un puntero e int es el tipo de datos. Si probáramos con string, por ejemplo, no funcionaría. La utilización de IntPtr o int provoca la entrada del cálculo de referencias en un mayor o menor coste. No existe un patrón determinado. A veces hace falta realizar el cálculo de referencia interoperativo casi de manera manual si se utilizan estructuras de tipo no completas, por ejemplo; así como podemos utilizar funciones externas de manera mucho más sencilla. Afortunadamente, hay muchos autores y soportes Web que tratan este tema y, sin duda, Internet es una muy buena fuente de conocimiento al respecto. obtiene un ensamblado de interoperabilidad primaria para poder interoperar. Si nos fijamos, estos tres métodos hacen justamente las operaciones de interoperabilidad que hemos visto hasta ahora de la implementación COM en .NET por medio del ensamblado de interoperabilidad primaria y mediante la importación de la biblioteca de tipos (TlbImp.exe) y la implementación .NET en COM mediante la exportación de la biblioteca de tipos ( TlbExp.exe ). Pues bien, todas estas operaciones pueden ser parametrizadas en tiempo de ejecución mediante la utilización de TypeLibConverter. Para mostrar realmente cómo utilizarlo, nos centraremos en los métodos blado de tipo Assembly, devolverá un objeto UCOMITypeLib que contendrá la definición administrada de la biblioteca de tipos lista para ser utilizada desde COM. En el segundo método, ConvertTypeLibToAssembly, lo que devolverá será un objeto de tipo AssemblyBuilder que encontramos en el espacio de nombres System.Reflection.Emit . Como parámetros, entre otros, pasaremos un objeto de tipo UCOMITypeLib, el cual hace referencia a la definición del componente COM. Veamos este método con más detalle. A priori, la utilización parece sencilla, y realmente lo sería si no fuese porque tenemos que hacer una llamada a una API del sistema operativo. Veamos el código en la figura 2. <<dotNetManía La clase TypeLibConverter 32 Otra alternativa a la hora de interoperar con las bibliotecas de tipo es mediante la utilización de la clase TypeLibConverter . TypeLibConverter es, como dije al principio, una clase sellada del espacio de nombres System.Runtime.InteropServices que permite la conversión de COM a .NET y viceversa, además de tener la posibilidad de obtener el ensamblado de interoperabilidad primaria. Es decir, que TypeLibConverter realiza las dos operaciones que hemos visto hasta ahora y es por ello que sea la opción más flexible y, aunque algo más compleja que las demás, conocer su funcionamiento merece la pena. TypeLibConverter tiene tres métodos funcionales para la interoperabilidad con código no administrado. Por un lado el método ConvertTypeLibToAssembly es el encargado de convertir una biblioteca de tipos a un ensamblado. ConvertAssemblyToTypeLib hace justo lo contrario, de un ensamblado exporta a un objeto como biblioteca de tipos como hace TlbExp.exe y, por último, GetPrimaryInteropAssembly Figura 2. Código de ejemplo de utilización del método ConvertTypeLibToAssembly de la clase TypeLibConverter. ConvertAssemblyToTypeLib y, especialmente, ConvertTypeLibToAssembly. El primero devuelve un objeto (object). Dicho objeto, no es más que un objeto que implementa una interfaz de tipo UCOMITypeLib. Así, pasado un ensam- Como vemos, no sólo depende de una llamada al método de conversión; entran otro tipo de llamadas. En primer lugar la clase ConversorNotificador que implementa la interfaz ITypeLibImporterNoti- << dnm.plataforma.net Para las funciones que no se encuentran en BCL,.NET ofrece un servicio llamado invocación de plataforma que encontraremos a menudo como P/Invoke.Dicho servicio se basa en los metadatos para el cálculo de referencias estándar y llamar a dichas funciones retorno de llamada sobre el estado de la conversión. Los dos métodos que debe implementar son ReportEvent (línea 11) y ResolveRef (línea 16). El primero describe el tipo de llamada según la enumeración ImporterEventKind y el segundo se ejecutará cuando haya necesidad de resolver una segunda referencia a otra biblioteca de tipos durante el proceso de conversión. La declaración del método LoadTypeLibEx (línea 28,29) de la librería oleaut32.dll, permite cargar una determinada biblioteca de tipos que referenciaremos desde el objeto tlb de tipo UCOMITypeLib (línea 33). En la llamada al método LoadTypeLibEx pasamos tres parámetros. En primer lugar la ubicación (path) del fichero que contiene la biblioteca de tipos en cuestión; en segundo lugar se le pasa el valor del enumerador RegKind (línea 24) y que estableceremos REGKIND_NONE, puesto que no deseamos registrar; por último pasamos la variable tlb por referencia y no inicializada (out) que será dónde se almacenará la definición de la biblioteca de tipos especificada en el primer parámetro, en modo de objeto. Cuando la variable tlb obtenga dicha referencia, utilizaremos, ahora sí, la clase TypeLibConverter para que el método ConvertTyperLibToAssembly se encargue de devolver un objeto de tipo AssemblyBuilder (líneas 40-43). Destacar que al método de conversión debemos indicarle el nombre del futuro ensamblado, la asignación de un nombre seguro y/o una clave para el nuevo ensamblado que en el ejemplo hemos omitido con valores null, la instancia de la clase de tipo ITypeLibImporterNotifySink que hemos definido arriba y el último parámetro en el que indicaremos mediante true o false, el tipo de control de las interfaces de la biblioteca de tipos según la parametrización de las clases de seguridad para el código no administrado (System.Security.Permission. SecurityPermissionsFlag.UnmanagedCode). Por último, el objeto AssemblyBuilder ab, mediante el método Save guardará en nuevo ensamblado en un fichero físico (línea 45). Hemos visto una de las dos sobrecargas del método ConvertTyperLibToAssembly , quizás la más utilizada, y solamente uno de los tres métodos que contie- ne la clase TypeLibConverter. Los tres pasos básicos son la implantación y utilización de LoadTypeLibEx, la utilización del método de conversión y la utilización de un objeto tipo AssemblyBuilder. Para acabar, quisiera hacer una observación en el método LoadTypeLibEx. El tercer parámetro es del tipo UCOMITypeLib, ya que se trata de un componente COM. Si no supiéramos el tipo exacto podemos utilizar un cálculo de referencias (marshalling) utilizándolo como tercer parámetro de la siguiente manera: [MarshalAs (UnmanagedType.Interface)] out Object typeLib;. Pueden ver el resultado en fuente 6. De esta forma parte del cáculo de referencias quedaria en manos del CLR. Hemos podido ver, de un rápido pincelazo, no sólo las tres posibles maneras de interoperabilidad con código no administrado, sino también una pequeña extensión en cuanto a cálculo de referencias se refiere. [DllImport( "oleaut32.dll", CharSet=CharSet.Unicode, PreserveSig=false )] private static extern void LoadTypeLibEx( String strTypeLibName, RegKind regKind, [ MarshalAs( UnmanagedType.Interface )] out Object typeLib ); } Fuente 6 Conclusión Con esta serie de tres artículos hemos visto la interoperabilidad desde varias perspectivas y con distintos métodos de uso y aplicación. Sin embargo, podríamos considerarlo sólo la punta del iceberg y para llegar a dominar y entender el tema en profundidad necesitaríamos de más páginas y ejemplos. Aún y así, espero que hayamos podido considerar y entender las nociones básicas para poder plantear dichas técnicas en nuestras aplicaciones aún a sabiendas de la complejidad que realmente tiene su aplicación en un contexto de migración, pero con la facilidad y transparencia que nos brinda .NET Framework, con el cual estaremos, sin duda, más tranquilos. <<dotNetManía fySink (línea 9) proporcionará un mecanismo de 33 Suscripción a dotNetManía ❑ Deseo suscribirme a dotNetManía por un año (11 ejemplares) y beneficiarme de la oferta del 10% de descuento por un importe total de 60 € para España; o por 120€ para el resto del mundo (envío por avión) (IVA incluido). ❑ Deseo suscribirme a dotNetManía por un año (11 ejemplares) por un importe de 45 € por ser estudiante (IVA incluido). Aporto fotocopia del carné de estudiante o sello del centro académico (IMPRESCINDIBLE). OFERTA VÁLIDA SÓLO PARA ESTUDIANTES RESIDENTES EN ESPAÑA. IMPORTES VÁLIDOS HASTA NUEVA OFERTA DATOS DE FACTURACIÓN CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dirección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Población . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Código Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Teléfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . . DATOS DE ENVÍO (sólo si son distintos de los datos de facturación) CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dirección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Población . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Código Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Teléfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . . FORMA DE PAGO ❑ Talón nominativo a nombre NETALIA, S.L. ❑ Transferencia bancaria a nombre de NETALIA, S.L. a: La Caixa - Número de cuenta 2100 4315 48 2200014696 (Indique su nombre en la transferencia) ❑ Domiciliación Bancaria (con renovación automática, previo aviso) Indique su número de cuenta: ❑ Tarjeta de crédito ❑ VISA ❑ MASTERCARD Número de su tarjeta: Fecha de caducidad: / (imprescindible) Firma y/o sello (imprescindible) a ❑ Nº7 ❑ Nº8 ❑ Nº9 de ❑ Nº10 de 20 0 ❑ Nº11 Si desea algún otro número indíquelo Puede enviar los datos al email [email protected], al FAX (34) 91 499 13 64 o al teléfono (34) 91 666 74 77. También puede enviarlo por correo postal a la siguiente dirección: Netalia, S.L C/ Robledal, 135 28529 - Rivas Vaciamadrid Madrid (España) Usted autoriza a la mecanización de estos datos. El responsable y destinatario de éstos es Netalia, S.L. Usted tiene derecho a acceder a sus datos, modificarlos y cancelarlos cuando lo desee. Sus datos no serán cedidos en ninguna de las formas posibles a terceras partes y no se utilizarán más que para el buen funcionamiento de su suscripción a la revista dotNetManía y para informarle de las actividades comerciales que realice la editorial Netalia, S.L. Si no desea recibir información comercial de dotNetManía marque la casilla siguiente ❑ ❑ Nº12 ❑ Nº13 Braulio Díez PDF Caja transparente Para la mayoría de la gente, el formato PDF es un tipo de documento universal que se puede ver desde cualquier máquina sin perder detalle. ¿Cómo se crea un PDF? Herramientas especiales, conversores…, en definitiva: “cajas negras”. En este artículo vamos a convertir dicha caja opaca, en transparente, estudiando su formato y viendo las diversas formas de generarlo; para ello usaremos C#. << Cuando le propuse a Paco Marín escribir este artículo, no tenía muy claro cómo enfocarlo; hacer unas librerías desde cero para generar PDF suponía eso que los desarrolladores amamos tanto: reinventar la rueda; mostrar sólo cómo se generan estos documentos a partir de varias librerías gratuitas o de pago, iba dejar a los lectores con la misma sensación que cuando van al taller a dejar el coche y le dicen “ha sido la junta de la trócola… son cien mil”, vamos que uno pone cara de tonto, paga, espera que el coche ande y no tener que volver muy a menudo por allí. Como ambos extremos se quedaban cojos, he optado por dar una introducción al formato PDF, con un ejemplo en C# del tipo “Hola mundo” que nos haga perderle el miedo. De esta forma sabremos cómo continuar si nos pica el gusanillo, aprenderemos conceptos muy interesantes que han introducido los chicos de Adobe y por último realizaremos ejemplos más avanzados utilizando una librería gratuita y otra de pago (en concreto iSharpText, y Tall Pdf kit). Figura 1. Nuestro “Hola mundo” Cuando hablamos de formato PDF podemos dividirlo en dos áreas: • Estructura del fichero: Cómo se organizan los objetos en un fichero PDF para su acceso directo de forma eficiente. • Estructura del documento: Cómo se distribuye la jerarquía de objetos para poder almacenar el contenido y aspecto del documento. ¡Hola mundo! Braulio Díez colabora habitualmente con dotNetManía. Es MCSD en programación distribuida con Visual C++. Trabaja como Solutions Developer de Avanade. Para estudiar el formato PDF de forma práctica, vamos a diseccionar el ejemplo más sencillo y conocido por todos nosotros: un documento con una página que contenga el maravilloso texto “¡Hola mundo!”(ver figura 1). La estructura de un fichero PDF La estructura de un fichero PDF se divide en cuatro partes (ver figura 2): • Cabecera: Está compuesta por una línea. En ella indicamos que el documento es un PDF y la ver- dnm.plataforma.net 1 0 obj << /Length 2 0 R >> stream BT /F0 10 Tf 30 798 Td 10 TL (Hola Mundo !)Tj ET endstream endobj 2 0 obj 49 endobj 3 0 obj <</Type/Page/Parent 4 0 R/Contents 1 0 R>> endobj 4 0 obj <</Type /Pages /Count 1 /Kids[ 3 0 R ] /Resources<</ProcSet[/PDF/Text]/Font<</F0 5 0 R>> >> /MediaBox [ 0 0 594 828 ] >> endobj 5 0 obj <</Type/Font/Subtype/Type1/BaseFont/Courier/ Encoding/WinAnsiEncoding>> endobj 6 0 obj <</Type/Catalog/Pages 4 0 R>> endobj xref 0 7 0000000000 65535 f 0000000009 00000 n 0000000110 00000 n 0000000128 00000 n 0000000186 00000 n 0000000322 00000 n 0000000408 00000 n trailer << /Size 7 /Root 6 0 R >> startxref 453 %%EOF CABECERA (una sola línea) • CUERPO Cadenas de texto, imágenes, diccionario de páginas, etc. Todo encapsulado en objetos TABLA DE REFERENCIAS CRUZADAS En qué offset del fichero se encuentra cada objeto del cuerpo, para acceder a ellos de forma directa TRAILER Aquí le indicamos cuál es el objeto principal, es decir. el objeto raíz, y dónde se encuentra la tabla de referencias cruzadas y su tamaño Figura 2. Estructura de un fichero PDF • • sión del formato con el que se ha realizado (por ejemplo: “%PDF-1.2”). Cuerpo (Body): Contiene los objetos que conforman los datos del documento (cadenas de texto, streams con imágenes, diccionarios de páginas, etc.). Tabla de referencias cruzadas (crossreference table): Cada elemento del documento (cadena de texto, imagen, etc.) se encuentra dentro de un objeto; en esta tabla de referencias cruzadas tenemos una lista con la dirección dentro del fichero en la que se encuentra cada objeto (de esta forma, para acceder a una parte del fichero que nos interese, no es necesario leer el archivo completo). Ver figura 3. Trailer: Nos indica dónde comienza la tabla de referencias cruzadas (en qué offset del fichero) y también cuál es el objeto principal desde el que se debe empezar a leer el documento. Ver figura 4. A primera vista seguro que se nos viene a la cabeza pensar que la estructura del fichero está casi al revés, ¿No sería lo lógico tener la cabecera, la tabla de referencias a los objetos y después el cuerpo del documento con todos los objetos? La respuesta es no, tener la tabla de referencias a los objetos al final nos permite crear ficheros PDF de una sola pasada, de la siguiente forma: 1. Escribimos los objetos que componen el documento de forma secuencial en el fichero, lo único que tenemos que almacenar en memoria es el par número de objeto / dirección del objeto (offset del fichero donde se encuentra el objeto). 2. Escribimos en el fichero la lista compuesta por los pares número de objeto/dirección del objeto almacenados en el paso 1 (esto sería la tabla de referencias cruzadas). 3. Escribimos el trailer, indicando cuál es el objeto principal y en qué posición (offset) del fichero se encuentra el comienzo de la tabla de referencias cruzadas (paso 2). Así pues, cuando leemos un fichero PDF, empezamos por el final (cargando en memoria cuál es el objeto principal y la tabla de referencias cruzadas), y al resto del documento vamos accediendo de forma dinámica (la ventaja es que podemos leer documentos de incluso varios gigas sin que nuestra máquina se quede “tostada”). La estructura de un documento PDF Ahora que conocemos cuál es el “esqueleto” de un PDF, vamos a ver cuál es su contenido. Los elementos que forman un PDF pueden ser de distintos tipos: <<dotNetManía %PDF-1.1 37 << dnm.plataforma.net xref 0 7 0000000000 0000000009 0000000116 0000000134 0000000192 0000000328 0000000414 65535 00000 00000 00000 00000 00000 00000 f n n n n n n Aquí comienza la tabla de referencias cruzadas. Le decimos que tenemos una subsección, que contiene 7 objetos, desde el 0 al 6 (0: referencia inicial del primer objeto, 7:número de objetos que referenciamos). La primera entrada debe ser siempre igual (0 65535 f). Es la primera entrada a una lista de objetos borrados (la f significa “free entry”), la n viene de “in use”. Esto, y los ceros que tenemos a la derecha, se usan para el control de versiones/modificaciones de un documento, que es un tópico avanzado; nosotros sólo nos preocuparemos de rellenar esos campos a sus valores por defecto. Offset del fichero en que se encuentra cada entrada obj (objetos). Podemos tener ficheros de hasta 10 Gigas. Figura 3. Detalle de la tabla de referencias cruzadas trailer << /Size 7 /Root 6 0 R >> startxref 459 %%EOF Aquí le decimos el número de entradas que hemos metido en la tabla de referencias cruzadas (7 en nuestro caso) y qué objeto tiene el diccionario principal (en este caso el objeto 6). Offset en bytes desde el principio del fichero hasta la entrada xref (comienzo de la tabla de referencias cruzadas). Marca de final de fichero. Figura 4. Detalle del trailer del fichero • Cada elemento que insertamos en un PDF está contenido en un objeto cuyo nombre es numéricoy único (así podemos referenciar de forma única a un elemento) • • <<dotNetManía • 38 Streams de Texto (cadenas de texto, definición de fuentes, etc.). Streams Binarios (que contengan, por ejemplo, imágenes). Diccionarios (nos sirven para almacenar, por ejemplo, listas de páginas). Cada elemento que insertamos en un PDF está contenido en un objeto cuyo nombre es numérico y único (así podemos referenciar de forma única a un elemento). Vale, ¿pero cómo pegamos todo esto para montar un documento con sentido? Para ello tenemos una jerarquía de objetos. Vamos a ver los trazos principales de la misma: • • • En la cima tenemos el Catálogo/ Diccionario del documento, que contiene las referencias a los objetos principales (en nuestro caso sólo tendrá una referencia al árbol de páginas). El árbol de páginas (se llama así porque podemos crear estilos de páginas y que hereden otras sus atributos). Las páginas. El resto de Objetos/Elementos (cadenas de texto, imágenes, fuentes, etc.). PDF un estándar de facto Tres de los factores que han hecho del PDF el formato estándar para publicar documentos son: • El formato no es secreto; de hecho los chicos de Adobe nos permiten bajarnos una extensa guía en la que nos explican de forma exhaustiva todos los detalles del mismo. • Es un formato tremendamente disciplinado que hereda del Postscript, en el que cada trozo de información se divide en objetos. • Adobe proporciona de forma gratuita un estupendo visor para casi cualquier plataforma. << << dnm.plataforma.net Aquí definimos un objeto. La referencia del objeto es el número 1, el 0 que se encuentra a la derecha significa que es la revisión 0 del objeto. Dentro de él vamos a añadir nuestro stream de texto “Hola mundo”. %PDF - 1.1 1 0 obj << /Length 2 0 R >> stream BT /F0 16 Tf 30 798 Td 10 TL (Hola Mundo !)Tj ET endstream endobj 2 0 obj 49 endobj 3 0 obj <</Type/Page/Parent 4 0 R/Contents 1 0 R>> endobj 4 0 obj <</Type /Pages /Count 1 /Kids[ 3 0 R ] /Resources<</ProcSet[/PDF/Text]/Font<</F0 5 /MediaBox [ 0 0 594 828 ] >> endobj 5 0 obj Para definir nuestro stream le tenemos que decir la longitud que tendrá; en este caso le decimos que la longitud se la especificamos en el objeto 2 (obj 2 0), la R significa “Referencia”. Esto se suele hacer para generar de una pasada el PDF. BT “Begin of Text” | ET “End of Text”. Establecemos la fuente 0 (que definimos más adelante en los recursos), con tamaño 10, le definimos la posición en la que escribir el texto (30,798). Secuencia de caracteres, delimitadas por paréntesis. Si estuvieran delimitadas por <>, sería un string con caracteres hexadecimales. El objeto 2 0, almacena el número 49, que es la longitud del stream del objeto 1 0. Aquí definimos la primera página, le decimos que herede los atributos del objeto 4 0, y le indicamos que el contenido de la misma lo puede encontrar en el objeto 1 0 (podríamos tener un array de objetos). 0 R>> >> Arbol de páginas, aquí le indicamos cuántas páginas vamos a poner, y la lista de objetos que contienen a esas páginas (en nuestro caso sólo una, la que definimos en el objeto 3 0). También le indicamos que las páginas usan un recurso fuente que hemos creado (el objeto 5 0 del documento), y con Media Box le indicamos el tamaño de la página. <</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding/WinAnsiEncoding>> endobj 6 0 obj <</Type/Catalog/Pages 4 0 R>> endobj La fuente F0 la definimos en el objeto 5 0, y la incluiremos en el objeto 4 0, para su uso en la xref página que hemos creado. 0 7 0000000000 65535 f 0000000009 00000 n 0000000110 00000 n Catálogo principal. Apuntamos al objeto que contiene el arbol de páginas (obj 4 0). 0000000128 00000 n 0000000186 00000 n 0000000322 00000 n 0000000408 00000 n trailer << /Size 7 /Root 6 0 R >> startxref 453 %%EOF Figura 5. Estructura de un documento PDF Cómo generar un PDF con C# sin ayudarnos de librerías Para terminar de perderle el miedo a este formato, vamos a generar nuestro “Hola mundo” partiendo de cero. El ejemplo en cuestión consta del fichero principal (una aplicación de consola) y una clase que gestiona de forma automática la Otras alternativas Existen múltiples alternativas para generar ficheros PDF de forma automática.A destacar: • Drivers de impresora que escriben a PDF traduciendo los comandos de GDI a formato PDF.Es una solución rápida,el problema es que no se tiene mucho control sobre el documento generado. • Herramientas XSL como FOP de Apache, que ofrecen un pseudolenguaje para generar los documentos PDF, a partir de éste se genera una plantilla XSL que convierte datos a formato PDF (lo bueno de estas herramientas es que permiten aprovechar las plantillas para generar otros múltiples formatos, Excel, MS Word, etc.). << <<dotNetManía Para ver cómo funciona todo esto, vamos a analizar nuestro “Hola mundo” (ver figura 5). Para seguir profundizando, puede bajar de la Web de Adobe la guía del formato (al final de este artículo se proporciona el link a dicha guía). 39 << dnm.plataforma.net static void Main(string[] args) { float pageWidth = 594.0f; // float pageDepth = 828.0f; // float pageMargin = 30.0f; float fontSize = 16.0f; float leadSize = 10.0f; float yPos = 0f; string strPDFElement PDFSimpleWriter pdfSimpleWriter Ponemos estos datos aquí para que sea más fácil cambiarlos = “”; = new PDFSimpleWriter(); pdfSimpleWriter.startNewDocument(); // Escribimos nuestro stream que contiene “Hola mundo” // ( tenemos que especificarle posición, fuente, y texto) // ********************** 1 0 obj ********************** // Le decimos que la longitud del stream se la indicamos en el objeto 2 0 strPDFElement = “<< /Length 2 0 R >>\n”; strPDFElement +=”stream\n”; int holaMundoStart = strPDFElement.Length; strPDFElement += yPos = pageDepth strPDFElement += strPDFElement += “BT\n/F0 “ + fontSize +” Tf\n”; - pageMargin; pageMargin + “ “ + yPos +” Td\n”; leadSize+” TL\n”; // Añadimos el texto strPDFElement += “(Hola Mundo !)Tj\n” ; strPDFElement += “ET\n”; int holaMundoEnd = strPDFElement.Length; strPDFElement += “endstream\n”; pdfSimpleWriter.WriteObject(strPDFElement); // </1 0 obj> // ********************** 2 0 obj ********************** // Ahora escribimos el objeto 2 0 que contiene la // longitud del stream que hemos escrito int HolaMundoLen = holaMundoEnd - holaMundoStart; <<dotNetManía strPDFElement = HolaMundoLen.ToString() + “\n”; pdfSimpleWriter.WriteObject(strPDFElement); // </2 0 obj> // ********************** 3 0 obj ********************** // Definimos la página primero, y le decimos que el // contenido está en el objeto 1 0 ( podríamos haber // definido un array de objetos) strPDFElement = “<</Type/Page/Parent 4 0 R/Contents 1 0 R>>\n”; pdfSimpleWriter.WriteObject(strPDFElement); // </3 0 obj> // ********************** 4 0 obj ********************** // Aquí definimos el arbol de páginas, en nuestro caso // sólo tenemos una página strPDFElement = “<</Type /Pages /Count 1\n”; strPDFElement += “/Kids[\n3 0 R\n]\n”; strPDFElement += “/Resources<</ProcSet[/PDF/Text]/Font<</F0 5 0 R>> >>\n”; strPDFElement += “/MediaBox [ 0 0 “+ pageWidth + “ “ + pageDepth + “ ]\n>>\n”; pdfSimpleWriter.WriteObject(strPDFElement); // </4 0 obj> // ********************** 5 0 obj ********************** // Añadimos la fuente que usaremos en la cadena de texto “Hola Mundo” strPDFElement = “<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding/WinAnsiEncoding>>\n”; pdfSimpleWriter.WriteObject(strPDFElement); // </5 0 obj> // ********************** 6 0 obj ********************** // Definimos el catalogo principal ( le decimos en que objeto // está el arbol de páginas). strPDFElement = “<</Type/Catalog/Pages 4 0 R>>\n”; pdfSimpleWriter.WriteRootObject(strPDFElement); // </6 0 obj> 40 // EndDocument genera la tabla de referencias cruzadas y el trailer pdfSimpleWriter.endDocument(); // Escribir el documento a fichero y testear pdfSimpleWriter.WriteToFile(@”Testeo.pdf”); } Fuente 1. Generación de PDF sin librerías. Main de la aplicación << dnm.plataforma.net << estructura del fichero (cabecera, tabla de referencias cruzadas y trailer), así sólo tenemos que preocuparnos del contenido del mismo (los objetos). En el fuente 1 puede ver el main de la aplicación, la clase que gestiona la estructura del documento está disponible en la web de dotNetManía (PDFSimpleWriter; debido a su extensión no la hemos incluido en el artículo), ambos ficheros están profusamente documentados para que sean fáciles de seguir. Se podría mejorar el ejemplo creando una tabla hash que mapeara los identificadores de los objetos a nombres “amistosos” (por ejemplo cadenaholamundo), para así poder referenciarlos de forma fácil desde otros objetos (por ejemplo, llamar a un stream desde una página). De esta forma se podrían tener clases auxiliares que crearan los objetos por nosotros (por ejemplo, una clase que nos sirviera para generar streams de texto, etc.). bajamos sólo la DLL, resulta que necesita otra DLL con una librería open source que se llama ZipLib, nos la bajamos también, creamos un proyecto de ejemplo, conseguimos que compile la aplicación y cuando vamos a ejecutarlo nos pega un cascazo. Buscamos en los foros, y resulta que la librería de ZipLib que nos habíamos bajado no era compatible con la que usan iTextSharp. Solución: nos bajamos el código fuente de iTextSharp y lo recompilamos (gracias a dios cargamos el .SLN, le damos a “build” y todo sale a la primera). Espero que ningún aficionado al open source se haya enfadado por el tironcete de orejas; es sólo que a veces por unos pequeños detalles, un excelente producto no se acaba usando porque es complicado de instalar. Generando los PDF Si en el punto anterior teníamos que dar un pequeño tirón de orejas, en éste todo lo contrario. Nos podemos bajar un tutorial muy completo desde su mismo sitio web, que nos permite ponernos en productivo en muy poco tiempo. Para ver una idea de cómo se usa la librería, he hecho un ejemplo que genera un recibo de un banco. Como se ve en el código, el uso de ésta es bastante intuitivo (en la figura 6 puede ver una captura del documento, y en fuente 2 el código que lo genera). Figura 6. Nuestro recibo de banco generado con iTextSharp iTextSharp TallPDF iTextSharp es una librería open source gratuita con muy buena fama. A continuación vamos a ver cómo instalarla y un pequeño ejemplo que genera un recibo de un banco. Hemos visto cómo hacer el “Tarzán” y generar un documento PDF desde cero, también cómo ayudarnos de una librería gratuita y ahorrarnos tiempo. Vamos a por la última opción, una librería de pago que tiene una forma de generar documentos muy interesante: en un fichero XML definimos una plantilla (si queremos, podemos definirlo usando la propia librería), y este componente convierte el XML en un fichero PDF. Las principales ventajas de esta aproximación, es que permiten tanto generar ficheros desde código, como directamente en XML, y lo más importante, podemos convertir estos XML a plantillas XSL, con lo que pode- Instalando la librería Si algo se les suele echar en cara a este tipo de librerías es que suelen ser muy enrevesadas de instalar y que no tienen una buena documentación para empezar a usarlas. En este caso, la instalación es un “poquito” trabajosa; si seguimos los pasos rápidos la cosa no sale, nos <<dotNetManía Aspectos avanzados del formato PDF es un formato vivo, evoluciona con el tiempo.Algunos aspectos más avanzados son: • Control de versiones: Permite guardar diferentes versiones de un documento. • Encriptación de documentos:Así podemos añadir seguridad al mismo y que sólo pueda ser leído por usuarios autorizados. • Streams Comprimidos: No sólo se puede añadir compresión a las imágenes que se inserten en un PDF sino también a cualquier trozo de texto o flujo de datos. • Representaciones gráficas: Este formato incorpora un potente motor para generar gráficas y representar fórmulas matemáticas. 41 << dnm.plataforma.net static void Main(string[] args) { // Creamos una instancia iTextSharp.text.Document: Document document = new Document(); // Le decimos donde queremos escribir PdfWriter.getInstance(document, new FileStream(@”C:\iTextSample.pdf”, FileMode.Create)); // Abrimos el documento document.Open(); // Creamos una tabla en la que imprimiremos el recibo Table table = new Table(3, 4); table.setWidths(new int[]{25, 55, 35}); table.BorderWidth = 1; table.Padding = 2; table.Spacing = 2; // Insertamos la imagen con el logo del banco Image logo = Image.getInstance(@”c:\ejemplo.jpg”); logo.Alignment = Image.TOP | Image.LEFT; logo.Border = 0; logo.scalePercent(60); //Insertamos la celda con el logo Cell cell1 = new Cell(logo); cell1.Colspan = 1; cell1.Rowspan = 1; table.addCell(cell1); //Cabecera del recibo Chunk chunk = new Chunk(“Comunicación operación a cuenta”, FontFactory.getFont( FontFactory.HELVETICA_BOLD, 18, Font.NORMAL, new Color(0, 0, 0))); Paragraph p1 = new Paragraph(chunk); Cell cell2 = new Cell(chunk); // El “Colspan” es como el de las tablas HTML cell2.Colspan = 2; table.addCell(cell2); // Creamos el detalle del recibo Chunk chunkBody1 = new Chunk(“Pago cuota del prestamo hipotecario correspodiente a el mes de Septiembre del 2004”, FontFactory.getFont(FontFactory.HELVETICA, 10, Font.NORMAL, new Color(0, 0, 0))); Chunk chunkBody2 = new Chunk(“\n”, FontFactory.getFont(FontFactory.HELVETICA, 10, Font.NORMAL, new Color(0, 0, 0))); Chunk chunkBody3 = new Chunk(“\n”, FontFactory.getFont(FontFactory.HELVETICA, 10, Font.NORMAL, new Color(0, 0, 0))); Paragraph pBody = new Paragraph(chunkBody1); pBody.Add(chunkBody2); pBody.Add(chunkBody3); // y el pie del mismo Cell cell3 = new Cell(pBody); cell3.Colspan = 3; table.addCell(cell3); Chunk Footer1 = new Chunk(“Cuota mensual”, FontFactory.getFont(FontFactory.HELVETICA, 10, Font.BOLD, new Color(0, 0, 0))); Paragraph pFooter1 = new Paragraph(Footer1); Cell cell4 = new Cell(pFooter1); cell4.Colspan = 2; table.addCell(cell4); <<dotNetManía Chunk Footer2 = new Chunk(“500 €”, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.NORMAL, new Color(0, 0, 0))); Paragraph pFooter2 = new Paragraph(chunkBody1); Cell cell5 = new Cell("500 €"); cell5.Colspan = 1; table.addCell(cell5); // Añadimos la tabla al documento document.Add(table); 42 // Cerramos el fichero document.Close(); } Fuente 2. Código que genera el recibo utilizando la librería open source iTextSharp << dnm.plataforma.net Instalando la librería Una librería de pago se supone que no debería tener ningún problema a la hora de instalarla ¿no? Bueno, lo cierto es que me dio un poco de guerra: el sistema para bajar la versión de prueba no me funcionó a la primera, eso era un sábado a las 17:00, les escribí un correo creyendo que no recibiría respuesta hasta por lo menos el siguiente lunes, y cual fue mi sorpresa cuando a las 19:49 recibí la contestación con las versiones de prueba para empezar a trabajar. Generando los PDF En los fuentes 3 y 4 podemos ver el XML y el código C# que utilizaríamos para generar el mismo ejemplo del recibo de banco del apartado anterior (figura 6). Lo que se echa en falta a este componente es una referencia a las etiquetas XML que se pueden usar para generar PDF. Hablé con Frank Rem (fundador de Tall Components) y me comentó que lo iban a tener en cuenta, aunque el XML que se genera son las clases del componente serializadas (así pues, si en la ayuda vemos una propiedad de un objeto que se llama MinHeight sabemos que podemos usar esa etiqueta en el fichero XML). Para saber más… Espero que este artículo sirva como punto de partida para que pueda adentrarse en el mundo del PDF, y haber convertido este formato en una caja menos misteriosa y opaca para nosotros, los desarrolladores. Si quiere seguir investigando este área, en la tabla referencias, tiene unos links interesantes. Referencias La guía oficial del formato PDF: http://partners.adobe.com/asn/tech/pdf/specifications.jsp La librería iTextSharp gratuita que analizamos en el artículo, la puede encontrar en: http://itextsharp.sourceforge.net Sobre los componentes de pago, la página web de Tall components es: http://www.tallcomponents.com Un buen sitio para desarrolladores: http://www.planetpdf.com/developer/index.asp Varios sitios de .net en los que se pueden encontrar artículos interesantes: http://www.codeproject.com , http://www.c-sharpcorner.com Para los "Javeros": http://xml.apache.org/fop/index.html <document xmlns=”http://www.tallcomponents.com/schemas/tallpdf/v1”> <section> <paragraph type=”table” preferredwidth=”400” forcewidth=”true”> <row> <cell fixed=”true” colspan=”1”> <border> <left /> <top /> </border> <paragraph type=”image” path=”c:\ejemplo.jpg” compression=”dct” width=”100” /> </cell> <cell colspan=”3” fixed=”true”> <border> <left /> <right /> <top /> </border> <paragraph type=”textparagraph”> <fragment font=”HelveticaBold”>Comunicacion operacion a cuenta</fragment> </paragraph> </cell> </row> <row MinHeight=”100”> <cell colspan=”4”> <border> <left /> <right /> <top /> </border> <paragraph type=”textparagraph”> <fragment>Pago cuota del prestamo hipotecario correspondiente al mes de Septiembre del 2004 </fragment> </paragraph> </cell> </row> <row> <cell colspan=”4”> Fuente 3. XML con el layout del recibo del banco que queremos generar (sigue...) <<dotNetManía mos generalizar y, por ejemplo, podríamos generar todos los recibos de banco a partir de una sola plantilla. 43 << dnm.plataforma.net <border> <left /> <right /> <bottom /> </border> <paragraph type=”textparagraph”> </paragraph> </cell> </row> <row> <cell colspan=”3” fixed=”true”> <border> <left /> <right /> <bottom /> </border> <paragraph type=”textparagraph”> <fragment>Cuota Mensual</fragment> </paragraph> </cell> <cell colspan=”1”> <border> <left /> <right /> <bottom /> </border> <paragraph type=”textparagraph”> <fragment>500 Euros</fragment> </paragraph> </cell> </row> </paragraph> </section> </document> (...continuación) Fuente 3. XML con el layout del recibo del banco que queremos generar static void Main(string[] args) { FileStream fs = null; try { // Le decimos donde queremos generar el fichero PDF fs = new FileStream( @”c:\TallSample.pdf”, FileMode.Create ); // Leemos la plantilla XML con el recibo de banco System.IO.StreamReader stream = new System.IO.StreamReader (@”C:\BancoEjemplo.xml”); XmlTextReader reader = new XmlTextReader (stream); Document document = new Document(); document.Read(reader); <<dotNetManía // Le decimos que transforme la plantilla XML y la escriba // en el fichero PDF que le hemos indicado antes document.Write(fs); 44 // Cerramos los streams al fichero XML y al PDF reader.Close(); stream.Close(); } catch(System.Exception ex) { System.Console.WriteLine(“***************************************”); System.Console.WriteLine(“ERROR: “ + ex.Message); System.Console.WriteLine(“***************************************”); System.Console.ReadLine(); } finally { fs.Close(); } } Fuente 4. Código C# que transforma la plantilla XML en un recibo de banco en formato PDF << dnm.mvp.online Jorge Serrano MZ-Tools << ¿Cuándo y cómo saber que una determinada utilidad o aplicación nos resulta útil o es catalogada como indispensable? Sé que es una pregunta muy genérica, pero ¿realmente se la ha hecho alguna vez de forma profunda? Y es que este mes he querido mostrar una utilidad muy especial de nombre MZ-Tools y desarrollada por Carlos J. Quintero, MVP de Visual Developer desde el año 2004. MZ-Tools no es otra cosa que un add-in o complemento para Visual Basic, VBA y Visual Studio .NET. De hecho, existen dos versiones de este complemento, MZ-Tools 3.0 para Visual Basic 5, Visual Basic 6, VBA y MZ-Tools 4.0 para Visual Studio .NET 2002 y Visual Studio .NET 2003. En este artículo nos centraremos únicamente en el complemento para Visual Studio .NET, aunque ambas versiones comparten muchas de las funcionalidades que mencionaré. Historia de MZ-Tools Jorge Serrano es redactor de dotNetManía. Es Ingeniero Informático y MVP de VB y de .NET. Es Webmaster de PortalVB.com y autor de diferentes libros y artículos técnicos. Jorge está entre los mejores profesores y conferenciantes sobre la tecnología .NET de nuestro país. MZ-Tools tiene un nombre más que curioso, si bien “Tools” es una palabra cuyo significado prácticamente todo el mundo conoce, “MZ” sí que requiere su explicación. Carlos nos describe claramente en su página Web que en un primer momento, intentó denominar a su complemento con el nombre de QTools, sin embargo, el dominio para este nombre estaba ocupado, por lo que pensando en otro dominio que estuviera disponible, finalmente cambió la “Q” por “MZ”. Lo que es realmente anecdótico en toda esta historia, es el significado de MZ, el cuál viene de aquellos entrañables dibujos animados japoneses de hace aproximadamente veinticinco años llamados Mazinger-Z, y que en España se han repuesto desde entonces en varias ocasiones. Respecto a la aparición de la primera versión de MZ-Tools, la podemos encontrar en el mes de Octubre de 2000, cuando se hace oficial la versión 1.0 de MZ-Tools. Se trataba de una versión para Visual Basic 6.0 distribuida con licencia freeware. Apenas 6 meses después, aparecería MZ-Tools 2.0 siendo una versión mucho más extendida y con más éxito que MZ-Tools 1.0. Igualmente en este caso, la licencia de distribución de este complemento fue de tipo freeware. Carlos continuó entonces con la mejora de las funcionalidades de este complemento, y así, en el mes de Octubre de 2001, apenas 1 año después de aparecer MZ-Tools 1.0, aparece la aún más renovada y poderosa versión MZ-Tools 3.0, un complemento con una distribución aún mucho mayor que obtuvo un éxito incontestable. Basta con darnos una pequeña vuelta en Internet y buscar un poco para encontrar todo tipo de halagos sobre esta herramienta. Siendo además la última versión para Visual Basic 5.0, Visual Basic 6.0 y VBA, hoy día es utilizada por una enorme cantidad de desarrolladores en todo el mundo. ¿Y que pasó a partir de ahí?. Carlos se introdujo en la tecnología .NET de Microsoft, y optó por hacer un MZ-Tools para .NET. Así que se puso manos a la obra y con no poco esfuerzo y trabajo, terminó lo que se ha llamado MZ-Tools 4.0, el complemento de MZTools para Visual Studio .NET. Sin embargo, esta nueva versión de este famoso complemento, obligaría a replantear algunas cosas respecto a versiones anteriores. MZ-Tools 4.0, sus cambios respecto a anteriores versiones MZ-Tools 4.0 supuso un importante reto, trabajo y esfuerzo para Carlos, por lo que ese esfuerzo y dedicación, merecía ser recompensado. De esta manera, MZ-Tools 4.0 se distribuiría a partir de ahora bajo licencia de pago. Es decir, MZ-Tools 4.0 ya no estaría bajo la licencia freeware. Algo que si lo pensamos fríamente, tiene su lógica. Además, MZ-Tools 4.0 obligó a que algunas de las características y propiedades de MZ-Tools 3.0, fueran abandonadas por no considerarse ahora indispensables. Sin embargo y por fortuna, el nuevo entorno de desarrollo, ha hecho que se incorporen a MZ-Tools algunas nuevas utilidades, por lo que MZ-Tools está más enriquecido en estos momentos. MZ-Tools 4.0, se distribuye por lo tanto, en dos tipos de licencia, una licencia de tipo individual conocida como Personal Edition, y que tiene un precio de 39,95€, y una licencia de servidor conocida como Enterprise Edition, y que tiene un precio de 399,95€. Ambas versiones tienen el mismo número de funcionalidades, pero la primera tiene una licencia adecuada para desarrolladores individuales, mientras que la segunda tiene una licencia tipo site adecuada para empresas. << dnm.mvp.online La instalación de este complemento para los lenguajes VB.NET y C# de Visual Studio .NET, es realmente simple. Basta con ejecutar la instalación y seguir las instrucciones que aparecen en pantalla, y por último, activar el producto con su correspondiente licencia. Cuando hemos completado estos pasos y arrancamos el entorno de desarrollo Visual Studio .NET, nos encontraremos con MZ-Tools integrado en nuestro entorno de desarrollo. En el menú “MZ-Tools >> Acerca de”, podremos recoger la información de la versión de MZ-Tools instalada en nuestro sistema, así como la licencia del producto, tal y como se muestra en la figura 1. incorporada en el entorno, o haciendo clic con el botón derecho del ratón sobre el código de la aplicación por ejemplo. Sus utilidades o funcionalidades son muy extensas y variadas, son cerca de 40 entre las cuales podemos destacar las siguientes: Para escribir código más deprisa • Plantillas de código: nos permite añadir plantillas de código rápidamente, así como gestores de excepciones como el conocido Try Catch. • Asistentes para nuevos procedimientos: nos permite crear propiedades o convertir variables públicas de una clase determinada en propiedades. • Asistentes para crear cajas de mensajes: permite además de crear las cajas de mensajes, previsualizarlas para ver el resultado. • Asistentes para cadenas de conexión: permite crear las cadenas de conexión con bases de datos a través de ADO.NET, ODBC, OLEDB, etc. • Asistentes para creación de bloques Select Case en el caso de VB.NET y bloques switch en el caso de C#, a partir de valores o listas enumeradas enum. Para encontrar código más deprisa Figura 1.Ventana de versión de MZ-Tools 4.0 e información sobre su licencia • Búsqueda avanzada: permite realizar una búsqueda mostrando los resultados en un árbol, agrupando los datos por proyecto, archivo, clase, etc. Permite incluso eliminar los resultados de la lista que no nos interesen o bien refrescarla. Se puede además almacenar tantas listas de resultados como nos interese, a través de fichas en un TabControl. ¿Para qué sirven los complementos en un entorno de desarrollo? ¿Qué ventajas nos ofrece MZ-Tools 4.0 a los desarrolladores? La productividad en el desarrollo de aplicaciones es una de las bazas fuertes de este complemento. Sus opciones son accesibles rápidamente desde el menú de Visual Studio .NET, desde la barra de botones Figura 2. Ejecución en Visual Studio .NET 2003 de la utilidad de búsqueda de MZ-Tools 4.0 • Elementos de código favoritos: nos permite crear una lista con los procedimientos, clases, etc., que usamos con más frecuencia, para poder acceder a ellos de forma más cómoda. • Soluciones favoritas: de la misma forma, se puede crear una lista con las soluciones que usamos con más frecuencia. <<dotNetManía Los entornos de desarrollo como Visual Studio .NET, nos ofrecen a los desarrolladores un conjunto de herramientas que nos permiten hacer la vida más fácil al desarrollador. Sin embargo, existen muchos campos sin cubrir, sigue habiendo muchas tareas arduas y complejas de llevar a cabo, muchas de ellas triviales y repetitivas que de estar simplificadas o automatizadas, nos ahorrarían mucho trabajo. Ahí es donde entra en juego el complemento MZ-Tools 4.0. 47 << dnm.mvp.online MZ-Tools no es otra cosa que un add-in o complemento para Visual Basic,VBA y Visual Studio .NET. De hecho, existen dos versiones de este complemento, MZ-Tools 3.0 para Visual Basic 5, Visual Basic 6,VBA y MZ-Tools 4.0 para Visual Studio .NET 2002 y Visual Studio .NET 2003 cedimientos. Estos encabezados pueden usar variables predefinidas (nombre, autor, fecha, etc.) o variables de usuario (por ejemplo, el propósito de un procedimiento). • Generación de documentación en formato XML: con esta funcionalidad se genera un archivo XML con toda la información (proyectos, archivos, clases, procedimientos, etc.) de nuestra solución. Es útil si se quiere procesar dicha información mediante algún parser XML para obtener algún tipo de información. • Funciones de desplazamiento: para poder ir al comienzo y final de un procedimiento o de una clase de una manera rápida. Diseñador de formularios Windows mucho más rápido <<dotNetManía • Modo de diseño inteligente: cuando este modo está activado, el complemento establece la posición y el tamaño más adecuado para un control cuando lo soltamos sobre un formulario. Para ello, examina los controles vecinos y obtiene cuales son los mejores valores de tamaño y posición para que queden alineados con los otros controles. Además, establece automáticamente la propiedad TabIndex del control que hemos agregado al formulario. Esta funcionalidad es una de las más innovadoras de la versión 4.0 de este complemento. • Asistente TabIndex: permite revisar o establecer de forma muy cómoda (pulsando un botón) la propiedad TabIndex adecuada a todos los controles de un formulario. • Asistente para nombres de controles: permite establecer la propiedad Text y Name de los controles de un formulario de manera muy rápida mediante un Grid. La propiedad Name se puede generar automáticamente a partir de la propiedad Text del control. Por ejemplo, si a un botón le asignamos el texto “Borrar pedido”, se asignaría automáticamente como nombre del control ButtonBorrarPedido. • Propiedades por defecto de controles o formularios: permite definir qué valores por defecto tendrán las propiedades de un nuevo formulario cuando se agrega a un proyecto, o las propiedades de un control cuando se agrega a un formulario. Por ejemplo, podemos establecer prefijos para los nombres de los controles de forma automática (Button “Btn”, etc.). 48 Documentación de código mucho más rápido • Encabezados: hay funciones que permiten agregar encabezados predefinidos a archivos, clases o pro- Figura 3. Ejecución en Visual Studio .NET 2003 de la utilidad de generación de documentación XML de MZ-Tools 4.0 • Generación de documentación en formato HTML: como la funcionalidad anterior, pero en formato HTML, más adecuado para consumo por parte de programadores, analistas, etc. Ayuda en la mejora del código fuente de las aplicaciones a través de diferentes revisiones • Revisión de código no permitido: permite detectar si algún programador ha empleado construcciones de código que se consideran malas prácticas de programación, por ejemplo, más de un punto de salida en un procedimiento. Se puede parametrizar el tipo de construcciones de código que no están permitidas. • Revisión de código obligatorio: permite detectar si algún programador no ha empleado construcciones de código que son obligatorias, por ejemplo, que cada procedimiento lleve un encabezado que se ajuste a un estándar. Esta funcionalidad también se puede parametrizar. • Revisión de la propiedad TabIndex: comprueba que todos los controles de los formularios de un proyecto tienen el valor de la propiedad TabIndex correcto. • Revisión de las teclas de acceso rápido: comprueba que todos los controles que lo necesitan tienen una << dnm.mvp.online tecla de acceso rápido (la letra subrayada de que permite usar la tecla [Alt]) y que no hay teclas de acceso rápido repetidas. • Revisión de código fuente no usado: informa de procedimientos, variables, parámetros, etc., que no son usados en ninguna parte del código y que por lo tanto, podrían ser eliminados. xionar, sobre todo, cuando nuestra función de desarrollo es frecuente o cuando desarrollamos aplicaciones de cierta envergadura. MZ-Tools es una de estas utilidades que nos harán la vida mucho más fácil y nos permitirá reducir enormemente la curva de desarrollo ganando tiempo y calidad en nuestros desarrollos. Otras funcionalidades Si desea aprender más acerca de MZ-Tools y adquirir una licencia para su entorno, puede hacerlo en: • Estadísticas: permite obtener estadísticas detalladas del número de archivos, clases, procedimientos, etc. de nuestra solución. • Autoguardar: permite guardar automáticamente el código fuente que vamos escribiendo cada pocos minutos. MZ-Tools: http://www.mztools.com MZ-Tools Legal Notice: http://www.mztools.com/legal_notice.htm MZ-Tools es una de estas utilidades que nos harán la vida mucho más fácil y nos permitirá reducir enormemente la curva de desarrollo ganando tiempo y calidad en nuestros desarrollos • Reordenar elementos de código: permite reordenar clases, procedimientos, etc., por nombre, visibilidad, etc. • Colapsar proyectos de una solución en el Explorador de soluciones, muy útil si tenemos muchos archivos. • Portapapeles privados: hasta 9, que permite almacenar trozos de código para uso frecuente. • Herramienta extensible: expone el API que usa internamente para sus operaciones de revisión y permite crear nuevos tipos de revisiones. Por ejemplo, se proporciona el código fuente de una revisión de corrección ortográfica del texto de los controles usando el corrector ortográfico de Microsoft Word. Conclusión Como podemos observar, este complemento no sólo está compuesto por un conjunto de funcionalidades útiles, sino que podríamos afirmar sin dudarlo, que estas funcionalidades podrían ser catalogadas sin problemas como indispensables. El hecho de que nuestra productividad y rentabilidad en las horas que pasamos delante de un ordenador haciendo funciones de desarrollo pueda ser incrementado, es algo que debería hacernos refle- Carlos es MVP de Visual Developer .NET desde Enero de 2004. Respecto a su formación, es Ingeniero Superior de Telecomunicaciones por la Universidad Politécnica de Madrid, pero desde el principio de su carrera profesional se dediCarlos J. Quintero có al mundo de la programación. Lleva trabajando 10 años en el grupo Sogecable (http://www.sogecable.com) dónde desde hace varios años ejerce en el puesto de Jefe de Área de Tecnología en su departamento de desarrollo. Desde el inicio ha trabajado con herramientas de Microsoft desde Visual Basic 3.0 hasta Visual Studio .NET. En el año 2000 comenzó a desarrollar MZ-Tools, objetivo de este artículo, actualizándolo y mejorándolo consecutivamente. MZ-Tools 3.0 es usado hoy día por docenas de miles de desarrolladores en todo el mundo, y la versión MZ-Tools 4.0 comenzó su andadura en el año 2004. Adicionalmente, contribuye asiduamente en los grupos de discusión –especialmente en habla inglesa– sobre lenguajes y tecnologías de desarrollo como Visual Basic, Add-Ins para .NET, etc. Acerca de sus contribuciones a las Comunidades, tiene mención especial el foro público de Yahoo (http://groups.yahoo.com/group/vsnetaddin) dónde Carlos contribuye de forma especial contestando infinidad de preguntas sobre complementos en .NET y organizando la sección de recursos de este foro como documentación, artículos, ejemplos, base de conocimiento, bugs, etc. Podrás contactar con él en la dirección de e-mail [email protected]. Si quieres saber más sobre el programa MVP puedes consultar la página oficial de Microsoft en: http://mvp.support.microsoft.com <<dotNetManía Sobre Carlos J. Quintero 49 << dnm.comunidad.net DOTNETSOLIDARIO El lado humano de la tecnología Aunque habitualmente en esta sección se suelen dar a conocer portales dedicados exclusivamente a temas relacionados con .NET, en esta ocasión queremos presentaros un portal que trata de fusionar iniciativas solidarias con temas relacionados con la tecnología .NET, se trata de DOTNETSOLIDARIO. << En nuestro mundo todo se mueve muy deprisa: conseguir un • Formación .NET, gracias a la colaboración de MSDN se impartirán seminarios relacionados mejor rendimiento en nuestras aplicaciones es algo con las tecnologías .NET, especialmente orienfundamental, tener un microprocesador más veloz es tados al desarrollo Web con ASP.NET imprescindible, y a veces conseguir un milisegundo • Desarrollo .NET, para las ONG que deseen cremas rápido de tiempo de ejecución es un gran logro. ar su Web pero no dispongan de medios se ofreAsí no es de extrañar, que nunca nos hayamos parace la posibilidad de buscar colaboradores, sin ánido un momento a pensar en cuál debe ser el objetivo mo de lucro, que les puedan ayudar a través de final de todos estos avances tecnológicos. DOTNETSOLIDARIO. En este mundo que nos movemos, de las Todas estas ayudas para las ONG ya están disponitecnologías de la información, todo resulta bles en el portal DOTNETSOLIDARIO, le recoa veces muy frío, y por más que busquemos mendamos que lo visite si desea ver más información al nos resulta muy complicado encontrar la conrespecto. Pero si no quieren quedarse sólo en eso, DOTciencia social en nuestro sector. NETSOLIDARIO tiene previsto ir ampliando estos Por este motivo surge una iniciativa que trata de servicios e ir añadiendo otras nuevas ideas para potenencontrar ese lado humano a las tecnologías de la inforciar aún más el apoyo de la tecnología .NET en favor mación, y que se llama DOTNETSOLIDARIO. El de las iniciativas solidarias. Actualmente, están prepaobjetivo de DOTNETSOLIDARIO es dar a conorando una iniciativa para realizar una recogida de matecer los proyectos de las ONG y prestarles ayuda a rial informático para montar escuelas en el tercer muntravés de la tecnología .NET, proporcionándoles do, pero de momento no podemos contar más, esta idea servicios gratuitos para que puedan desarrollar sus está sólo en proyecto. proyectos. Estos servicios gratuitos que ofrece DOTNETSOLIDARIO a las ONG son los El objetivo de DOTNETSOLIDARIO es dar a conocer siguientes: los proyectos de las ONG y prestarles ayuda a través • Hosting ASP .NET, alojade la tecnología .NET, proporcionándoles servicios gramiento gratuito en un sertuitos para que puedan desarrollar sus proyectos. vidor Web que permite la ejecución de paginas ASP y ASP .NET, 50 megas de En la Web de DOTNETSOLIDARIO cabe desespacio en disco, y una cuenta FTP para poder tacar su foro donde se pueden abrir debates, dejar actualizar la web siempre que se desee. También sugerencias y comentarios y opinar de todo lo que dispone de la posibilidad de alojar su base de queráis. También hay disponible un chat, en el cual datos en Microsoft Access. se pueden intercambiar opiniones con otros usuarios • Software .NET. Gracias a la colaboración de tanto de temas solidarios como de cosas relacionaMicrosoft las ONG que vayan a realizar su Web dos con la tecnología .NET. en ASP.NET podrán obtener, sin cargo alguno, una licencia de Visual Studio .NET 2003 Professional. Esta promoción esta limitada a ONG que estén radicadas en España. <<dotNetManía [ 50 1 Finalmente, el dominio de DOTNETSOLIDARIO es dotnetsolidario.com y no dotnetsolidario.info como fue en un principio ] dnm.comunidad.net Y, por supuesto, en el portal DOTNETSOLIDARIO también tenéis información de todas las ONG que se han registrado en DOTNETSOLIDARIO, así como de los proyectos que están realizando estas ONG. Y si estás interesado en colaborar con alguna ONG en alguno de los proyectos que están publicados puedes solicitarlo a través de la Web de DOTNETSOLIDARIO. Pero DOTNETSOLIDARIO no es sólo una Web interesante para las ONG, también dispone de una recopilación de cientos de artículos relacionados con .NET, que va creciendo día a día y que sin duda es de gran utilidad para cualquier desarrollador .NET. Actualmente dispone de más de 500 artículos, pero cada día son añadidos nuevos artículos y por supuesto todos en castellano. Y muy interesante para los desarrolladores es el CLUB DOTNETSOLIDARIO, al que podrá apuntarse todo el que lo desee para estar informado de las últimas novedades relacionadas con la tecnología .NET y los proyectos solidarios que se vayan publicando. El CLUB DOTNETSOLIDARIO pretende ser una comunidad de personas solidarias e interesadas en la tecnología .NET. Por supuesto apuntarse no cuesta nada y CRUCIGRAMA 14. 18. 22. (c) Grupo Weboo 23. Horizontales 6. Importante recurso de programación que se basa en usar una pila y que no existió en el primer lenguaje FORTRAN (E) 11. Nombre que se le da al concepto de que un objeto agrupe métodos (código) y variables (memoria) (E) 12. Característica importante de los lenguajes con jerarquías de tipo, gracias a la cual, un objeto de un tipo te permitirá estar informado de las actividades que vaya realizando la comunidad DOTNETSOLIDARIO. [ “El conocimiento es un bien que crece a medida que se comparte”, esta es la frase que podrá ver en todas las páginas de la web de DOTNETSOLIDARIO y que resume su filosofía. ] Pero su intención es la de ser una comunidad dinámica donde todos sus miembros aporten su granito de arena. Cada uno de la manera que quiera o que pueda, bien aportando ideas, sugerencias, artículos, o cualquier ayuda para poder llevar a cabo iniciativas solidarias, o simplemente para compartir sus conocimientos con los demás. “El conocimiento es un bien que crece a medida que se comparte”. Ésta es la frase que podrá ver en todas las páginas de la Web de DOTNETSOLIDARIO y que resume su filosofía. Por último, recordaros que la Web DOTNETSOLIDARIO podéis verla en http://www.dotnetsolidario.com1. Se trata de una iniciativa sin ánimo de lucro, que está abierta a la colaboración con otras personas u organizaciones. La tecnología no puede olvidar nunca su lado humano; si todos tenemos en mente esta idea, DOTNETSOLIDARIO habrá logrado su objetivo. derivado puede ser asignado a una variable o parámetro declarado como de un tipo base (E) Nombre que se le da a la información general incluida en un ensamblado .NET y que describe a éste (E) Nombre del “objeto” utilizado en una aplicación Web ASP.NET para compartir información entre todas las páginas con independencia de la identidad del cliente (I) Nombre que se le da a la capacidad de poder definir más de un método con el mismo nombre (E) Siglas del protocolo estándar utilizado para el intercambio de objetos en la Web (I) Verticales 1. Atributo que se le pone en .NET a la definición de un tipo para indicar que los objetos de este tipo pueden ser transferidos por la red (E) 2. Especificador de forma de traspaso de parámetros en C# que obliga al método llamado a asignarle un valor al parámetro formal antes de retornar (I) 3. Iniciales del cómo se denominan los controles que se ejecutan en el lado del servidor y mediante los cuales una aplicación Web puede ofrecer interfaces de usuario que se muestran en el cliente según las capacidades de éste (I) 4. Siglas del mecanismo de compilación que aplica el CLR .NET cuando carga un ensamblado (I) Nota La “E” indica que la respuesta es en español, la “I” indica que la respuesta es en inglés) Las soluciones en dotnetmanía.com 5. Siglas del mecanismo de seguridad aplicado durante la ejecución de código en .NET (I) 7. Nombre del indicador existente en el lenguaje C con el que se puede especificar que dos variables ocupan la misma zona de memoria (I) 8. Iniciales de cómo se le llama en un lenguaje a la característica de que no haya diferencia sintáctica en la forma en que se le consulta a un objeto un valor independientemente si este valor es calculado o está representado directamente en memoria por una variable de instancia. C# da soporte a esto mediante las propiedades (E) 9. Nombre correspondiente en VB al this de C# (I) 10.¡Importante revista española dedicada a la tecnología .NET! (E) 13.Especificación de forma de traspaso de parámetros que permite que cuando se le asigne al parámetro formal un valor, éste se asigne sobre el parámetro real. (I) 15.Concepto en .NET que complementa al de la reflexión para poder generar tipos dinámicamente (I) 16.Especificador en C# para indicar múltiples parámetros en la definición de un método (I) 17.Nombre del recurso en Pascal equivalente al struct de C (I) 19.Extensión utilizada por los ficheros de música de Windows Media Player (I) 20. Iniciales del modelo de componentes de Microsoft (I) 21.Iniciales del mecanismo al que está asociado la aplicación del método Finalize en C# (I) dnm.todotnet.qa Dino Esposito Consideraciones de rendimiento Era estudiante de primer año en la universidad, cuando uno de los profesores dijo algo que no he sido capaz de olvidar en los 20 años siguientes. En una clase de Algorítmica, el profesor estaba explicándonos cómo computar el rendimiento de un algoritmo y cómo clasificarlo en el tiempo y en el espacio. Un estudiante alzó su mano y preguntó: ¿nos debería importar realmente el rendimiento?, ¿no basta con comprar una máquina más rápida? Su respuesta, fue sorprendente para la mayoría de nosotros:“Las máquinas rápidas son las más beneficiadas de los algoritmos rápidos”. ¿Qué significa esto para el desarrollador de .NET? << La optimización es -ciertamente- un noble arte, pero, antes de Dino Esposito es redactor de dotNetManía. Formador, consultor y escritor afincado en Roma. Miembro del equipo de Wintellect, Dino está especializado en ASP.NET y ADO.NET. Puede enviarle sus consultas a [email protected] que los artistas se introduzcan en el análisis exhaustivo del código, es preferible contar con un buen grupo de ingenieros que revisen los pilares y fundamentos de la aplicación. Allí es donde tiene lugar la verdadera optimización. Para las aplicaciones ASP.NET, por ejemplo, eso significa reducir el tiempo de descarga entre nodos y mejorar la gestión de estado, quizá utilizando páginas de acceso asíncrono. Con toda seguridad, hay trucos aplicables en todo esto, pero ése es el auténtico objetivo del artista. Y ahí es donde la optimización se convierte en arte noble. Si la aplicación está pobremente diseñada, no hay mucho que los artistas puedan hacer. Si está bien diseñada, las optimizaciones, en cambio, pueden marcar la diferencia. En este artículo, no me referiré específicamente al rendimiento de aplicaciones ASP.NET o Windows. En una sección de Preguntas y Respuestas, las generalizaciones no están permitidas. No puedes sencillamente preguntar ¿cómo puedo hacer que mi aplicación ASP.NET vuele? Las mejoras de rendimiento, aunque pueden categorizarse dentro de un conjunto común de patrones, son sólo aplicables rigurosamente para cada aplicación en concreto y requiere de los artistas e ingenieros el conocimiento interno de todos los recursos implicados. Las preguntas de este mes se refieren al rendimiento, pero son cuestiones específicas, que la mayoría de los lectores encontrarán de utilidad. Yo estaba convencido de que la gestión de cadenas desde una aplicación .NET no era uno de sus puntos fuertes. Ahora me doy cuenta de que son más rápidas que ninguna de las plataformas anteriores, pero no puedo concatenarlas eficientemente. ¿Qué es lo que sucede exactamente? Las cadenas son un tipo de dato muy común en todo tipo de aplicaciones, y no sólo en .NET. Piensas que la optimización del tratamiento de cadenas puede tener un gran impacto sobre el rendimiento general de la aplicación debido a que las cadenas son manejadas con frecuencia. Pero, ¿qué es una cadena en el contexto de .NET? ¿ A pesar de que la gestión de cadenas en .NET es más eficiente que en plataformas anteriores, no puedo concatenarlas eficientemente. ¿Qué es lo que sucede exactamente? ? En .NET, hay dos categorías de tipos básicos: por valor y por referencia. Los tipos por valor son ligeros, y corresponden con tipos usados frecuentemente, tales como byte, char, single, double, long, bool, decimal e int64. Además, incorporan los tipos enum, struct, y, en general, cualquier tipo que here- “ Los tipos por valor son ligeros, y corresponden con tipos usados frecuentemente ” Así pues, ¿crees que una cadena es un tipo por valor o por referencia? En .NET una cadena es un tipo por referencia. Una cadena es una secuencia inmutable de caracteres ordenados. La clase String hereda de System.Object e implementa varias interfaces: IComparable, ICloneable, IConvertible e IEnumerable. Las cadenas son tipos primitivos para los compiladores de C# y Visual Basic .NET, lo que las convierte en un tipo por referencia muy especial. Por “ Cualquier tipo al que nos referimos como clase es un tipo por referencia. ” ejemplo, podemos crear una cadena sin llamar al operador new. Por razones de rendimiento, el CLR gestiona el manejo de la cadena y la marca como sellada (no heredable), evitando así que el usuario cree sus propios tipos de cadenas. Además, las cadenas son inmutables: una vez creadas no pueden modificarse de ninguna forma. Podemos trabajar con ellas, pero no cambiar su valor. Cuando usamos expresiones complejas, que concatenan múltiples cadenas, se generan muchos objetos de vida corta, lo que no supone una merma en el rendimiento, debido a que son fáciles de reciclar por el Recolector de Basura. Y, más importante aún, las cadenas son mantenidas en un depósito global de memoria de forma que las variables de memoria que apuntan al mismo texto, son, en definitiva, punteros a la misma dirección. El CLR mantiene una tabla hash de objetos String y utiliza el texto como clave. Cuando se solicita una cadena, el CLR busca la cadena en la tabla y la recupera de forma rápida y eficiente. Como decía mi profesor, las tablas hash son muy rápidas a la hora de recuperar cualquier dato contenido en la tabla. El secreto principal del rendimiento para las cadenas es precisamente, su carácter inmutable y constante. ¿Qué sucede si concatenas dos cadenas? Si, simplemente, sumas dos cadenas, sucede algo inesperado. Como una cadena no puede ser modificada por diseño, si se añade una cadena a otra, se crea una nueva con el tamaño apropiado para contener la suma de ambas. Para evitar la penalización de esta forma de trabajo, deberíamos utilizar la clase StringBuilder. Esta clase funciona de forma muy similar a cómo funcionaban las cadenas en las plataformas anteriores a .NET. Cuando añadimos una cadena a un StringBuilder, si la capacidad de éste no es suficiente, se incrementa para permitir albergar a la nueva cadena. Los miembros de la clase permiten recomponer la cadena y añadirle formato. Por último, el método ToString(), devuelve el texto almacenado como una cadena clásica, inmutable. Nunca deberíamos utilizar concatenación, sino más bien, usar la clase StringBuilder para componer dos o más cadenas. También es correcto el uso de StringFormat ya que –internamente– utiliza un objeto StringBuilder. <<dotNetManía de de la clase ValueType. Estos tipos se ubican en la pila correspondiente al proceso en ejecución, no en el montón (heap) administrado, y no están sujetos a la destrucción dinámica asociada al Recolector de Basura (Garbage Collector). Una variable que instancia un tipo por valor no se referencia mediante una dirección de memoria, sino que contiene en si misma los campos de esa instancia. Cuando se copia, se efectúa una copia campo a campo. Los tipos por valor no pueden ser heredados. Por otro lado, un tipo por referencia tiene su memoria asociada ubicada en el montón administrado, requiere una inicialización y la presencia del Recolector de Basura. Cualquier tipo al que nos referimos como clase es un tipo por referencia. Cuando asignamos un tipo por referencia a otra variable, sólo se copia la dirección de memoria. Según esto, dos o más variables pueden “apuntar” al mismo objeto físico. Los cambios producidos en cualquiera de las variables pueden afectar a las otras. Los tipos por valor tienen dos representaciones –empaquetada y no empaquetada– (boxed y unboxed), mientras que los tipos por referencia sólo existen en forma empaquetada. Empaquetar quiere decir convertir un tipo por valor en un tipo por referencia. El empaquetado no siempre es necesario. Sólo se requiere cuando se pasa un tipo por valor a un tipo por referencia. Esta técnica tiene un impacto en el rendimiento porque debe crearse un nuevo objeto en el montón administrado, copiar los campos de datos y, finalmente, se devuelve la dirección del nuevo objeto así creado. Una aplicación hecha exclusivamente de tipos por referencia se ejecutaría más lentamente, y por consiguiente, lo mismo le sucedería a un aplicación que utilizase constantemente las técnicas de empaquetado y desempaquetado. [email protected]@dotnetmania.com << dnm.todotnet.qa 53 54 [email protected]@dotnetmania.com <<dotNetManía << dnm.todotnet.qa Una parte de mi vieja aplicación VB6 estaba diseñada para enlazar objetos dinámicos. En VB6 utilizaba enlace tardío. Cuando he migrado la aplicación a .NET utilicé bastante la introspección (Reflection). ¿Supone esto un problema? El rendimiento no es malo, pero me gustaría poder mejorarlo todavía más. El enlace tardío en .NET puede obtenerse a través de métodos de la clase Activator. Resumiendo, Activator es aproximadamente equivalente a CreateObject, en el sentido de que toma un ensamblado y un nombre y devuelve un objeto. El método CreateInstance de la clase Activator es bastante rápido, pues recupera el constructor de la clase y lo invoca. El problema viene después. ¿Cómo trabajamos con los objetos creados de esta forma? Si conoces algo del objeto, –como por ejemplo su interfaz– podemos hacer un “casting” a dicho interfaz y trabajar sin penalizaciones de rendimiento. Si no, tenemos que utilizar Reflection y eso tiene una penalización. Sin embargo, el costo de la introspección es fundamentalmente, el de los pasos iniciales. Para llamar a un objeto de forma indirecta, necesitas obtener previamente un Proxy. Esto es lo que supone una mayor penalización y puede optimizarse situando el Proxy en una caché. Para poner un ejemplo, un Proxy es la clase MethodInfo, que suministra el método Invoke para realizar la llamada. Si es posible, deberíamos evitar la creación indiscriminada de objetos del tipo xxxInfo. La reutilización de estos objetos, mejora el rendimiento. Si usas Reflection para importar componentes externos (similar al modelo “plug-in”) apenas tienes problemas. Las aplicaciones que pueden potencialmente tener problemas de rendimiento son aquellas que investigan las características de los objetos y, entonces, deciden qué hacer. En este caso, lo que realmente necesitas es un objeto que se modifique dinámicamente para acomodar al objeto llamado. La ejecución de tu código lleva una carga superior a lo habitual debido a la sobrecarga de la introspección. ¿Podría ser de ayuda la generación dinámica de código? .NET utiliza la generación dinámica de código y la compilación dinámica en varios de sus subsistemas –desde ASP.NET a la seriación XML–. Esto mejora el rendimiento, pero sólo si se acompaña de las apropiadas políticas de uso de cachés, para minimizar la generación ¿ En una vieja aplicación VB6 enlazaba objetos dinámicos y utilizaba enlace tardío. Cuando migré la aplicación a .NET utilicé bastante reflection. ¿Supone esto un problema? ? de código. Para mí, donde cobra mayor sentido es en los subsistemas fundamentales de tu aplicación. No resulta adecuado para temas puntuales. El costo de implementar un mecanismo similar suele ser trivial. El API a analizar es CodeDOM. Una pregunta rápida. ¿Deberíamos utilizar DataReaders en lugar de DataSets para mejorar el rendimiento? Se trata de objetos completamente distintos, de forma que la pregunta no refleja una comparación posible. Los Readers son un recurso excelente cuando se necesita consumir datos. Los DataSets son muy buenos cuando es preciso almacenar los datos en memoria para su utilización posterior. Si necesitas –simplemente– consumir datos, los DataSets no son una buena opción; si necesitas mover datos entre capas de la aplicación, los DataReaders son, simplemente, imposibles de utilizar. Los sistemas bien diseñados deben de tener esto en cuenta a la hora de seleccionar la ruta adecuada. Consumir datos significa realizar cualquier tipo de operación sobre un DataRow sin guardarlo en memoria local. Si esto es lo que necesitas hacer –recorrer datos hasta el final del conjunto de resultados– los DataReaders son adecuados. El uso de un DataSet en este contexto no añade ninguna ventaja, más bien al contrario, diría yo. ¿ ¿Deberíamos utilizar DataReaders en lugar de DataSets para mejorar el rendimiento? ? Cuando rellenas un DataSet obtienes un Reader, recorres los datos y creas filas en una tabla local. Tras esto, para trabajar con los datos, tienes que irlos analizando para realizar tu labor. Como puedes ver, tiene lugar una doble navegación, lo que no es bueno. Por otro lado, yo no suscribo la afirmación de que el uso de los Readers es preferible al uso de DataSets en cualquier situación. Hay escenarios donde no es posible utilizar un Reader en lugar de un DataSet. Si tienes que pasar datos de la capa de lógica intermedia a la capa de datos, sólo puedes hacerlo utilizando un DataSet. El único escenario donde el DataSet y los DataReaders pueden usarse indistintamente (con preferencia para los Readers por temas de rendimiento) es en el enlace de datos ASP.NET. Algunos controles ASP.NET aceptan un DataReader como origen de datos. Esto puede conllevar una optimización, siempre que no se pueda permitir el uso de cachés locales en el servidor Web. Pero si puedes permitírtelo, en mi opinión, la forma auténtica de optimización es el uso de cachés. Traducción por Marino Posadas dnm.laboratorio.net Pedro Pozo Con esta pequeña aplicación, podemos cambiar de versión ASP.NET con tan solo un clic de nuestro ratón. Nos resultará muy útil cuando estemos testeando nuestras aplicaciones, pudiendo comprobar que se ejecutan bien en distintas versiones de ASP.NET. Tal y como podemos ver en la figura, al utilizar ASP .NET Switcher vemos en la parte superior de la ventana un listado de todas nuestras aplicaciones ASP .NET y en la parte inferior las distintas versiones de ASP .NET que tengamos instaladas en nuestra máquina. << Localization Manager Cada vez son más las páginas Web que presentan sus contenidos personalizados dependiendo del país o del lenguaje del usuario que las visita. Si está pensando en realizar una Web de ese tipo, el componente Localization Manager le puede resultar muy útil. Se trata de un componente totalmente gratuito, lo podrá descargar de Internet e instalar en sólo unos minutos, y además también incluye unos ejemplos donde poder comprobar el funcionamiento de este componente y ver lo sencillo que es utilizarlo en sus páginas Web. Por ejemplo, si desea comprobar cuál es el lenguaje que está utilizando el usuario que se conecta a su Web, tan solo deberá llamar al método siguiente: ActiveUp.WebControls.LocalizationManager.ParseLanguageCode Pedro Pozo es redactor de dotNetManía. Es es consultor e-Bussines. Ingeniero Técnico Informático de Sistemas y Webmaster del portal para desarrolladores .NET Framework Clikear.com Si desea obtener el país desde el cuál está accediendo el usuario que se conecta a su Web, tan solo deberá llamar al método siguiente: ActiveUp.WebControls.LocalizationManager.ParseCountryCode Seleccionando la aplicación que deseemos ejecutar podemos ir cambiando de versión de ASP.NET tan solo seleccionándolo en ASP.NET Version Switcher. En realidad lo que hace ASP.NET Switcher es realizar una llamada al comando ASPNET_REGIIIS que viene incluido en el Framework .NET para cambiar de una versión a otra, sencillo pero práctico. Además es totalmente gratuita y ocupa muy poco, por lo cual se convierte en una herramienta muy práctica que siempre nos puede ahorrar tiempo en nuestras pruebas. Ficha técnica Nombre ASP .NET Version Switcher Versión 1.1 Fabricante DenisBauer.com http://www.denisbauer.com/NETTools/ Web ASPNETVersionSwitcher.aspx Categoría Utilidades Precio GRATIS Valoración La lógica que utiliza el método ParseCountryCode no es 100% fiable, esto es debido a que Localization Manager utiliza un pequeño truco, y es comprobar la información que es enviada por el navegador al servidor en cada petición de página Web. Esto supone un pequeño inconveniente y es que si, por ejemplo, estuviésemos en España y tuviésemos nuestro navegador en inglés, configurado con idioma por defecto en ingles, pues el componente Localization Manager nos devolvería nuestro país e idioma, el inglés, aunque realmente estamos conectándonos en España. A pesar de esto, Localization Manager se trata de un componente que puede ser muy práctico en cualquier desarrollo multilenguaje, y es una alternativa más económica que otros componentes que se basan en la dirección IP del usuario como método para detectar el país desde el que se conecta. Ficha técnica Nombre Localization Manager Versión 1.1 Fabricante Active VP http://www.activeup.com/products/ Web components/localizationmanager Categoría Componentes Precio GRATIS Valoración <<dotNetManía << ASP.NET Version Switcher 55 << dnm.laboratorio.net << ISAPI URL Mapper Si en la máquina donde desarrollas tienes instalado Personal Web Server podrás haber comprobado que sólo puedes crear un sitio Web. Esto supone un inconveniente cuando tienes en desarrollo varias webs, ya que no puedes definir varios websites. Si está en esta situación le será de gran utilidad ISAPI URL Mapper. Se trata de un filtro ISAPI que nos permite crear más de un website, identificando a los diferentes sites que definamos. Para utilizar esta utilidad deberemos registrar como un filtro ISAPI la DLL URLMAPPER.DLL que va incluida en el paquete. Posteriormente añadiremos el cgi URLMapperConfig al directorio en el que se << Sharp PDF encuentra la Web que deseamos crear y que nos permitirá configurarlo. De esta forma ya tendremos listo nuestro servidor Web y podremos crear tantos websites virtuales como queramos. Ficha técnica Nombre ISAPI URL Mapper Versión 1.1 Fabricante newObjects[ ] http://www.newobjects.com/product.asp?Cat Web egory=46&Story=94 Categoría Utilidades Precio 40$ Valoración reales. Además estos elementos tienen sus propias clases que los definen como por ejemplo pdfTable, pdfPageMaker, pdfPersistanPage y pdfBookmarkNode. A continuación veamos un ejemplo de lo sencillo que es utilizar Sharp PDF en nuestros desarrollos, y en un alarde de originalidad he creado un ejemplo en el que creamos un documento PDF que contiene el texto “Hola Mundo”. <<dotNetManía Sharp PDF es una librería que nos permite crear documentos en formato PDF dinámicamente en nuestras aplicaciones. Se trata de un componente de código abierto y totalmente gratuito, desarrollado por completo con C#. De fácil manejo, nos servirá tanto para nuestras aplicaciones Web como para las aplicaciones de escritorio. Y como disponemos del código fuente, siempre podremos introducir las pdfDocument miDocumento = new pdfDocument("EJEMPLO","PEDRO"); modificaciones que considepdfPage miPagina = miDocumento.addPage(); remos necesarias para adap- miPagina.addText("Hola Mundo",200,450,predefinedFont.csHelvetica,20); tarlo a las necesidades de miDocumento.createPDF(@"c:\holamundo.pdf"); nuestro proyecto. Su forma de trabajar es muy sencilla, inicialmente se crea un objeto del tipo pdfDocument, que representa Sin duda un componente muy recomendable, al documento PDF. A nuestro objeto pdfDocument le por las posibilidades que nos ofrece, por su sencideberemos ir añadiendo los objetos pdfPage, que llez de manejo y a un precio inigualable. representan las páginas del documento y que a su vez Ficha técnica están formadas por objetos del tipo pdfObjects. Los pdfObjects se pueden dividir en dos tipos: Nombre Sharp PDF • Objetos Reales. Son elementos escritos directamenVersión 2.0 Beta 2 te en el código PDF y que son creados llamando Web http://sharppdf.sourceforge.net a métodos de la clase pdfPage. Algunos ejemplos Categoría Componente de estos elementos son text, paragraph, line, image. Precio GRATIS (licencia LGPL) • Objetos Abstractos. Son elementos que son creValoración ados a partir de una colección de elementos 56 << dnm.biblioteca.net dnm.biblioteca.net La Cara Oculta de C# Ian Maartens Editorial: IntSight España ISBN: 84-607-9860-7 Páginas: 465 Publicado: Enero, 2004 Estamos ante uno de los mejores libros que hemos visto en castellano sobre ADO.NET (el autor es de habla hispana, a pesar del nombre). Una obra pensada para el desarrollador por un desarrollador, que aborda los problemas diarios y las dificultades típicas que se presentan más allá de la presentación de datos en un control. Además, contiene no pocas dosis de humor bien entendido. Según sus propias palabras: Windows Forms Programming in C# Chris Sells Editorial: Addison-Wesley ISBN: 0321116208 Páginas: 736 Publicado: Agosto, 2003 Muchos expertos de talla (como Jeff Prosise, Brian Randell o Fritz Onion) han recomendado esta obra como “imprescindible” para el desarrollador de aplicaciones Windows. Salvando las distancias, también la recomendamos aquí, no sólo por su temática (más escasa de lo que parece), sino por la calidad del contenido y la exposición. Aborda absolutamente todo lo necesario para la correcta construcción de aplicaciones Windows, sin concesiones a la galería, y sin eludir ninguno de los entresijos necesarios para su desarrollo, optimización e implantación. Y un detalle más para animar a su lectura: Chris fue contratado el pasado año para trabajar en uno de los equipos de desarrollo de Visual Studio 2005: ¿imaginan cuál? <<dotNetManía << “La primera parte presenta el lado menos conocido de Transact SQL y SQL Server: las técnicas más útiles desde el punto de vista de un programador; la segunda parte explica qué es ADO.NET, cómo funcionan las capas conectadas y desconectadas, y cómo se pueden presentar los datos extraídos de una base de datos en una aplicación basada en Windows Forms. Finalmente, la tercera parte cubre temas poco frecuentes: desde .NET Remoting, pasando por los servicios Web, para terminar explicando el funcionamiento de los servicios corporativos de COM+. El último capítulo ofrece técnicas y sugerencias de utilidad para crear servidores de capa intermedia (cifrado, colas de mensajes, envío de correo electrónico...)”. Absolutamente recomendable. 57 << dnm.desvan noticias.noticias Marino Posadas La nueva XBox 2,dispondrá de una potencia de cálculo similar a la de los superordenadores:un billón de operaciones por segundo J. Allard, Microsoft Corporate Vice President y Chief XNA (la plataforma para la construcción de juegos de Microsoft), presentó a primeros de marzo la que será nueva versión de la popular consola. “La potencia de cálculo será similar a la de un superordenador, y permitirá la creación de toda una nueva generación de juegos que aprovechará toda la potencia del hardware actual, para ofrecer una experiencia de usuario sin precedentes hasta ahora.", afirmaba Allard. Microsoft ha contado con la colaboración de dos grandes compañías en la creación de la plataforma: IBM por parte de los procesadores, y ATI Technologies para el soporte gráfico. El anuncio casi coincide en el tiempo con la presentación del nuevo XNA Studio, la plataforma de desarrollo específicamente pensada para juegos, basada en Visual Studio 2005. A este respecto, Chris Satchell, Director de XNA en Microsoft., afirmaba: “No sólo se hacen necesarias nuevas plata- formas de hardware para poder ofrecer a los jugadores más de lo que demandan, sino que el verdadero reto consiste en integrar todo el proceso de desarrollo, para que se automaticen muchas rutinas que hasta ahora obligaban a volver a programar a menudo las mismas. Gracias a XNA, los desarrolladores podrán plasmar sus visiones y crear juegos de mayor calidad en menos tiempo. IBM duplica la capacidad del que era –hasta ahora– el ordenador más potente del mundo. Blue-Gene/L: 72 trillones de teraflops. Los investigadores de IBM en Lawrence Livermore, California, han comunicado la adición de 32.000 nuevos procesadores al superordenador Blue-Gene/L, que ya contaba con otros 32.000, lo que le convertía en el más potente del planeta. La iniciativa es parte de un proyecto todavía más ambicioso, tendente a conseguir un ensamblado hardware de cerca de 130.000 procesadores, para el próximo mes de junio, lo que debería conducir a un nivel de rendimiento cercano a los 300 trillones de teraflops. Documentos en la Red Cómo subir y bajar ficheros mediante ASP.NET: File Upload with ASP.NET (http://www.codeproject.com/aspnet/fileupload.asp) lo explica con todo lujo de detalles. Eso sí, hay que darse de alta antes en CodeProject, sitio ya recomendado en esta revista. Generar una aplicación de principio a fin (con C#), Excelente y completo documento cuyo título habla por sí mismo acerca de las buenas prácticas en la construcción de aplicaciones para .NET. Aunque está pensado para el lenguaje C#, es fácilmente extrapolable a cual otro de los lenguajes de la plataforma. Creado por Microsoft en su “Universidad .NET” de Argentina, y disponible en http://www.microsoft.com/spanish/msdn/comunidad/uni.net/CSharpnet/about.asp Enlaces del mes Sitio Web de Lluís Franco: http://www.uyssoft.com con información sobre VB.NET, C#, ADO.NET, que recopila artículos, recursos, utilidades y muchas más cosas sobre .NET. <<dotNetManía The Visual Basic Team: ¿Quiere saber qué se está cociendo 58 en al cocina del desarrollo de la próxima versión de Visual Basic .NET? Este es uno de los sitios para verlo. Controles, novedades del lenguaje, comportamiento en acceso a datos y casi de todo (en inglés: http://blogs.msdn.com/vbteam/category/4754.aspx). A Daniel, que ha querido venir al mundo también en 2005. Daniel, venga, que vienes a tiempo de probar la Beta 2 :-). ¡Suerte chaval!
Documentos relacionados
Motor para el reconocimiento óptico de caracteres en .NET OCR.NET
demostración práctica de Windows Mobile 6.0 con .NET CF 3.5. Intercepción de SMS, integración con el teléfono —así como con las tareas y correo de Pocket Outlook— y buenas prácticas en el uso de no...
Más detalles