Cuando hay mucho para escoger
Publicado por Rómulo Rodríguez 14 Octubre 2007 en General, Herramientas, Mono, c++. Etiquetas: No Tags. english • españolEsto me sucede a menudo. Tiene uno un problema por resolver y gracias a los tiempos en que vivimos tiene a su disposición prácticamente un océano de herramientas.
Así pasó esta semana en la oficina. Teníamos que desarrollar un pequeño programa que tomara datos de otro software mediante comunicación interprocesos. Cóntabamos solamente con una API que los desarrolladores de dicho software habían publicado. El detalle es que estaba en C++.
Rápidamente pusimos a funcionar un programita tipo cónsola que usaba esta librería en C++. Hurgando aquí y allá en la Internet logramos integrar un montón de código disperso para que el programa hiciera lo que queríamos. Leía los datos del software residente mediante la API, elaboraba con estos datos una estructura en XML y luego enviaba este montón de datos vía HTTP a un servidor web que almacanaba y mostraba dichos datos. ¡Eureka! dijimos todos. Funciona de maravilla. Ahora es cuestión de ponerle cara de programa en Windows y agregarle los periquitos usuales.
Y la cosa no parece tan fácil como suena.
Primero pensamos en hacer la interfaz de usuario en .NET o en mono aprovechando las destrezas que tenemos en C#. Rápidamente creamos la interfaz de usuario con la funcionalidad básica, e incluso con alguna que otra cosa avanzada. Pero craso error, la interfaz gráfica no lo es todo. Perdimos un montón de tiempo tratando de compilar una dll con las clases del API que pudiéramos utilizar en el programa en C#. Resulta que cuando usamos comunicación interprocesos leemos y escribimos en áreas de memoria que no le gusta a la máquina de .NET. A lo mejor hay forma de que funcione, pero el tiempo lo teníamos en contra. Mezclar código “unmanaged” con código “managed” no es cosa fácil. Después de horas y horas de errores y errores tiramos la toalla.
La solución evidente fue hacer el programa en C++ con código nativo de Windows. Suena algo largo y tedioso, sobre todo para los que conocen el API de Windows.
Usemos MFC pensamos. Después de ir para allá y para acá con las diferentes versiones de Microsoft Visual Studio decidimos que tampoco es algo fácil. Debiera haber algo más sencillo.
Nos acordamos del excelente Dev-C++ de Bloodshed Software. Lo descargamos pero vimos que es muy poderoso pero sin siquiera algo como MFC. Seguimos buscando y encontramos a Smartwin una librería sencilla para ventanas licenciada GPL. Ya empezábamos a ver luz.
Luego de descargar Dev-C++ y Smartwin nos enteramos que existe un IDE que los integra a los dos de manera impecable. Se trata de Sally, A Simple C++ IDE. Sally corre en .NET 1.1 así que lo bajamos también y empezamos a configurarlo para gcc y Smartwin. En menos de una hora todo estaba listo.
Creamos una aplicación Smartwin y le integramos el código del API y las librerías HTTP. En pocas horas ya teníamos lo que queríamos. Y todo con software libre licenciado GPL.
Falta todavía algo para que esta pequeña aplicación salga de su fase de prototipo, pero la funcionalidad básica está completa y ahora lo que queda es la fase de integración con el resto de la arquitectura y embellecerla un poco para que parezca hecha por alguien que sabe lo hace jeje…
le habéis echado un vistazo a las wxWidgets
wxWidgets también lo revisamos, tiene la ventaja sobre Smartwin de ser crossplatform. No obstante como lo que estamos haciendo es solo para windows decidimos irnos por la solución minimalista. Sally es una lindura de IDE.
Romulo hace unos 4 años me toco hacer algo mas o menos similar, pro un lado teníamos un API que se exponía solo en c++(Software AG Entirex) y por el otro lado una en C ANSI(Lib. SAP RFC) en ambos caso se crearon pequeños wrappers sobre las APIs expuesta que nos hacía de bridge entre la API y el código no manejado.
Desde C# lo que hicimos fue usar el TAG dllimport y solo devolver entre ambos mundos Strings (En un caso uno formateado como un XML y otro como un string por espacios).
Sufrimos de leaks memorys pero fue hasta que vimso como hacer un correcto free del memory pero la appz soporta como uno 12000 usuarios hoy día.
Esa fue mi experiencia y realmente pude hacer interopratibilidad sin trauma aparente.
Zeitan, si, supongo que con un poco de más tiempo habríamos podido hacer que funcionara, hemos hecho un montón de aplicaciones con usos de dll’s externas sin problemas en el pasado. Pero acá la cosa fue algo diferente por culpa de la presión del tiempo. Uno de los problemas más importantes fue convertir la clase inicial en C++ en una dll que pudiera utilizarse en C#. Las dll por lo general no pueden exportar clases que puedan instanciarse sino más bien funciones, eso por culpa de la forma en que se manejan los objetos en ambos ambientes (C# y C++). Eso hizo que cambiáramos el código para convertirlo en funciones exportables. Luego tal como dices usamos dllimport para usarlas en el código C#, pero el detalle es que nuestro API requiere reservar segmentos de memoria que comparte en modo de escritura y lectura con el software con el que estamos haciendo la IPC. El caso es realmente complejo y a cada rato teníamos excepciones de sistema por conflictos en la asignación de memoria. Creo que con un poco más de tiempo pudiéramos ver como evitamos estos problemas. Nuestro API usa las funciones de Windows ReadProcessMemory, WriteProcessMemory, VirtualAllocEx, VirtualFreeEx y SendMessage entre otras para que tengas una idea.
Uno de los grandes dilemas de integrar tantas tecnologias, y las cosas se ponen feas cuando los proveedores de harware te dan solo un DLL. Agradece que, por lo que veo, el API por lo menos es usable y no tan feo como otros que he visto que requieren el Handler de una ventana Windows para recibir mensajes (y si mi programa no tiene GUI???? jajaj falta de visión para no decir ignorancia de los proveedores)
Es este caso yo fuese experimentado con otra solución, siguiendo un poco la filosofia Unix, crearía un programa no interactivo que sería la interfaz con el dispositivo (un daemon) y crearia la interfaz con la tecnología de mi preferencia (Java, .Net, etc…). usando un protocolo de comunicación sencillo entre el GUI y el daemon
Tu blog es … genial! no lo perderé de vista!