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

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