Fco. Javier Ceballos Sierra Profesor titular de la Escuela Politcnica Superior Universidad de Alcal
http://www.fjceballos.es
Visual C++. Programacin Avanzada en Win32 Fco. J avier Ceballos Sierra De la edicin: RA-MA 1999
MARCAS COMERCIALES: Las designaciones utilizadas por las empresas para distinguir sus productos suelen ser marcas registradas. RA-MA ha intentado a lo largo de este libro distinguir las marcas comerciales de los trminos descriptivos, siguiendo el estilo de maysculas que utiliza el fabricante, sin intencin de infringir la marca y slo en beneficio del propietario de la misma.
RA-MA es una marca comercial registrada.
Se ha puesto el mximo empeo en ofrecer al lector una informacin completa y precisa. Sin embargo, RA-MA Editorial no asume ninguna responsabilidad derivada de su uso, ni tampoco por cualquier violacin de patentes ni otros derechos de terceras partes que pudieran ocurrir. Esta publicacin tiene por objeto proporcionar unos conocimientos precisos y acreditados sobre el tema tratado. Su venta no supone para el editor ninguna forma de asistencia legal, administrativa ni de ningn otro tipo. Caso de precisarse asesora legal u otra forma de ayuda experta, deben buscarse los servicios de un profesional competente.
Reservados todos los derechos de publicacin en cualquier idioma.
Ninguna parte de este libro puede ser reproducida, grabada en sistema de almacenamiento o transmitida en forma alguna ni por cualquier procedimiento, ya sea electrnico, mecnico, reprogrfico, magntico o cualquier otro, sin autorizacin previa y por escrito de RA-MA; segn lo dispuesto en el artculo 534-bis del Cdigo Penal vigente sern castigados con la pena de arresto mayor y multa quienes intencionadamente, reprodujeren o plagiaren, en todo o en parte, una obra literaria, artstica o cientfica.
Editado por: RA-MA Editorial Ctra. Canillas, 144 28043 MADRID Telfono: 91 381 03 00 Telefax: 91 381 03 72 Correo electrnico: rama@arrakis.es Servidor Web: http://www.ra-ma.es ISBN: 84-7897-344-3 Depsito Legal: Autoedicin: Fco. J avier Ceballos Filmacin e impresin: Albadalejo, S.L. Impreso en Espaa Primera impresin: Enero 1999
NDICE
PRLOGO .............................................................................................................. XXI CAPTULO 1. AADIR CARACTERSTICAS A UNA APLICACIN ........ 1 VENTANA DE PRESENTACIN .................................................................... 1 CARGAR UNA APLICACIN UNA SOLA VEZ ............................................ 5 INFORMACIN DEL SISTEMA ...................................................................... 9 GetSystemInfo ............................................................................................... 9 GetVersionEx ................................................................................................. 12 GlobalMemoryStatus ..................................................................................... 13 GetDiskFreeSpace .......................................................................................... 15 GetSystemDirectory ....................................................................................... 15 Acerca de ........................................................................................................ 16 FORMULARIOS FLOTANTES ........................................................................ 20 SALIR DE WINDOWS DE UNA FORMA CONTROLADA ........................... 23 EJ ECUTAR UNA APLICACIN WINDOWS O DE CONSOLA ................... 24 ABRIR O IMPRIMIR UN DETERMINADO FICHERO .................................. 30 AADIR UN ICONO A LA BARRA DE TAREAS ......................................... 31 MENS CONTEXTUALES .............................................................................. 36 AADIR UN SISTEMA DE AYUDA A UNA APLICACIN ........................ 38 Soporte de ayuda proporcionado por AppWizard .......................................... 39 Compilar los ficheros de ayuda ...................................................................... 40 Diseando el sistema de ayuda ...................................................................... 41 Construir el fichero de ayuda ......................................................................... 46 Ayuda sensible al contexto ............................................................................ 48 Funcin WinHelp ........................................................................................... 49 Propiedad Context help de las ventanas ......................................................... 49 HTML Help Workshop .................................................................................. 49 VIII VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Convertir un fichero de ayuda WinHelp ........................................................ 50 Vincular el sistema de ayuda HTML a una aplicacin .................................. 51 Ayuda HTML sensible al contexto ................................................................ 52 DISTRIBUCIN DE UNA APLICACIN ........................................................ 54 Colocar el icono de la aplicacin en el men Inicio ...................................... 58 Asignacin de grupos a componentes ............................................................ 59 Asignar componentes a cada tipo de instalacin ............................................ 60 Ventana de presentacin y fichero leame.txt ................................................. 60 Asignar ficheros a los grupos de ficheros ...................................................... 61 Recursos ......................................................................................................... 62 Construir las imgenes de los discos de distribucin ..................................... 62
CAPTULO 2. HILOS ........................................................................................... 65 CONCEPTO DE PROCESO .............................................................................. 65 HILOS ................................................................................................................. 66 Estados de un hilo .......................................................................................... 67 Crear de un hilo .............................................................................................. 68 CWinThread .............................................................................................. 69 Finalizar un hilo ............................................................................................. 70 Planificacin de hilos ..................................................................................... 70 Asignacin de prioridades .............................................................................. 71 Prioridad relativa de un hilo ...................................................................... 71 HILOS UTILIZANDO LA BIBLIOTECA MFC ............................................... 72 COMUNICACIN ENTRE HILOS ................................................................... 78 Comunicacin utilizando variables globales .................................................. 79 Comunicacin utilizando mensajes ................................................................ 80 SINCRONIZACIN DE HILOS ........................................................................ 82 Secciones crticas ........................................................................................... 82 Creacin de una seccin crtica ................................................................. 87 Exclusin mutua ............................................................................................. 89 Semforos ....................................................................................................... 93 Problema del productor-consumidor con semforos ................................. 95 Eventos ........................................................................................................... 102 Eventos con inicializacin manual ............................................................ 104 Eventos de inicializacin automtica ........................................................ 104 Problema del productor-consumidor con eventos ..................................... 105 Espera activa y pasiva .................................................................................... 109 ELEGIR EL TIPO DE SINCRONIZACIN ...................................................... 113
NDICE IX
CAPTULO 3. COMUNICACIONES .................................................................. 115 COMUNICACIONES POR EL PUERTO SERIE.............................................. 117 Aplicacin Win32 para comunicaciones va RS232 ...................................... 120 Registro de Windows ..................................................................................... 123 Interfaz de comunicaciones ............................................................................ 124 Funcin Iniciar .......................................................................................... 126 Funcin Terminar ...................................................................................... 126 Funcin EstablecerConexion .................................................................... 127 Funcin MensajeDeError .......................................................................... 130 Funcin ConfigurarDisCom ...................................................................... 131 Controlar eventos ...................................................................................... 134 Funcin LeerCaracteresPuerto .................................................................. 138 Funcin EscribirCarsPuerto ...................................................................... 141 Funcin CortarConexion ........................................................................... 142 INTERFAZ DEL USUARIO .............................................................................. 143 ENVIAR Y RECIBIR DATOS ........................................................................... 147 CONTROL DE COMUNICACIONES ............................................................... 148 Tipo VARIANT ............................................................................................. 153 Manipular las comunicaciones ....................................................................... 156 Interfaz de comunicaciones ............................................................................ 159 Funcin Iniciar .......................................................................................... 161 Funcin Terminar ...................................................................................... 161 Funcin EstablecerConexion .................................................................... 162 Funcin ConfigurarDisCom ...................................................................... 163 Controlar eventos ...................................................................................... 164 Funcin LeerCaracteresPuerto .................................................................. 166 Funcin EscribirCarsPuerto ...................................................................... 166 Funcin CortarConexion ........................................................................... 167 INTERFAZ DEL USUARIO .............................................................................. 168 ENVIAR Y RECIBIR DATOS ........................................................................... 172
CAPTULO 4. CONTROLES ............................................................................... 175 CONTROL IMAGE LIST .................................................................................. 177 Crear una lista de imgenes ............................................................................ 177 CONTROL LIST ................................................................................................ 178 CListCtrl y CListView ................................................................................... 179 Aadir elementos a un control list ................................................................. 182 Aadir columnas a un control list .................................................................. 184 Aadir los subelementos ................................................................................ 185 Vistas del control list ...................................................................................... 185 X VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Iconos grandes .......................................................................................... 187 Iconos pequeos ........................................................................................ 187 Lista .......................................................................................................... 187 Detalles ..................................................................................................... 188 Elemento seleccionado ................................................................................... 188 CONTROL TREE ............................................................................................... 189 CTreeCtrl y CTreeView ................................................................................. 190 Elementos de un control tree .......................................................................... 192 Elementos padre e hijo ................................................................................... 193 Aadir elementos a un control tree ................................................................ 193 Seleccionar un elemento ................................................................................ 197 Sincronizacin de los controles tree y list ...................................................... 198
CAPTULO 5. COMPONENTES SOFTWARE ................................................. 203 COMPONENTES FRENTE A BIBLIOTECAS ................................................ 204 MODELOS DE COMPONENTES ..................................................................... 204 OLE ..................................................................................................................... 205 COM .................................................................................................................... 206 Fundamentos de los objetos COM ................................................................. 206 DCOM ................................................................................................................. 209 OLE 2 .................................................................................................................. 212 ActiveX ............................................................................................................... 213 CONTENEDOR ActiveX ................................................................................... 214 Incrustar un objeto ......................................................................................... 215 Vincular un objeto .......................................................................................... 216 Esqueleto de la aplicacin .............................................................................. 217 Activar un objeto utilizando el ratn .............................................................. 219 Eliminar un objeto ActiveX ........................................................................... 227 SERVIDOR ActiveX .......................................................................................... 228 Esqueleto de la aplicacin .............................................................................. 229 Completar el servidor ActiveX ...................................................................... 232 SERVIDOR ActiveX AUTOMATIZADO ......................................................... 236 Esqueleto de la aplicacin .............................................................................. 237 Completar el servidor ActiveX automatizado ................................................ 241 Aadir una interfaz al servidor ....................................................................... 245 CLIENTE AUTOMATIZADO ........................................................................... 249 INTERFACES ..................................................................................................... 256 Interfaz personalizada .................................................................................... 256 Interfaz de tipo dispinterface .......................................................................... 257 Interfaz dual ................................................................................................... 260 Aadir una interfaz dual ................................................................................. 262 NDICE XI
Implementar la interfaz dual .......................................................................... 264 CONTROL ActiveX ........................................................................................... 269 Crear un objeto ActiveX ................................................................................ 270 Aadir una interfaz al control ActiveX .......................................................... 271 Completar el control ActiveX ........................................................................ 273 Propiedades de un control ActiveX ................................................................ 277 Aadir una propiedad normal ................................................................... 278 Aadir una propiedad parametrizada ........................................................ 280 Aadir una propiedad comn .................................................................... 281 Aadir una propiedad ambiental ............................................................... 282 Hoja de propiedades .................................................................................. 283 Aadir una nueva pgina de propiedades ................................................. 286 Aadir una pgina de propiedades comn ................................................ 287 Aadir mtodos .............................................................................................. 288 Aadir eventos ............................................................................................... 289 Persistencia ..................................................................................................... 292 CONTENEDOR PARA UN CONTROL ActiveX ............................................. 293
CAPTULO 6. ATL ................................................................................................ 297 QU ES ATL ...................................................................................................... 297 VISUAL C++Y ATL ......................................................................................... 298 CONTROL ActiveX ........................................................................................... 300 Crear un proyecto para un control ActiveX ................................................... 300 Crear un control ActiveX ............................................................................... 301 Clase del control ............................................................................................. 304 Visualizar datos en el control ActiveX .......................................................... 305 Aadir una interfaz al control ActiveX .......................................................... 309 Completar el control ActiveX ........................................................................ 313 Propiedades de un control ActiveX ................................................................ 317 Aadir una propiedad definida por el usuario ........................................... 318 Aadir una propiedad comn .................................................................... 322 Aadir una propiedad ambiental ............................................................... 324 Hoja de propiedades .................................................................................. 324 Aadir una pgina de propiedades comn ................................................ 330 Aadir mtodos .............................................................................................. 330 Aadir eventos ............................................................................................... 332 Persistencia ..................................................................................................... 336 CONTENEDOR PARA UN CONTROL ActiveX ............................................. 336 PROGRAMACIN AVANZADA CON ATL ................................................... 338 Propiedades asncronas .................................................................................. 338 Utilizacin del portapapeles ........................................................................... 345 XII VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Funcionalidad necesaria para utilizar el portapapeles ............................... 348 El control pone datos en el portapapeles ................................................... 353 El control obtiene datos del portapapeles ................................................. 355 Arrastrar y soltar ............................................................................................ 359 Control como fuente de datos ................................................................... 359 Control como destino de los datos ............................................................ 361 Controles con interfaz dual ............................................................................ 365
CAPTULO 7. MULTIMEDIA ............................................................................. 367 ARQUITECTURA MULTIMEDIA ................................................................... 368 TIPOS DE DATOS MULTIMEDIA .................................................................. 369 MULTIMEDIA MCI........................................................................................... 370 CD de audio ................................................................................................... 375 Audio por forma de onda ............................................................................... 381 Audio y vdeo entrelazado ............................................................................. 383 Ejemplo de multimedia MCI .......................................................................... 386 MULTIMEDIA UTILIZANDO LA API DE WINDOWS ................................. 393 Servicios de audio .......................................................................................... 393 Interfaz de control de medios ......................................................................... 396 Dispositivos MCI ........................................................................................... 397 rdenes MCI .................................................................................................. 398 Abrir un dispositivo ....................................................................................... 399 Tipos de dispositivos ...................................................................................... 399 Reproducir un fichero .................................................................................... 400 Detener un dispositivo ................................................................................... 400 Cerrar un dispositivo ...................................................................................... 400 CD de audio ................................................................................................... 400 Audio por forma de onda ............................................................................... 407 Audio y vdeo entrelazado ............................................................................. 409 PONER SONIDO A UNA APLICACIN ......................................................... 412 HIPERMEDIA .................................................................................................... 415 Cargar una imagen ......................................................................................... 420 Establecer y probar zonas activas .................................................................. 422 Guardar y recuperar las zonas activas ............................................................ 427 ANIMACIN DE GRFICOS........................................................................... 431 Un ejemplo de animacin ............................................................................... 433 ANIMACIN CON CDIB .................................................................................. 439 Un ejemplo de animacin con CDIB ............................................................. 441
NDICE XIII
CAPTULO 8. BIBLIOTECAS DINMICAS .................................................... 459 CREACIN DE UNA DLL EN Win32 .............................................................. 460 Fichero de cabecera (.h) ................................................................................. 462 Fichero fuente (.c o .cpp) ............................................................................... 462 Fichero de definicin de mdulos (.def) ........................................................ 465 LLAMANDO A LAS FUNCIONES DE LA DLL ............................................. 465 Enlace esttico ................................................................................................ 467 Enlace dinmico ............................................................................................. 468 RECURSOS EN UNA DLL ............................................................................... 471 Acceso a los recursos en una DLL ................................................................. 474 OBJ ETOS COM COMO ALTERNATIVA A LAS DLLs ................................. 480 Esqueleto de la aplicacin .............................................................................. 481 Aadir mtodos .............................................................................................. 482 Utilizacin del servidor COM ........................................................................ 484 Obtener un puntero a una interfaz ............................................................. 485 Llamando a las funciones de la interfaz .................................................... 488 Clase _com_ptr_t ...................................................................................... 489 Directriz #import ....................................................................................... 491 Aadir otra interfaz ................................................................................... 494 Aplicacin de tipo consola ........................................................................ 499
CAPTULO 9. BASES DE DATOS ...................................................................... 503 CLASES ODBC PARA ACCESO A BASES DE DATOS ................................ 504 ACCESO A UNA BASE DE DATOS UTILIZANDO ODBC .......................... 510 Integridad referencial ..................................................................................... 511 Caractersticas de la aplicacin ...................................................................... 511 Registrar la base de datos ............................................................................... 512 Crear una aplicacin con soporte ODBC para BD ......................................... 513 Diseo de la plantilla de dilogo .................................................................... 517 Asociar los controles con los campos del conjunto de registros .................... 518 Ejecutar la aplicacin ..................................................................................... 519 Aadir otro conjunto de registros ................................................................... 520 Llenar la lista desplegable con la lista de idiomas ......................................... 521 Establecer un filtro ......................................................................................... 524 Establecer un parmetro ................................................................................. 525 Editar, aadir y borrar registros ..................................................................... 528 Editar un registro....................................................................................... 529 Aadir un registro ..................................................................................... 530 Borrar un registro ...................................................................................... 533 Abandonar una operacin de edicin o de adicin ................................... 533 XIV VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Registros borrados .......................................................................................... 534 Conjunto de registros vaco ............................................................................ 535 OBJ ETOS ACTIVEX PARA ACCESO A DATOS ........................................... 536 Modelo de objeto ADO .................................................................................. 537 Acceso a los datos con ADO .......................................................................... 539 ADO Comparado con RDO y DAO ............................................................... 540 ACCESO A UNA BASE DE DATOS UTILIZANDO ADO ............................. 541 Crear una aplicacin con soporte ADO para BD ........................................... 542 Asistente ADO Data Bound Dialog .......................................................... 542 Extensiones Visual C++para ADO .......................................................... 543 Diseo de la plantilla de dilogo ............................................................... 547 Abrir una conexin ................................................................................... 550 Moverse por la base de datos .................................................................... 553 Ejecutar la aplicacin ..................................................................................... 554 Aadir nuevos controles al dilogo Recordset ............................................... 555 Aadir otro conjunto de registros ................................................................... 556 Acceder a los campos de un registro .............................................................. 558 Llenar la lista desplegable con la lista de idiomas ......................................... 559 Establecer un filtro ......................................................................................... 560 Habilitar o inhabilitar controles ..................................................................... 561 Actualizar los datos ........................................................................................ 563 Actualizacin inmediata ................................................................................. 564 Actualizacin por lotes ................................................................................... 565 Cancelar modificaciones ................................................................................ 566 Aadir un nuevo registro ................................................................................ 567 Borrar un registro ........................................................................................... 569 Conjunto de registros vaco ............................................................................ 570 Caractersticas soportadas .............................................................................. 571 Nmero de registros ....................................................................................... 571
CAPTULO 10. INTERNET ................................................................................. 573 QU ES INTERNET? ....................................................................................... 573 Intranet ........................................................................................................... 574 Extranet .......................................................................................................... 574 Terminologa Internet .................................................................................... 574 SERVICIOS EN INTERNET ............................................................................. 577 Correo electrnico .......................................................................................... 578 Conexin remota (telnet)................................................................................ 579 Transferencia de ficheros (ftp) ....................................................................... 580 Noticias (news) .............................................................................................. 582 Conversaciones .............................................................................................. 583 NDICE XV
Herramientas para bsqueda de informacin ................................................. 584 World Wide Web (WWW) ....................................................................... 584 Gopher ...................................................................................................... 586 Archie ........................................................................................................ 587 La informacin en Internet ............................................................................. 589 PGINAS WEB .................................................................................................. 589 Qu es HTML ................................................................................................ 590 Etiquetas bsicas HTML ................................................................................ 590 Etiquetas de formato de texto ......................................................................... 591 URL ................................................................................................................ 593 Enlaces entre pginas ..................................................................................... 594 Grficos .......................................................................................................... 595 Marcos ............................................................................................................ 596 Pginas dinmicas .......................................................................................... 597 VBScript en una pgina Web .................................................................... 599 Insertar un control ActiveX en una pgina Web ....................................... 600 ActiveX Control Pad ...................................................................................... 602 El editor de textos ..................................................................................... 603 El editor de objetos ................................................................................... 604 El asistente de VBScript o J Script ............................................................ 605 El editor de plantillas ................................................................................ 607 Distribucin y licencia de ActiveX Control Pad ............................................ 611 Controles ActiveX para pginas Web ............................................................ 611 Control Marquee ....................................................................................... 612 Control HotSpot ........................................................................................ 612 Control ActiveMovie ................................................................................ 612 Documentos ActiveX ..................................................................................... 613 Objetos de Internet Explorer .......................................................................... 613 Objeto window .......................................................................................... 614 Objeto frames ............................................................................................ 616 Objeto history ........................................................................................... 617 Objeto navigator........................................................................................ 617 Objeto location .......................................................................................... 618 Objeto script .............................................................................................. 618 Objeto document ....................................................................................... 618 Objeto link ................................................................................................ 620 Objeto anchor ............................................................................................ 620 Objeto form ............................................................................................... 620 Objeto element .......................................................................................... 620 Microsoft FrontPage Express ......................................................................... 621
XVI VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
CAPTULO 11. VISUAL INTERDEV ................................................................. 623 ENTORNO DE PROGRAMACIN DE VISUAL INTERDEV ....................... 623 Esquema html ................................................................................................. 624 Cuadro de herramientas .................................................................................. 624 Esquema de secuencias de comandos ............................................................ 625 Vista Diseo ................................................................................................... 625 Vista Cdigo .................................................................................................. 625 Vista rpida .................................................................................................... 626 DISEO DE UN SITIO WEB ............................................................................ 626 Trabajar sin conexin ..................................................................................... 628 Crear un proyecto Web .................................................................................. 628 Crear y organizar pginas .............................................................................. 630 Incluir grficos ............................................................................................... 632 Establecer vnculos ........................................................................................ 632 Tema y diseo ................................................................................................ 633 Barra de exploracin ...................................................................................... 634 FrontPage y Visual InterDev .......................................................................... 635 Distribucin de la aplicacin Web ................................................................. 635 INTEGRAR MULTIMEDIA .............................................................................. 635 PGINAS ASP ................................................................................................... 639 Modelo ASP ................................................................................................... 640 Secuencias de rdenes .................................................................................... 640 Lenguajes de secuencias de rdenes .............................................................. 642 Objetos ASP predefinidos .............................................................................. 643 Objeto Response ....................................................................................... 643 Objeto Request .......................................................................................... 643 Objeto Server ............................................................................................ 643 Objeto Session .......................................................................................... 644 Objeto Application .................................................................................... 644 Objeto ObjectContext ............................................................................... 644 OBTENER INFORMACIN MEDIANTE FORMULARIOS .......................... 644 Diseo de un formulario HTML .................................................................... 646 Procesar la informacin del formulario en el cliente ..................................... 647 Procesar formularios en el servidor ................................................................ 648 ACCESO A UNA BASE DE DATOS ................................................................ 649 Diseo del acceso a bases de datos ................................................................ 650
CAPTULO 12. APLICACIONES DE INTERNET ........................................... 655 CREAR UN EXPLORADOR WEB ................................................................... 655
NDICE XVII
WININET ............................................................................................................ 658 Acceso a un servidor HTTP ........................................................................... 659 Acceso a un servidor FTP .............................................................................. 662 SOCKETS ........................................................................................................... 672 WinSock ......................................................................................................... 674 Comunicacin orientada a conexin .............................................................. 674 Servidor ..................................................................................................... 676 Cliente ....................................................................................................... 685 Enviar mensajes ............................................................................................. 694
CAPTULO 13. DIRECTX.................................................................................... 701 COMPONENTES DE DIRECTX ....................................................................... 701 DIRECTX Y COM.............................................................................................. 702 CMO TRABAJ A DIRECTX ............................................................................ 704 CREAR UNA APLICACIN DIRECTDRAW ................................................. 706 Crear el objeto DirectDraw ............................................................................ 708 Fijar el nivel de cooperacin .......................................................................... 709 Fijar la resolucin de la tarjeta grfica ........................................................... 710 Crear las superficies de visualizacin ............................................................ 710 Crear los objetos de recorte ............................................................................ 714 Crear la paleta de colores ............................................................................... 717 Asociar la paleta con las superficies de visualizacin .................................... 718 Crear los sprites .............................................................................................. 719 Acceso directo a la memoria de la superficie ............................................ 721 Emplear la GDI y hacer una copia desde un DIB ..................................... 722 Bucle de animacin ........................................................................................ 723 Salir de la aplicacin ...................................................................................... 725 DIRECTDRAW Y LA BIBLIOTECA MFC ...................................................... 726 APLICACIN DIRECTDRAW ......................................................................... 727 Arquitectura de la aplicacin ......................................................................... 728 Crear superficie .............................................................................................. 733 Cargar mapa de bits ........................................................................................ 735 Crear paleta de colores ................................................................................... 737 Obtener la profundidad de color .................................................................... 739 Objeto de recorte ............................................................................................ 740 Creacin del entorno DirectDraw .................................................................. 741 Fijar el modo de vdeo ................................................................................... 743 Mostrar la nueva escena generada .................................................................. 749 Dibujar en una ventana .................................................................................. 752 La ventana principal recibe el foco ................................................................ 753 La paleta de colores cambi ........................................................................... 754 XVIII VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
La resolucin de vdeo cambi ...................................................................... 754 La posicin de la ventana se modific ........................................................... 755 El tamao de la ventana cambi ..................................................................... 756 Mens a pantalla completa ............................................................................. 758 Destruir los objetos DirectDraw ..................................................................... 761 Cambiar a pantalla completa .......................................................................... 761 Sincronizacin con el espacio vertical ........................................................... 763 Cerrar la aplicacin pulsando la tecla Esc ...................................................... 763 Posicin del ratn ........................................................................................... 764 Animacin de imgenes ................................................................................. 764 Interfaz de programacin ............................................................................... 766 Variables y recursos .................................................................................. 766 Funciones de la interfaz de programacin ................................................ 767 Funcin OnUsrCreaEscena ....................................................................... 768 Funcin OnUsrCambiaResolucion ........................................................... 769 Funcin OnUsrRestauraSuperficies .......................................................... 770 Funcin OnUsrTick .................................................................................. 771 Funcin OnUsrDestruyeEscena ................................................................ 773 Funcin TamSuperficie ............................................................................. 773 Funcin UsrDibujaMosaico ...................................................................... 774 Funcin UsrDibuja .................................................................................... 775 DIRECT3D ......................................................................................................... 775 Interfaces Direct3DRM utilizadas .................................................................. 776 Pasos para crear una aplicacin Direct3DRM ................................................ 778 Objetos DirectDraw y Direct3DRM, superficies y modo de vdeo ............... 779 Enumerar los drivers de dispositivo ............................................................... 783 Crear el dispositivo y la superficie de proyeccin ......................................... 788 Crear los objetos de la escena ........................................................................ 792 Notificar mensajes a Direct3DRM ................................................................. 796 Crear el bucle de animacin ........................................................................... 798 API de Direct3DRM ...................................................................................... 801 Luces ......................................................................................................... 801 Secuencias de animacin .......................................................................... 802 Marcos ...................................................................................................... 802 Objetos visibles ......................................................................................... 803 Crear objetos 3D ....................................................................................... 803 Cargar un objeto 3D .................................................................................. 805 Perspectivas ............................................................................................... 806 Transformaciones ...................................................................................... 806 Clase Direct3DRMObject ......................................................................... 809 Crear la escena ............................................................................................... 809 DIRECTSOUND ................................................................................................. 816 Interfaces a utilizar ......................................................................................... 817 NDICE XIX
Aplicacin DirectSound ................................................................................. 817 API de DirectSound ....................................................................................... 819 Crear el objeto DirectSound ...................................................................... 819 Nivel de cooperacin de la aplicacin ...................................................... 820 Crear un buffer de sonido ......................................................................... 820 Efecto Doppler .......................................................................................... 821 Reproducir un buffer de sonido ................................................................ 822 Detener un sonido ..................................................................................... 822 Volumen .................................................................................................... 822 Balance ...................................................................................................... 823 Conos de sonido ........................................................................................ 823 Crear los objetos de sonido para aplicacin ................................................... 826 Interfaz de programacin ............................................................................... 827 Cargar sonidos .......................................................................................... 828 Reproducir un buffer de sonido ................................................................ 829 Detener un sonido ..................................................................................... 829 Sonidos utilizados .......................................................................................... 830
APNDICE A. CDIGOS DE CARACTERES .................................................. 835 UTILIZACIN DE CARACTERES ANSI CON WINDOWS .......................... 835 J UEGO DE CARACTERES ANSI ..................................................................... 836 UTILIZACIN DE CARACTERES ASCII ....................................................... 837 J UEGO DE CARACTERES ASCII .................................................................... 838 CDIGOS EXTENDIDOS ................................................................................. 839 CDIGOS DEL TECLADO ............................................................................... 840
APNDICE B. NDICE ALFABTICO .............................................................. 841
PRLOGO
Para facilitar el desarrollo de aplicaciones para Windows escritas en C, Microsoft introduce en 1992, C/C++7.0 y la biblioteca de clases MFC 1.0. Las investiga- ciones demostraron que debido al nivel de dificultad de aprender y utilizar no slo C++, sino el conjunto de clases de las MFC, muchos desarrolladores se quedaban en el intento de migrar a C++. Por este motivo fue creado en febrero de 1993 Vi- sual C++1.0, para facilitar a los desarrolladores la migracin a C++. Con Visual C++se introdujo una tecnologa de desarrollo innovadora a base de asistentes con una nueva versin de las MFC ms potente; la 2.0. La biblioteca MFC 2.0, compatible con la versin anterior, permiti implementar una arquitec- tura de aplicacin que facilitaba enormemente el desarrollo de aplicaciones. Los asistentes que permitan generar esta nueva arquitectura basada en las clases MFC, evitaban escribir muchas de las lneas de cdigo necesarias para construir una aplicacin. Esto es, los asistentes generaban aplicaciones para Windows, sin necesidad de escribir lneas y lneas de cdigo. Por ello, Visual C++se convirti en el camino ms corto para el desarrollo de aplicaciones C++para Windows, combinando, adems, un alto rendimiento con una comodidad en el uso. Posteriormente, en abril de 1993, Microsoft present OLE 2.0. Con OLE 2.0 los desarrolladores podan implementar objetos que interactuaban entre s sin im- portar cmo actuaba cada objeto especficamente. Ms an, podan utilizarse apli- caciones enteras como componentes, lo que haca ms fcil la integracin de aplicaciones y como consecuencia la combinacin de informacin. No obstante, hasta que Visual C++1.5 y la biblioteca MFC 2.5 no estuvieron disponibles, no fue cmodo desarrollar aplicaciones OLE 2.0. A partir de este momento, los des- arrolladores tuvieron asistentes para crear objetos OLE cliente, servidor o conte- nedor con pocos esfuerzos. Asimismo, esta biblioteca tambin inclua soporte XXII VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
completo de ODBC para facilitar la programacin y el acceso a bases de datos lo- cales o remotas. El fuerte inters de los desarrolladores por crear aplicaciones de 32 bits (basa- das en Win32 y OLE) as como tener flexibilidad para dirigirse a mltiples plata- formas (Windows y Windows NT para Intel y RISC, Windows 9x y Macintosh) condujo a Microsoft a crear Visual C++2.0 y la biblioteca MFC 3.0 totalmente compatible con las versiones anteriores. De esta forma los desarrolladores podan continuar manteniendo sus aplicaciones de 16 bits con Visual C++1.5x y los des- arrolladores de 32 bits las suyas con Visual C++2.x. El compilador C++de Visual C++2.0 ya incorporaba las ltimas caractersti- cas de C++; esto es, plantillas de C++y los manipuladores de excepciones que sus antecesores incorporaban a base de macros. Visual C++2.0 aportaba entre otras caractersticas: la creacin de aplicaciones de 32 bits, hilos (threads) y un espacio de memoria plana (eliminando los segmentos de 64K). Asimismo, la biblioteca MFC 3.0 aada soporte OLE de 32 bits y ODBC. En realidad Visual C++2.0 fue un trampoln para sus predecesores (Visual C++n.x, Visual C++6.0). Visual C++6.0 presenta la tecnologa de compilacin ms avanzada para producir aplicaciones de 32 bits ms rpidas y de menor tamao. Tambin incor- pora las caractersticas y palabras clave ms actuales del estndar ANSI. Las nue- vas MFC&T combinan la fiabilidad y productividad de la biblioteca MFC con la biblioteca ATL (Active Template Library). Hace ms fcil el desarrollo de softwa- re basado en componentes (soporte COM). Incorpora un gran nmero de nuevos elementos diseados para explotar las tecnologas de Internet. Visual C++6.0 proporciona varias formas de trabajo con bases de datos. Por ejemplo, utilizando la biblioteca de clases MFC, podemos recurrir a las clases DAO (Data Access Objects - objetos de acceso a datos) o a las clases ODBC (Open DataBase Connectivity - conectividad abierta de bases de datos). Pero las tecnologas actuales de acceso a datos tienen que satisfacer los nuevos escenarios demandados por las empresas, tales como los sistemas de informacin basados en la Web. En esta lnea, Microsoft ofrece utilizar OLE DB como un proveedor de datos y objetos ADO (ActiveX Data Objects - objetos ActiveX para acceso a da- tos), como tecnologa de acceso a datos, argumentando que el acceso a datos ba- sado en OLE DB y ADO es adecuado para una gama amplia de aplicaciones, desde pequeos procesos en estaciones de trabajo a aplicaciones Web a gran esca- la. OLE DB es un conjunto de interfaces COM que puede proporcionar acceso uniforme a los datos guardados en diversas fuentes de informacin. El modelo de objeto ADO define una coleccin de objetos programables, que soportan el mode- lo de objeto componente (COM) y la automatizacin OLE, que pueden interac- cionar con la tecnologa OLE DB. El modelo de objeto de ADO, comparado con PRLOGO XXIII
otros objetos de acceso a datos como RDO o DAO, tiene menos objetos y es ms simple de utilizar. Este libro, escrito con la versin 6 de Visual C++, es continuacin del publi- cado anteriormente, Visual C++ Aplicaciones para Win32. Por lo tanto, para abordar su contenido el autor ha supuesto que el lector conoce todo lo expuesto en el ttulo anteriormente citado. Este libro trata temas ms avanzados, como el sis- tema de ayuda HTML, hilos, comunicaciones RS-232, controles ActiveX, tecno- loga COM, biblioteca ATL, dispositivos MCI, bibliotecas dinmicas, acceso a bases de datos, Internet, Visual Interdev, aplicaciones de Internet, DirectDraw, Direct3DRM y DirectSound. Todos los temas se han documentando con abundan- tes ejemplos resueltos, lo que le facilitar el aprendizaje. Este libro es el cuarto de una coleccin de cuatro libros orientados al desarro- llo de aplicaciones con C/C++. Entre los cuatro, y en el orden especificado, cu- bren los siguientes aspectos: programacin con C, programacin orientada a objetos con C++, desarrollo de aplicaciones para Windows basadas en objetos, y programacin avanzada en Windows incluyendo Internet. El primero, Curso de programacin C/C++, abarca todo lo relativo a la pro- gramacin estructurada con C. Tambin incluye diversos algoritmos de uso comn as como estructuras dinmicas de datos. El segundo, Programacin orientada a objetos con C++, estudia como su nombre indica el desarrollo de aplicaciones orientadas a objetos. Esta tecnologa es imprescindible conocerla si queremos desarrollar aplicaciones utilizando bi- bliotecas de clases como las MFC&ATL de Microsoft Visual C++. El tercero, Visual C++ - Aplicaciones para Win32, le ensea fundamental- mente cmo desarrollar aplicaciones para Windows (aplicaciones con una interfaz grfica basada en ventanas). Y ste, el cuarto, Visual C++ - Programacin avanzada, complementa al li- bro anterior abordando temas ms complejos, a los que me he referido anterior- mente. Agradecimientos He recibido ideas y sugerencias de algunas personas durante la preparacin de es- te libro, entre las que se encuentran, cmo no, mis alumnos, que con su inters por aprender me hacen reflexionar sobre objetivos que a primera vista parecen inal- canzables, pero que una vez logrados sirven para que todos aprendamos; a todos ellos les estoy francamente agradecido. XXIV VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
En especial, quiero expresar mi agradecimiento a Alfons Gonzlez por sus ideas que siempre son bienvenidas, a Oscar Garca Poblacin y Rafael Torcida por sus buenas recomendaciones y aportaciones, y a David Jurado Gonzlez por su participacin en la correccin de esta obra, por sus aportaciones a la misma y en especial, porque sin su empeo y colaboracin este libro posiblemente no habr- a incluido DirectX. Tambin, quiero agradecer a Microsoft Ibrica la cesin de los programas in- cluidos en el CD-ROM. Francisco Javier Ceballos Sierra http://www.fjceballos.es/
Faltan pginas...
CAPTULO 3 F.J.Ceballos/RA-MA
COMUNICACIONES
Este captulo presenta tcnicas de comunicacin con otras mquinas utilizando el puerto serie. Antes de empezar el desarrollo de una aplicacin que implemente comunicaciones serie, resulta til hacer una breve descripcin del funcionamiento bsico de la propia interconexin RS-232. Las seales disponibles en un conector RS-232 estn pensadas nicamente pa- ra asegurar la correcta transmisin y recepcin de datos desde un equipo denomi- nado DTE (Data Terminal Equipment - Equipo terminal de datos) a un DCE (Data Communication Equipment - Equipo de comunicacin de datos). Un DTE es generalmente un ordenador y un DCE un mdem. El enlace estndar entre un DTE y un DCE se puede ver en la figura siguiente.
116 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
La funcin de cada una de las seales es como sigue: Seal Nombre Direccin Funcin TxD Transmitted Data hacia DCE Salida de datos DTE RxD Received Data hacia DTE Entrada de datos DTE RTS Request To Send hacia DCE DTE desea cambiar a modo transmisin CTS Clear To Send hacia DTE DCE listo para transmitir DSR Data Set Ready hacia DTE DCE listo para comunicar con DTE Signal Common Lnea comn del circuito (masa) DCD Data Carrier Detect hacia DTE Detectar si est conectado DTR Data Terminal Ready hacia DCE Pone a trabajar al mdem RI Ring Indicator hacia DTE Anuncia una llamada TxD se encarga de transportar los datos serie hasta el mdem. Para ello, han tenido que activarse RTS, CTS, DSR y DTR. RxD, recepcin de datos, no depende de ninguna otra funcin RS-232. RTS tiene como misin conmutar un mdemsemi-duplex entre modos de re- cepcin y transmisin. Cuando el DTE quiere transmitir, informa al mdem de su deseo activando esta patilla. Cuando el mdem conmuta para transmisin, lo in- forma al DTE activando la patilla CTS, indicando que ya puede enviar los datos. El mdem origen no transmite ni activa su DSR hasta recibir el tono de res- puesta del mdem remoto. DCD, deteccin de seal de lnea recibida, se activa cuando el mdem recibe una portadora remota. En mdems semi-duplex, DCD se activa nicamente en el mdem receptor. Una vez que el mdem est conectado a la lnea, DTR deber permanecer ac- tiva mientras dure la conexin; si se inhibe, se produce la desconexin, interrum- piendo bruscamente el enlace. Adems del enlace estndar, existen otros, como la conexin denominada mdem nulo (cable de seis hilos), utilizada generalmente para transferir ficheros entre dos ordenadores. Esta conexin, como su nombre indica, no es en absoluto un mdem, sino una conexin directa entre dos ordenadores (DTE) para comuni- carse siguiendo las reglas lgicas del protocolo RS-232. Otra solucin para la co- municacin DTE-DTE ms sencilla todava, es la conexin de dos hilos (TxD y RxD). CAPTULO 3: COMUNICACIONES 117
De lo expuesto puede deducirse que para que exista una comunicacin entre dos equipos tiene que haber un acoplamiento entre ellos, y dicho acoplamiento puede realizarse por software o por hardware. El acoplamiento hardware slo es posible si ambos equipos estn fsicamente conectados mediante un cable. Se suele realizar mediante las seales DTR/DSR o bien utilizando simplemente las seales secundarias RTS/CTS. El acoplamiento software no siempre es posible, ya que para que pueda darse, los equipos deben reconocer caracteres de control. En un acoplamiento software es el receptor el que controla el acoplamiento. Lo hace de la forma siguiente: Cuando su cola de entrada est llena, enva un carcter de desconexin (nor- malmente ASCII_XOFF - 0x13). Cuando el transmisor recibe este carcter se detiene. Cuando la cola de entrada del receptor puede recibir ms caracteres, enva un carcter de conexin (normalmente ASCII_XON - 0x11). Cuando el transmisor recibe este carcter reinicia el envo de caracteres. COMUNICACIONES POR EL PUERTO SERIE La gestin de los puertos de comunicacin no es una tarea fcil. Lo primero que hay que pensar es que los datos llegan a los puertos de forma asncrona; es decir, su llegada es imprevisible. Esto sugiere que el dato que llega tiene que procesarse inmediatamente, puesto que pueden llegar otros datos. De esta tarea se encarga el hardware del PC, de forma que cuando detecta la llegada de un dato, interrumpe el flujo normal del proceso para ceder el control a la rutina de proceso de comuni- caciones. Esta rutina tiene que ser una rutina de Windows en lugar de una rutina de la aplicacin. Esto es as por dos razones: ### Windows debe mantener el control de la multitarea. En efecto, si la llegada de un dato hiciera que se transfiriera el control del procesador a su aplicacin, Windows perdera su habilidad para gestionar la multitarea. Esto quiere decir que Windows tiene que estar entre la aplicacin y el hardware. ### Windows no puede dirigir el dato recibido directamente a la aplicacin. La razn es que los datos que se reciben en el puerto de comunicaciones no lle- gan con la identidad de la aplicacin que los tiene que recibir. Por lo tanto, Windows tiene que guardar en un buffer los datos que llegan para una aplica- cin. Para qu aplicacin, debe determinarse por adelantado; esto es, su apli- 118 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
cacin debe haberle pedido a Windows la propiedad del puerto utilizando la funcin CreateFile. Cuando una aplicacin solicita a Windows la propiedad de un puerto, Win- dows slo se lo dar si ninguna otra aplicacin lo tiene. Por el mismo motivo, mientras su aplicacin tiene el control de un puerto, Windows se lo prohbe a las dems aplicaciones que lo soliciten. Cuando su aplicacin finalice la operacin de E/S con un puerto, debe dejar el control del mismo para que otras aplicaciones puedan utilizarlo, lo cual requiere llamar a la funcin CloseHandle. Como ejemplo, vamos a realizar una aplicacin que permita transferir datos entre dos ordenadores personales. Para probar la aplicacin, debe conectar va puerto de comunicaciones los dos ordenadores. Una vez realizada la conexin, asegrese de que est bien hecha utilizando un paquete de comunicaciones co- mercial, como el programa HiperTerminal de Windows. Una forma de realizar esta aplicacin sera utilizando las funciones de la API de Win32 que se indican en la tabla siguiente: Funcin Descripcin CreateFile Abrir un puerto de comunicaciones SetCommMask Eventos que sern atendidos SetupComm Tamao de las colas de E/S PurgeComm Terminar operaciones pendientes y limpiar colas GetCommState Obtener las caractersticas del puerto (estructura DCB) SetCommState Establecer las caractersticas del puerto ReadFile Leer datos WriteFile Enviar datos CloseHandle Cerrar un puerto de comunicaciones Otras funciones disponibles en la API de 32 bits son: SetCommTimeouts, EscapeCommFunction, WaitCommEvent, GetLastError, ClearCommError, BuildCommDCB, etc. No obstante, la forma ms sencilla de trabajar con el puer- to serie en aplicaciones de 32 bits es utilizando el control de comunicaciones mscomm32.ocx (Microsoft Communications Control), cuestin que veremos ms adelante en este mismo captulo. Para establecer una comunicacin utilizando la API de 32 bits, siga estos pasos: 1. Abra el puerto de comunicacin. Para realizar esta operacin, llame a la fun- cin CreateFile con los argumentos: puerto de comunicaciones (COM1, COM2, etc.), modo de acceso (leer y/o escribir), modo de comparticin, atri- butos de seguridad, accin a tomar tanto si existe el fichero como si no existe, CAPTULO 3: COMUNICACIONES 119
y atributos del fichero. Esta funcin devuelve un handle que identifica el puerto de comunicaciones abierto, o el valor ERROR_FILE_NOT_FOUND si el puerto no est disponible. 2. Establezca la mscara de comunicaciones para especificar los eventos que sern atendidos. Para realizar esta operacin, llame a la funcin SetComm- Mask. 3. Defina el tamao de los buffers de las colas de entrada y salida. Utilice para ello la funcin SetupComm. No obstante, esta operacin no siempre es nece- saria, puesto que existen dos buffers definidos por omisin. A continuacin, limpie estos buffers invocando a la funcin PurgeComm. 4. Construya una estructura de tipo DCB que especifique la configuracin del puerto (DCB - device control block). Para ello, llame a la funcin GetComm- State para obtener la configuracin inicial del puerto y, a partir de estos valo- res iniciales, modifique los miembros de inters de la estructura DCB. Otra posibilidad es utilizar la funcin BuildCommDCB con los argumentos: defi- nicin del puerto y estructura DCB. La definicin del puerto es una cadena de caracteres con un formato igual al utilizado por los argumentos de la orden mode (com2:9600,n,8,1). Por lo tanto esta funcin slo modifica los miem- bros velocidad de transmisin, paridad, bits por carcter y bits de parada de la estructura DCB especificada. 5. Establezca la configuracin del puerto. Para realizar esta operacin, llame a la funcin SetCommState pasando como argumento la estructura DCB, en la que previamente se almacen dicha configuracin. 6. Cuando quiera enviar datos al puerto de comunicaciones, utilice la funcin WriteFile, que tiene los siguientes parmetros: el handle que identifica el dispositivo de comunicaciones (este valor es devuelto por la funcin Create- File), una cadena de caracteres que contiene los caracteres enviados, el nme- ro de caracteres enviados, un puntero a una variable que almacena el nmero de caracteres escritos y un puntero a una estructura OVERLAPPED. La fun- cin WriteFile devuelve un valor de tipo BOOL; un valor FALSE significa que ha ocurrido un error. 7. Establezca un proceso que permita estar a la espera de los datos que nuestra aplicacin espera recibir. Cuando se reciban datos, utilice la funcin Read- File que tiene los siguientes parmetros: el handle que identifica el dispositi- vo de comunicaciones, una cadena de caracteres que almacenar los caracteres recibidos, el nmero de caracteres a leer, un puntero a una variable que almacena el nmero de caracteres ledos y un puntero a una estructura 120 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
OVERLAPPED. La funcin ReadFile devuelve un valor de tipo BOOL; un valor FALSE significa que ha ocurrido un error. 8. Cuando ocurre un error durante una operacin de comunicaciones, Windows bloquea el puerto correspondiente, el cual permanecer bloqueado hasta que se llame a la funcin ClearCommError. Los parmetros de esta funcin son: un handle al dispositivo de comunicaciones, un puntero a una variable que re- cibe el cdigo de error y un puntero a una variable que recibe el estado del dispositivo de comunicaciones. 9. Utilice la funcin CloseHandle para cerrar el puerto de comunicaciones cuando stas finalicen. Si ocurre un error, esta funcin devuelve un cero; in- voque a GetLastError si quiere saber de qu error se trata. Aplicacin Win32 para comunicaciones va RS232 Como ejemplo, cree una nueva aplicacin SDI denominada Comm que utilice una caja de dilogo como ventana principal. Para ello, ejecute AppWizard y haga que la clase CCommView sea una clase derivada de CFormView. A continuacin, abra el editor de recursos y site sobre la plantilla de dilogo creada por omisin los controles con las propiedades que se especifican a conti- nuacin: Objeto Propiedad Valor Etiqueta ID Caption IDC_STATIC Texto a transmitir: Caja de texto ID Multiline Vertical scroll Want return IDC_TX S S S Etiqueta ID Caption IDC_STATIC Texto recibido: Caja de texto ID Multiline Vertical scroll Want return IDC_RX S S S Botn de pulsacin ID Caption IDC_ENVIAR &Enviar
El resultado que obtendr ser similar al mostrado en la figura siguiente: CAPTULO 3: COMUNICACIONES 121
Ejecute ClassWizard y vincule las cajas de texto con las variables miembro m_tx y m_rx de la clase CCommView, y el botn con la variable m_botonEnviar miembro de la misma clase. class CCommView : public CFormView { // ... public: //{{AFX_DATA(CCommView) enum { IDD = IDD_COMM_FORM }; CButton m_botonEnviar; CString m_rx; CString m_tx; //}}AFX_DATA // ... }; A continuacin, modifique la barra de mens con los mens y las rdenes que se especifican en la tabla siguiente: Objeto Propiedad Valor Men Conexin Caption Popup Cone&xin S Orden Establecer ID Caption ID_CONEXION_ESTABLECER &Establecer Orden Cortar ID Caption ID_CONEXION_CORTAR &Cortar Separador Separator S Orden Salir ID Caption ID_APP_EXIT &Salir 122 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Men Configuracin Caption Popup &Configuracin S Orden Parmetros COM ID Caption ID_CONFIG_PARAM &Parmetros COM Men Ayuda Caption Popup &Ayuda S Orden Acerca de ID Caption ID_APP_ABOUT &Acerca de Comm...
La orden Parmetros COM visualizar una caja de dilogo, que permitir al usuario establecer las caractersticas bajo las que se realizar la comunicacin. Segn esto, vamos a disear una caja de dilogo (IDD_PARAMETROSCOM) con los controles que se indican en la tabla siguiente: Objeto Propiedad Valor Etiqueta Caption Puerto: Lista desplegable ID Items IDC_PUERTO COM1, COM2, COM3, COM4 Etiqueta Caption Baudios: Lista desplegable ID Items IDC_BAUDIOS 300, 600, 1200, 2400, ..., 256000 Etiqueta Caption Paridad: Lista desplegable ID Items IDC_PARIDAD Ninguna, Par, Impar, Marca, Espacio Etiqueta Caption Bits por carcter: Lista desplegable ID Items IDC_BITSCAR 4, 5, 6, 7, 8 Etiqueta Caption Bits de parada: Lista desplegable ID Items IDC_BITSPARADA 1, 1.5, 2 Etiqueta Caption Control de flujo: Lista desplegable ID Items IDC_CONTROLFLUJ O Ninguno, Xon/Xoff, Hardware (DTR/DSR), Hardware (RTS/CTS) Botn de pulsacin ID Caption Default button IDOK &Aceptar S Botn de pulsacin ID Caption IDCANCEL &Cancelar Botn de pulsacin ID Caption IDC_DEFAULT &Restaurar
CAPTULO 3: COMUNICACIONES 123
La caja de dilogo diseada ser similar a la siguiente:
A continuacin, desde el editor de recursos, seleccione la caja de dilogo e invoque a ClassWizard. Esto le permitir aadir a la aplicacin una clase CPa- ramCom derivada de CDialog, basada en la plantilla IDD_PARAMETROSCOM que acaba de disear. Guarde la declaracin y la definicin de esta clase en los fi- cheros paramcom.h y paramcom.cpp. Despus, aada a la clase CParamCom las variables miembro m_nPuerto, m_nBaudios, m_nParidad, m_nBitsCar, m_nBitsParada y m_nControlFlujo vinculadas a cada una de las listas desplega- bles correspondientes. class CParamCom : public CDialog { // ... // Dialog Data //{{AFX_DATA(CParamCom) enum { IDD = IDD_PARAMETROSCOM }; int m_nBitsCar; int m_nBaudios; int m_nBitsParada; int m_nControlFlujo; int m_nPuerto; int m_nParidad; //}}AFX_DATA // ... }; Registro de Windows Vamos a almacenar en el registro de Windows las caractersticas por omisin bajo las que se realizar la comunicacin. Lo que pretendemos es almacenar la ltima configuracin utilizada, como configuracin por omisin, para la siguiente vez 124 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
que se utilice la aplicacin. La funcin SetRegistryKey de la clase CWinApp permite almacenar las caractersticas de la aplicacin en el registro de Windows con la clave HKEY_CURRENT_USER\Software\... SetRegistryKey es llamada por la funcin InitInstance de la aplicacin. Tiene un parmetro que permite es- pecificar el nombre de la clave. Segn lo expuesto, abra el fichero comm.cpp y modifique en la funcin InitInstance la llamada a SetRegistryKey como se indi- ca a continuacin: SetRegistryKey(_T("App Comm")); Cuando posteriormente invoquemos a las funciones miembro GetProfileInt, WriteProfileInt, GetProfileString, y WriteProfileString de la clase CWinApp, stas operaran sobre el registro de Windows en lugar de hacerlo sobre un fichero con extensin ini. Interfaz de comunicaciones Para facilitar la implementacin de las comunicaciones va RS232, vamos a im- plementar una interfaz con las operaciones ms comunes. Segn hemos desarro- llado nuestra aplicacin, integraremos esta interfaz que resumimos en la tabla siguiente, en la clase de la vista: Funcin Descripcin Iniciar Lee del registro de Windows la configuracin inicial. Terminar Guarda en el registro de Windows la configuracin actual. EstablecerConexion Abre el puerto de comunicaciones. ConfigurarDisCom Establece los parmetros con los que se realizarn las comunicaciones. LeerCaracteresPuerto Lee un byte de la cola de entrada del puerto de co- municaciones. EscribirCarsPuerto Escribe un byte en la cola de salida del puerto de comunicaciones. CortarConexion Cierra el puerto de comunicaciones. MensajeDeError Convierte un cdigo de error en el mensaje corres- pondiente. ControlarEventos Hilo para notificar a la aplicacin el evento que ha ocurrido sobre el puerto de comunicaciones. CAPTULO 3: COMUNICACIONES 125
Aada la declaracin de estas funciones y de las variables necesarias para su implementacin a la declaracin de la clase CCommView. #define WM_EVENTO_COM WM_USER + 100 // mensaje de notificacin UINT ControlarEventos(LPVOID p); // hilo
class CCommView : public CFormView { // ...
// Operations public: ///////////////////////////////////////////////// // Interfaz para comunicaciones static int m_indPuerto; static int m_indBaudios; static int m_indParidad; static int m_indBitsCar; static int m_indBitsParada; static int m_indControlFlujo;
HANDLE m_hDisCom; // handle al dispositivo de comunicaciones OVERLAPPED m_sOverRead; // utilizada en una entrada asncrona OVERLAPPED m_sOverWrite; // utilizada en una salida asncrona UINT m_wTablaBaudios[13]; // tabla de velocidades BYTE m_TablaParidad[5]; // tabla de paridades BYTE m_TablaBitsParada[3]; // tabla bits de parada BOOL m_ConexionEstablecida; // TRUE si el puerto fue abierto BOOL m_bHiloActivo; // TRUE si el hilo est activo
static void Iniciar(); static void Terminar(); BOOL EstablecerConexion(); BOOL ConfigurarDisCom(); BOOL CortarConexion(); int LeerCaracteresPuerto(BYTE *pBytesLeidos, int BloqueMax); BOOL EscribirCarsPuerto(BYTE *pBytesAEscribir, DWORD dwBytes); void MensajeDeError(DWORD nError); ///////////////////////////////////////////////// // ... }; Las variables m_ind... contienen el ndice del elemento seleccionado de las listas de la caja de dilogo Configuracin. A continuacin escribiremos cada una de las funciones descritas. Antes, inicie las variables estticas y defina las cadenas de caracteres necesarias para el registro de Windows en la implementacin de la clase CCommView. 126 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
int CCommView::m_indPuerto = 1; // COM2 int CCommView::m_indBaudios = 6; // 9600 int CCommView::m_indParidad = 0; // ninguna int CCommView::m_indBitsCar = 4; // 8 int CCommView::m_indBitsParada = 0; // 1 int CCommView::m_indControlFlujo = 1; // Xon/Xoff
static char szComu[] = "Comunicaciones"; static char szPuerto[] = "Puerto"; static char szBaudios[] = "Baudios"; static char szParidad[] = "Paridad"; static char szBitsCar[] = "BitsCar"; static char szBitsParada[] = "BitsParada"; static char szControlFlujo[] = "ControlFlujo"; Finalmente, inicie la variable m_hDisCom a NULL y las variables m_Cone- xionEstablecida y m_bHiloActivo a FALSE, en el constructor de su clase. Funcin Iniciar La funcin Iniciar obtiene del registro de Windows la configuracin por omisin del dispositivo de comunicaciones. void CCommView::Iniciar() { CWinApp *pApp= AfxGetApp(); // Recuperar configuracin del registro de Windows m_indPuerto = pApp->GetProfileInt(szComu, szPuerto, 1); m_indBaudios = pApp->GetProfileInt(szComu, szBaudios, 6); m_indParidad = pApp->GetProfileInt(szComu, szParidad, 0); m_indBitsCar = pApp->GetProfileInt(szComu, szBitsCar, 4); m_indBitsParada = pApp->GetProfileInt(szComu, szBitsParada, 0); m_indControlFlujo = pApp->GetProfileInt(szComu, szControlFlujo, 1); } La funcin GetProfileInt de la clase CWinApp recupera del registro de Windows el entero asociado con la cadena sz... (segundo argumento) correspon- diente a la seccin szComu de la clave especificada por la funcin SetRegistry- Key. Si la entrada especificada por sz... no se encuentra, la funcin devuelve el valor especificado por el argumento tercero. Funcin Terminar La funcin Terminar ser invocada cuando se corta la comunicacin para guardar en el registro de Windows la configuracin actual del dispositivo de comunica- ciones. CAPTULO 3: COMUNICACIONES 127
void CCommView::Terminar() { CWinApp *pApp= AfxGetApp(); // Guardar configuracin en el registro de Windows pApp->WriteProfileInt(szComu, szPuerto, m_indPuerto); pApp->WriteProfileInt(szComu, szBaudios, m_indBaudios); pApp->WriteProfileInt(szComu, szParidad, m_indParidad); pApp->WriteProfileInt(szComu, szBitsCar, m_indBitsCar); pApp->WriteProfileInt(szComu, szBitsParada, m_indBitsParada); pApp->WriteProfileInt(szComu, szControlFlujo, m_indControlFlujo); } La funcin WriteProfileInt de la clase CWinApp guarda en el registro de Windows el entero especificado por el argumento tercero, asociado con la cadena sz... (segundo argumento), en la seccin szComu de la clave especificada por la funcin SetRegistryKey. Funcin EstablecerConexion La funcin EstablecerConexion permite abrir el puerto de comunicaciones especi- ficado por la variable miembro m_indPuerto. Para ello: 1. Invoca a la funcin CreateFile para abrir el puerto de comunicaciones. Hay dos formas de abrir un puerto de comunicaciones: solapada (overlapped) y no solapada (nonoverlapped). La documentacin del SDK de Win32 utiliza los trminos asncrono y sncrono. Un puerto abierto para operaciones solapadas permite mltiples hilos realizando operaciones de E/S (ReadFile o WriteFi- le) simultneas, as como ejecutar otra tarea mientras las operaciones estn pendientes. Adems, el comportamiento de las operaciones solapadas permite a un nico hilo realizar peticiones diferentes y ejecutar tareas en segundo pla- no mientras las operaciones estn pendientes. Si el puerto se abre para opera- ciones no solapadas, el hilo queda bloqueado mientras la operacin de E/S solicitada no est completada; una vez completada, el hilo puede seguir traba- jando. En este caso, si un hilo est bloqueado esperando a que su operacin de E/S finalice, cualquier otro hilo que requiera una operacin de E/S quedar bloqueado. Esta ltima forma de abrir un puerto es til cuando el sistema ope- rativo no soporta operaciones de E/S solapadas. El cdigo siguiente indica la forma apropiada de abrir un puerto de comunicaciones de forma solapada: m_hDisCom = CreateFile(szPuerto, GENERIC_READ | GENERIC_WRITE, 0, // acceso exclusivo NULL, // sin atributos de seguridad OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 128 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Cuando se especifica el parmetro FILE_FLAG_OVERLAPPED, las funcio- nes ReadFile y WriteFile deben especificar una estructura OVERLAPPED. 2. Invoca a la funcin SetCommMask para especificar los eventos que sern atendidos. Por ejemplo: SetCommMask( m_hDisCom, EV_RXCHAR | EV_TXEMPTY | EV_RX80FULL | EV_ERR); El parmetro m_hDisCom es un handle al dispositivo de comunicaciones de- vuelto por la funcin CreateFile. El otro parmetro especifica los eventos que son habilitados. Un valor cero inhabilita todos los eventos. Por ejemplo, EV_RXCHAR se produce cuando se recibe un carcter en la cola de entrada; EV_TXEMPTY se produce cuando se enva el ltimo carcter de la cola de sa- lida; EV_RX80FULL se produce cuando la cola de entrada est llena al 80%; EV_ERR sucede cuando se produce alguno de los siguientes errores: CE_FRAME, CE_OVERRUN, o CE_RXPARITY. 3. Invoca a la funcin SetupComm para especificar el tamao en bytes de las colas de recepcin y de transmisin. SetupComm(m_hDisCom, COLARX, COLATX); 4. Invoca a la funcin PurgeComm para terminar las operaciones de lectura y escritura pendientes y limpiar las colas de recepcin y de transmisin. PurgeComm( m_hDisCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); 5. Construye una estructura DCB e invoca a la funcin SetCommState para configurar el dispositivo de comunicaciones con los valores almacenados en dicha estructura. 6. Invoca a la funcin SetCommTimeouts para establecer los tiempos lmites para las operaciones de recepcin y transmisin. 7. Si la comunicacin va a ser controlada por eventos, lanza un hilo dedicado a controlar cada evento de inters que se produzca en el puerto de comunica- ciones. 8. Invoca a la funcin EscapeCommFunction para activar la seal DTR mien- tras dure la conexin. BOOL CCommView::EstablecerConexion() { CAPTULO 3: COMUNICACIONES 129
char szPuerto[10]; BOOL bExito = FALSE;
// Formar la cadena "COM" ms el nmero de dispositivo wsprintf(szPuerto, "COM%d", m_indPuerto + 1);
// Cerrar el puerto si estuviera abierto if (m_hDisCom) CloseHandle(m_hDisCom);
// Abrir el puerto de comunicaciones m_hDisCom = CreateFile(szPuerto, GENERIC_READ | GENERIC_WRITE, 0, // acceso exclusivo NULL, // sin atributos de seguridad OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
// Especificar los eventos que sern atendidos SetCommMask( m_hDisCom, EV_RXCHAR | EV_TXEMPTY | EV_RX80FULL | EV_ERR);
// Establecer el tamao de las colas de recepcin y de transmisin SetupComm(m_hDisCom, COLARX, COLATX);
// Terminar las operaciones de lectura y escritura pendientes // y limpiar las colas Rx y Tx PurgeComm( m_hDisCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
// Establecer los parmetros de la comunicacin bExito = ConfigurarDisCom();
if ( bExito ) { m_ConexionEstablecida = TRUE; // Crear un hilo secundario para ver qu evento ocurre if ( AfxBeginThread(ControlarEventos, this) == NULL ) { AfxMessageBox( "Error: No se puede iniciar el hilo", MB_OK | MB_ICONEXCLAMATION ); m_ConexionEstablecida = FALSE; CloseHandle(m_hDisCom); return FALSE; 130 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
} m_bHiloActivo = TRUE; // Enviar la seal DTR (data-terminal-ready). EscapeCommFunction(m_hDisCom, SETDTR); } else { AfxMessageBox( "Error: No se puede configurar el dispositivo", MB_OK | MB_ICONEXCLAMATION ); m_ConexionEstablecida = FALSE; CloseHandle(m_hDisCom); } return bExito; } Defina las constantes COLARX y COLATX, que definen los tamaos de las colas de recepcin y transmisin respectivamente, en el fichero commview.cpp: #define COLARX 4096 #define COLATX 4096 Funcin MensajeDeError La funcin MensajeDeError convierte un cdigo de error en el correspondiente mensaje obtenido del sistema, y visualiza un dilogo con dicho mensaje. La con- versin la hace invocando a la funcin FormatMessage de la API, que obtiene el mensaje de la tabla de mensajes del sistema y lo almacena en un buffer en memo- ria que crea automticamente invocando a LocalAlloc. Una vez visualizado el mensaje, la funcin MensajeDeError invoca a LocalFree para liberar el buffer asignado por LocalAlloc desde FormatMessage (para obtener ms informacin sobre esta funcin, consulte la ayuda en lnea). void CCommView::MensajeDeError( DWORD nError ) { LPVOID lpMsg;
// Liberar el buffer ::LocalFree( lpMsg ); } Funcin ConfigurarDisCom Para construir la estructura DCB y configurar el dispositivo de comunicaciones as como para establecer los tiempos lmites para las operaciones de recepcin y de transmisin, EstablecerConexion invoca a la funcin ConfigurarDisCom. BOOL CCommView::ConfigurarDisCom() { BYTE bEstablecer; DCB dcb;
// Establecer los tiempos lmites para las operaciones de E/S COMMTIMEOUTS CommTimeOuts;
CommTimeOuts.ReadIntervalTimeout = MAXDWORD; CommTimeOuts.ReadTotalTimeoutMultiplier = 0; CommTimeOuts.ReadTotalTimeoutConstant = 1000; // CBR_9600 es aproximadamente 1 byte/ms. Para nuestros // propsitos permitiremos un tiempo de espera por carcter // doble al necesario CommTimeOuts.WriteTotalTimeoutMultiplier = 2*CBR_9600/dcb.BaudRate; CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts( m_hDisCom, &CommTimeOuts);
return TRUE; } La iniciacin de un puerto se hace llamando a la funcin de la API de Win- dows SetCommState. El primer argumento hace referencia al puerto de comuni- caciones y el segundo a una estructura de tipo DCB que almacena la configuracin del puerto. DCB est definido en windows.h y recoge todos los parmetros relacionados con la configuracin de un puerto. Si observa la definicin de la funcin ConfigurarDisCom, ver que es necesa- rio asignar valores a los arrays m_wTablaBaudios, m_TablaParidad y m_Tabla- BitsParada miembros de la clase CCommView. Realice esta asignacin en el constructor de su clase, como se indica a continuacin: CCommView::CCommView() : CFormView(CCommView::IDD) { m_wTablaBaudios[0] = CBR_110; m_wTablaBaudios[1] = CBR_300; m_wTablaBaudios[2] = CBR_600; m_wTablaBaudios[3] = CBR_1200; m_wTablaBaudios[4] = CBR_2400; m_wTablaBaudios[5] = CBR_4800; m_wTablaBaudios[6] = CBR_9600; m_wTablaBaudios[7] = CBR_14400; m_wTablaBaudios[8] = CBR_19200; CAPTULO 3: COMUNICACIONES 133
//{{AFX_DATA_INIT(CCommView) m_rx = _T(""); m_tx = _T(""); //}}AFX_DATA_INIT // TODO: add construction code here m_hDisCom = NULL; m_pHiloEv = NULL; } Finalmente hemos establecido los tiempos lmite invocando a la funcin Set- CommTimeouts. El primer argumento hace referencia al puerto de comunicacio- nes y el segundo a una estructura COMMTIMEOUTS que almacena los tiempos lmites. El significado de cada uno de estos miembros se expone a continuacin. ReadIntervalTimeout especifica el tiempo mximo, en milisegundos, que puede transcurrir entre la llegada de dos caracteres en la lnea de comunicaciones. Durante una operacin ReadFile, el lapso de tiempo empieza cuando se recibe el primer carcter. Si el intervalo de tiempo entre la llegada de dos caracteres cua- lesquiera excede esta cantidad, la operacin ReadFile se da por finalizada devol- viendo cualquier carcter que haya en la cola de entrada. Un valor cero indica que no se usan tiempos lmites. Un valor MAXDWORD, combinado con valores cero para los miembros Re- adTotalTimeoutConstant y ReadTotalTimeoutMultiplier, especifica que la opera- cin de lectura tiene que retornar inmediatamente con los caracteres que ya se han recibido, aun cuando no se haya recibido ningn carcter. ReadTotalTimeoutMultiplier especifica el multiplicador, en milisegundos, uti- lizado para calcular el tiempo lmite total para las operaciones de lectura. Para ca- da operacin de lectura, este valor es multiplicado por el nmero de bytes que se quieren leer. 134 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
ReadTotalTimeoutConstant especifica la constante, en milisegundos, utilizada para calcular el tiempo lmite total para las operaciones de lectura. Para cada ope- racin de lectura, este valor se agrega al producto de ReadTotalTimeoutMultiplier por el nmero de bytes solicitados. Un valor cero para ReadTotalTimeoutMulti- plier y ReadTotalTimeoutConstant indican que el tiempo lmite total no se utili- zar para operaciones de lectura. WriteTotalTimeoutMultiplier especifica el multiplicador, en milisegundos, utilizado para calcular el tiempo lmite total para las operaciones de escritura. Para cada operacin de escritura, este valor es multiplicado por el nmero de bytes que se quieren escribir. WriteTotalTimeoutConstant especifica la constante, en milisegundos, utiliza- da para calcular el tiempo lmite total para operaciones de escritura. Para cada operacin de escritura, este valor se agrega al producto de WriteTotalTimeoutMul- tiplier por el nmero de bytes escritos. Un valor cero para WriteTotalTimeoutMul- tiplier y WriteTotalTimeoutConstant indica que el tiempo lmite total no se utilizar para operaciones de escritura. Si una aplicacin pone ReadIntervalTimeout y ReadTotalTimeoutMultiplier a MAXDWORD y ReadTotalTimeoutConstant a un valor mayor que cero y menor que MAXDWORD, cuando la funcin ReadFile sea invocada, puede ocurrir que: Si hay caracteres en la cola de entrada, ReadFile retorna inmediatamente con los caracteres de la cola. Si no hay caracteres en la cola de entrada, ReadFile espera hasta que llegue un carcter y entonces retorna inmediatamente. Si ningn carcter llega dentro del tiempo especificado por ReadTotalTime- outConstant, ReadFile retornar pasado el tiempo lmite. Controlar eventos Vimos que la funcin EstablecerConexion, despus que abre el puerto de comuni- caciones, lanza un hilo ControlarEventos. El hilo controlar los eventos que ocu- rren sobre el puerto invocando a la funcin WaitCommEvent. Esta funcin informa del evento ocurrido sobre el dispositivo de comunicaciones a travs de su segundo argumento. Los eventos a controlar fueron establecidos por la funcin SetCommMask. Tambin puede utilizar GetCommMask para ver los eventos que fueron establecidos. DWORD dwMascEvt; OVERLAPPED sOver = {0, 0, 0, 0, 0}; CAPTULO 3: COMUNICACIONES 135
sOver.hEvent = CreateEvent( NULL, // sin seguridad TRUE, // iniciacin manual FALSE, // inicialmente ocupado NULL ); // sin nombre // ... WaitCommEvent( m_hDisCom, &dwMascEvt, &sOver ); // ... Si un proceso intenta cambiar la mscara de eventos del dispositivo utilizando SetCommMask mientras una operacin WaitCommEvent est en curso, Wait- CommEvent retorna inmediatamente y la variable dwMascEvt es puesta a 0. Si m_hDisCom no se abriera con FILE_FLAG_OVERLAPPED, WaitCom- mEvent no retorna hasta que ocurra uno de los eventos especificados, o un error. Si m_hDisCom se abre con FILE_FLAG_OVERLAPPED, el parmetro terce- ro de WaitCommEvent no debe ser NULL. Debe apuntar a una estructura OVERLAPPED vlida que contenga un handle a un evento con iniciacin ma- nual. Si fuera NULL, la funcin puede informar incorrectamente de que la opera- cin est finalizada. En este ltimo caso, si una operacin de E/S solapada no puede completarse inmediatamente, la funcin WaitCommEvent devuelve FALSE y la funcin Ge- tLastError devuelve ERROR_IO_PENDING, indicando que la operacin se est ejecutando en segundo plano. Cuando esto ocurre, el sistema pone el objeto refe- renciado por el miembro hEvent de la estructura OVERLAPPED en estado ocu- pado antes de que WaitCommEvent retorne. Cuando posteriormente ocurra un evento de los especificados o un error, el sistema liberar dicho objeto. El proceso que llama puede utilizar una de las funciones de espera (WaitForSingleObject, WaitForMultipleObjects, etc.) para determinar el estado del objeto evento y despus invocar a GetOverlappedResult para determinar los resultados de la operacin WaitCommEvent. GetOverlappedResult informa del xito o fracaso de la operacin, y la variable dwMascEvt indica el evento que ocurri. UINT ControlarEventos(LPVOID p) { DWORD dwMascEvt; CCommView *const pView = (CCommView *)p; OVERLAPPED sOver = {0, 0, 0, 0, 0};
// Crear un evento de E/S utilizado para lecturas solapadas sOver.hEvent = CreateEvent( NULL, // sin seguridad TRUE, // iniciacin manual FALSE, // inicialmente ocupado NULL ); // sin nombre 136 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
if (sOver.hEvent == NULL) { AfxMessageBox( "Fallo al crear el evento para el hilo", MB_OK | MB_ICONEXCLAMATION ); return 0; }
// Restablecer los eventos por si hubieran cambiado if (!SetCommMask( pView->m_hDisCom, EV_RXCHAR | EV_TXEMPTY | EV_RX80FULL | EV_ERR )) return 0;
while ( pView->m_ConexionEstablecida ) { dwMascEvt = 0; WaitCommEvent( pView->m_hDisCom, &dwMascEvt, &sOver ); if ((dwMascEvt & EV_RXCHAR) == EV_RXCHAR) ::PostMessage(pView->m_hWnd, WM_EVENTO_COM, EV_RXCHAR, 0); else if ((dwMascEvt & EV_TXEMPTY) == EV_TXEMPTY) ::PostMessage(pView->m_hWnd, WM_EVENTO_COM, EV_TXEMPTY, 0); else if ((dwMascEvt & EV_RX80FULL) == EV_RX80FULL) ::PostMessage(pView->m_hWnd, WM_EVENTO_COM, EV_RX80FULL, 0); else if ((dwMascEvt & EV_ERR) == EV_ERR) ::PostMessage(pView->m_hWnd, WM_EVENTO_COM, EV_ERR, 0); } pView->m_bHiloActivo = FALSE; // Liberar el manipulador del evento CloseHandle( sOver.hEvent ); return 1; } Observe que cuando ocurre un evento sobre el dispositivo de comunicaciones, el hilo secundario se lo notifica al hilo principal (a la aplicacin) envindole un mensaje WM_EVENTO_COM. Este mensaje fue definido anteriormente en el fi- chero CommView.h. La informacin que acompaa a dicho mensaje permite co- nocer a la aplicacin cul fue el evento ocurrido. Por lo tanto, lo siguiente es aadir dicho mensaje al mapa de mensajes de la vista. Para ello, abra el fichero CommView.cpp, localice el mapa de mensajes y aada la lnea que se muestra a continuacin: BEGIN_MESSAGE_MAP(CCommView, CFormView) //{{AFX_MSG_MAP(CCommView) // ... //}}AFX_MSG_MAP ON_MESSAGE(WM_EVENTO_COM, OnEventoCom) END_MESSAGE_MAP() En el captulo de hilos vimos las posibles formas de comunicacin entre hilos. No es correcto invocar desde un hilo a una funcin miembro de una clase de la CAPTULO 3: COMUNICACIONES 137
aplicacin. Por ejemplo, si procede de la forma siguiente, tendr problemas, ya que un hilo, en nuestro caso ControlarEventos, adems de no ser un miembro de una clase de la aplicacin, se ejecuta con concurrentemente con ella (con el hilo principal): if ((dwMascEvt & EV_RXCHAR) == EV_RXCHAR) nBytes = pView->LeerCaracteresPuerto( BytesLeidos, BLOQUEMAX ); La respuesta al mensaje WM_EVENTO_COM es la funcin OnEventoCom, que realizar un proceso u otro en funcin del evento ocurrido. Por ejemplo, si el evento es que se recibi informacin en la cola de recepcin del dispositivo de comunicaciones, esta funcin leer dicha informacin y la procesar; en nuestro caso la visualizar en la caja de texto correspondiente. Por lo tanto, aada esta funcin a la clase CCommView y edtela como se indica a continuacin: long CCommView::OnEventoCom(UINT wParam, long lParam) { BYTE BytesLeidos[BLOQUEMAX + 1]; int nBytes;
// Mensajes recibidos desde el hilo switch (wParam) { case EV_RXCHAR: if (nBytes = LeerCaracteresPuerto( BytesLeidos, BLOQUEMAX )) OnVisualizarCars( BytesLeidos, nBytes ); break; case EV_TXEMPTY: // ... break; case EV_RX80FULL: // ... break; case EV_ERR: // ... break; } return 0; } Vemos que si el evento que se produjo fue EV_RXCHAR, la funcin OnEven- toCom invoca primero a la funcin LeerCaracteresPuerto para leer los caracteres recibidos y despus a OnVisualizarCars para aadir dicha informacin a la caja de texto referenciada por m_rx de la interfaz de usuario. 138 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Funcin LeerCaracteresPuerto La funcin LeerCaracteresPuerto lee un bloque del puerto COM y lo almacena en un array. El primer parmetro es el array donde se almacenarn los bytes ledos y el segundo es el nmero mximo de bytes que se van a leer. int LeerCaracteresPuerto(BYTE *pBytesLeidos, int BloqueMax ); Defina la constante BLOQUEMAX en el fichero CommView.cpp, as: #define BLOQUEMAX 80 La funcin LeerCaracteresPuerto leer como mximo BLOQUEMAX carac- teres. Para ello invocar a la funcin ReadFile. bLeer = ReadFile( m_hDisCom, pBytesLeidos, dwNumBytes, &dwNumBytes, &m_sOverRead ); pBytesLeidos[dwNumBytes] = 0; // finalizar con el carcter nulo La funcin ReadFile lee dwNumBytes caracteres del dispositivo de comuni- caciones referenciado por m_hDisCom y los almacena en el array pBytesLeidos. Si m_hDisCom se abri con FILE_FLAG_OVERLAPPED, el ltimo parme- tro de ReadFile no debe ser NULL; debe apuntar a una estructura OVERLAP- PED vlida que contenga un handle a un evento con iniciacin manual. En este caso, ReadFile puede retornar antes de que la operacin de lectura se haya com- pletado, en cuyo caso devolver FALSE y la funcin GetLastError ERROR_IO_PENDING. Esto permite continuar la ejecucin del proceso que hizo la llamada mientras la operacin de lectura finaliza. Cuando la operacin finaliza, el evento especificado en la estructura OVERLAPPED, que est ocupado, se po- ne en el estado libre. Para obtener informacin del xito o fracaso de la operacin, podemos invocar a la funcin GetOverlappedResult; si esta funcin devuelve cero significa que la operacin no se ha completado o que ha fracaso. Para obtener informacin de lo ocurrido, podemos invocar a la funcin GetLastError. Si el ltimo parmetro de ReadFile fuera NULL, la funcin puede informar incorrectamente de que la operacin est finalizada. La informacin obtenida a travs de la funcin GetOverlappedResult co- rresponde a la ltima operacin solapada sobre el dispositivo especificado, para la cual fue proporcionada la estructura OVERLAPPED especificada, y para la que los resultados de la operacin estaban pendientes. if (!bLeer) { CAPTULO 3: COMUNICACIONES 139
if (GetLastError() == ERROR_IO_PENDING) { while(!GetOverlappedResult( m_hDisCom, &m_sOverRead, &dwNumBytes, FALSE )) { dwError = GetLastError(); if(dwError == ERROR_IO_INCOMPLETE) // ... } Si la funcin que inicia la operacin devuelve FALSE y GetLastError de- vuelve ERROR_IO_PENDING, es porque esa operacin queda pendiente. Cuan- do una operacin de E/S est pendiente, la funcin que inici la operacin pone el evento referenciado por el miembro hEvent de la estructura OVERLAPPED en el estado ocupado. Y cuando la operacin pendiente finaliza, el sistema pone el ob- jeto evento en el estado libre. Si el ltimo parmetro de GetOverlappedResult es TRUE, la funcin no re- torna hasta que la operacin pendiente finalice. Tambin, si posteriormente ocurre un evento de los especificados o un error, el sistema liberar el objeto evento. Si es FALSE y la operacin est todava pendiente, la funcin devuelve FALSE y GetLastError devuelve ERROR_IO_INCOMPLETE. int CCommView::LeerCaracteresPuerto(BYTE *pBytesLeidos, int Bloque- Max) { BOOL bLeer; COMSTAT EstadoCom; DWORD dwCodsError; DWORD dwNumBytes; DWORD dwError; char szError[10];
// Leer cada vez como mximo BloqueMax caracteres ClearCommError( m_hDisCom, &dwCodsError, &EstadoCom ); dwNumBytes = min( (DWORD)BloqueMax, EstadoCom.cbInQue );
if (dwNumBytes > 0) { bLeer = ReadFile( m_hDisCom, pBytesLeidos, dwNumBytes, &dwNumBytes, &m_sOverRead ); pBytesLeidos[dwNumBytes] = 0; // finalizar con el carcter nulo
if (!bLeer) { if (GetLastError() == ERROR_IO_PENDING) { // Tenemos que esperar a que la lectura se complete. Esta 140 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
// funcin ser interrumpida cuando transcurra un tiempo // CommTimeOuts.ReadTotalTimeoutConstant.
// Chequear errores en el puerto while(!GetOverlappedResult( m_hDisCom, &m_sOverRead, &dwNumBytes, FALSE )) { dwError = GetLastError(); // Si no termin, dwError vale ERROR_IO_INCOMPLETE if(dwError == ERROR_IO_INCOMPLETE) continue; else { // Ocurri un error, intentar recuperarlo MensajeDeError( dwError ); ClearCommError( m_hDisCom, &dwCodsError, &EstadoCom ); if ( dwCodsError > 0 ) { wsprintf( szError, "Com: <CE-%u>", dwCodsError ); // Los errores IE son < 0 (winbase.h) AfxMessageBox( szError, MB_OK | MB_ICONEXCLAMATION ); } break; } } // fin while } else // error distinto de ERROR_IO_PENDING { dwNumBytes = 0; ClearCommError( m_hDisCom, &dwCodsError, &EstadoCom ); if ( dwCodsError > 0 ) { wsprintf( szError, "Com: <CE-%u>", dwCodsError ); AfxMessageBox( szError, MB_OK | MB_ICONEXCLAMATION ); } } } } return ( dwNumBytes ); } La funcin ClearCommError obtiene informacin sobre el error de comuni- caciones ocurrido y sobre el estado del dispositivo de comunicaciones. Asimismo, desactiva cualquier indicador de error que haya sido activado para habilitar opera- ciones E/S adicionales. CAPTULO 3: COMUNICACIONES 141
Funcin EscribirCarsPuerto La funcin EscribirCarsPuerto escribe un bloque en el puerto COM procedente de un array pasado como parmetro. El primer parmetro es el array donde se al- macenarn los bytes ledos y el segundo es el nmero de bytes que se quieren es- cribir. Las explicaciones con respecto a algunas de las funciones utilizadas ya han sido descritas en apartados anteriores. BOOL CCommView::EscribirCarsPuerto(BYTE *pBytesAEscribir, DWORD dwBytes) { COMSTAT EstadoCom; BOOL bEscribir; DWORD dwNumBytes; DWORD dwCodsError; DWORD dwError; DWORD dwBytesEnviados=0; char szError[128];
// Normalmente el cdigo siguiente no se ejecutar porque // el driver cachea las operaciones de escritura. Por lo // tanto, pequeas peticiones de E/S (hasta algunos cientos // de bytes) sern normalmente aceptadas inmediatamente // y WriteFile devolver TRUE aunque el puerto de // comunicaciones permita operaciones solapadas.
if (!bEscribir) { if( GetLastError() == ERROR_IO_PENDING ) { // Debemos esperar a que la operacin de escritura termine // para conocer el xito o no de la misma.
// Podra ser beneficioso colocar la operacin de escritura // en un hilo separado para que un bloqueo durante la // realizacin de dicha operacin no afecte negativamente // la sensibilidad de la interfaz de usuario.
// Si la operacin de escritura tarda demasiado en finalizar, // esta funcin ser interrumpida cuando transcurra un tiempo // CommTimeOuts.WriteTotalTimeoutMultiplier. Este cdigo toma // nota de la interrupcin pero no reintenta la escritura.
// Terminar las operaciones de lectura y escritura pendientes // y limpiar las colas Rx y Tx PurgeComm( m_hDisCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
CloseHandle( m_hDisCom ); m_hDisCom = NULL;
return TRUE; } INTERFAZ DEL USUARIO Cuando se ejecute la aplicacin, una de las primeras tareas que hay que realizar es leer la configuracin almacenada en el registro de Windows. Este proceso lo rea- lizaremos desde la funcin OnInitialUpdate de la clase CCommView. void CCommView::OnInitialUpdate() { CFormView::OnInitialUpdate();
// Ajustar el tamao de la ventana marco a la vista ResizeParentToFit( FALSE );
// Iniciar el puerto y los controles de la IU UpdateData(FALSE); m_botonEnviar.EnableWindow(FALSE); Iniciar(); // configuracin inicial del puerto COM } Asimismo, la funcin OnInitialUpdate, adems de ajustar el marco de la ventana al tamao de la vista, inhabilita el botn Enviar. Cuando se establece una conexin entre dos equipos, previamente hay que especificar los siguientes parmetros: puerto (COM1, COM2, COM3, COM4), 144 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
baudios (110, 300, 600, 1200, 2400, 4800, 9600, ...), paridad (par, impar, ningu- na), bits de datos (normalmente 7 u 8 bits por carcter), bits de parada (1 o 2) y control de flujo (Xon/Xoff, hardware, ninguna). Para establecer los parmetros anteriormente especificados la aplicacin pro- porciona la orden Parmetros COM. Cuando el usuario ejecute esta orden se vi- sualizar la caja de dilogo Configuracin que le permitir establecer los parmetros con los que se realizarn las comunicaciones. La respuesta a esta ac- cin del usuario ser la funcin OnConfigParam. Ejecute ClassWizard, aada esta funcin y edtela como se indica a continuacin: void CCommView::OnConfigParam() { // Crea el objeto de dilogo CParamCom dlg;
// Actualizar los datos del dilogo dlg.m_nPuerto = m_indPuerto; dlg.m_nBaudios = m_indBaudios; dlg.m_nParidad = m_indParidad; dlg.m_nBitsCar = m_indBitsCar; dlg.m_nBitsParada = m_indBitsParada; dlg.m_nControlFlujo = m_indControlFlujo;
// Mostrar el cuadro de dilogo y verificar el botn pulsado if (dlg.DoModal() != IDOK) return;
if( EstablecerConexion() ) { m_ConexionEstablecida = TRUE; AfxMessageBox( "Puerto de comunicaciones abierto", MB_OK | MB_ICONEXCLAMATION ); m_botonEnviar.EnableWindow(TRUE); } } Una vez visualizada la caja de dilogo Configuracin, si el usuario hace clic en el botn Aceptar (IDOK) se actualizarn los parmetros de configuracin con los valores seleccionados y se invocar a la funcin EstablecerConexion para abrir el puerto de comunicaciones especificado. Si este proceso se ejecuta satisfac- CAPTULO 3: COMUNICACIONES 145
toriamente, ponemos la variable m_ConexionEstablecida a valor TRUE, variable que utilizaremos posteriormente para identificar si el puerto est o no abierto. Esta variable fue declarada anteriormente como miembro de la clase CCommView e iniciada en el constructor con el valor FALSE. Como la funcin anterior hace referencia a la clase CParamCom, es necesario aadir la lnea siguiente al fichero CommView.cpp. #include "ParamCom.h" Para no permitir modificar la configuracin del puerto de comunicaciones cuando est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CCommView, controladora del mensaje UPDATE_COMMAND_UI. void CCommView::OnUpdateConfigParam(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_ConexionEstablecida); } Si el usuario hace clic en el botn Cancelar (IDCANCEL) de la caja de dilo- go Configuracin, no se realizar ninguna accin. En cambio, si el usuario hace clic en el botn Restaurar (IDC_DEFAULT) se establecern como valores por omisin los ltimos valores que fueron almacena- dos en el registro de Windows. void CParamCom::OnPorOmision() { // Parmetros por defecto de la comunicacin m_nPuerto = 1; // COM2 m_nBaudios = 6; // 9600 m_nParidad = 0; // Ninguna m_nBitsCar = 4; // 8 m_nBitsParada = 0; // 1 m_nControlFlujo = 1; // Xon/Xoff
UpdateData(FALSE); // Refrescar las listas desplegables } Ejecute ClassWizard y vincule la funcin OnPorOmision miembro de la clase CParamCom al botn Restaurar. La orden Establecer del men Conexin tiene como funcin abrir el puerto de comunicaciones con los parmetros actualmente seleccionados. Cuando el usuario ejecute esta orden, como respuesta ser invocada la funcin OnConexionEstable- cer miembro de la clase CCommView, que a su vez invocar a la funcin Estable- 146 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
cerConexion. Por lo tanto, ejecute ClassWizard, vincule OnConexionEstablecer con la orden Establecer y edtela como se indica a continuacin: void CCommView::OnConexionEstablecer() { if ( EstablecerConexion() ) { UpdateData(TRUE); m_botonEnviar.EnableWindow(TRUE); UpdateData(FALSE); } } Si el dispositivo de comunicaciones se abre satisfactoriamente, la funcin OnConexionEstablecer, adems, actualiza las variables miembro m_tx y m_rx li- gadas con las cajas de texto de transmisin y de recepcin, respectivamente, y habilita el botn Enviar. Para no permitir abrir el puerto de comunicaciones cuando ya est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CCommView, con- troladora del mensaje UPDATE_COMMAND_UI. void CCommView::OnUpdateConexionEstablecer(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_ConexionEstablecida); } Para cerrar el puerto de comunicaciones, la interfaz de la aplicacin propor- ciona la orden Cortar del men Conexin. Para hacer operativa esta orden, vinc- lela con la funcin OnConexionCortar y edtela as: void CCommView::OnConexionCortar() { Terminar(); // guardar la configuracin CortarConexion(); m_botonEnviar.EnableWindow(FALSE); } Observe que la funcin OnConexionCortar primero llama a la funcin Termi- nar para guardar la configuracin actual en el registro de Windows, despus invo- ca a la funcin CortarConexion y finalmente inhabilita el botn Enviar. Para no permitir cerrar el puerto de comunicaciones cuando no est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CCommView, con- troladora del mensaje UPDATE_COMMAND_UI. void CCommView::OnUpdateConexionCortar(CCmdUI* pCmdUI) CAPTULO 3: COMUNICACIONES 147
{ pCmdUI->Enable(m_ConexionEstablecida); } Para prever el caso de que estando un dispositivo de comunicaciones abierto, el usuario cierre la aplicacin sin haber ejecutado previamente la orden Cortar, defina el destructor de la clase CCommView as: CCommView::~CCommView() { if ( m_ConexionEstablecida ) CortarConexion(); } ENVIAR Y RECIBIR DATOS Para enviar datos, el usuario arrancar la aplicacin, establecer las comunicacio- nes, escribir el texto a enviar en la caja de Texto a transmitir y pulsar el botn Enviar. Por lo tanto, aada la funcin OnEnviar controladora del mensaje WM_COMMAND que Windows enva al hacer clic en el botn Enviar y edtela como se muestra a continuacin: void CCommView::OnEnviar() { int vr, n; BYTE *pszBytes;
UpdateData(TRUE); // Enviar los datos que hay en la caja transmisin if ( n = m_tx.GetLength() ) { pszBytes = (BYTE *)m_tx.GetBuffer(n + 1); vr = EscribirCarsPuerto(pszBytes, n ); m_tx.ReleaseBuffer(); // Eliminar los caracteres transmitidos if ( vr ) { m_tx = ""; UpdateData( FALSE ); } } } La funcin OnEnviar enva la informacin a la cola de salida invocando a la funcin EscribirCarsPuerto y despus limpia la caja Texto a transmitir. 148 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Cuando los caracteres enviados desde otra mquina se reciben en la cola de recepcin, el dispositivo de comunicaciones lo notifica por medio del evento EV_RXCHAR. Este evento es capturado por el hilo ControlarEventos por medio de la funcin WaitCommEvent. Entonces, el hilo enva un mensaje WM_EVEN- TO_COM al hilo principal (a la aplicacin) notificndole el evento ocurrido en el dispositivo de comunicaciones. Como respuesta a este mensaje se ejecuta la fun- cin OnEventoCom que en este caso invoca a la funcin LeerCaracteresPuerto para obtener los datos del puerto, y despus a la funcin OnVisualizarCars para visualizarlos en la caja Texto recibido. Por lo tanto, ejecute ClassWizard, aada la funcin OnVisualizarCars como miembro de CCommView y edtela como se in- dica a continuacin: void CCommView::OnVisualizarCars(BYTE *pszBytes, int nBytes) { m_rx += pszBytes; // aadir los caracteres recibidos a los ya existentes UpdateData( FALSE ); // visualizarlos GetDlgItem(IDC_TX)->SetFocus(); // enfocar la caja de transmisin } La aplicacin est finalizada. Ahora puede compilarla y ejecutarla. Para reali- zar las pruebas en un solo ordenador, puede unir los hilos numerados dos y tres de su puerto serie. De esta forma lo que trasmita, lo recibir de nuevo. Otra solucin, es conectar un mdem. Si enva una orden ATZ ms CR, el mdem le devolver OK. Esta aplicacin no soporta la transmisin de ficheros binarios, slo soporta la transmisin de ficheros de texto ASCII. Se deja como ejercicio para el lector mo- dificar la aplicacin para que soporte ficheros texto y binarios (observe que el problema deriva de haber utilizado para manipular la informacin, cadenas de ca- racteres finalizadas con el carcter ASCII nulo). CONTROL DE COMUNICACIONES Visual C++incluye un control personalizado, Microsoft Communications Con- trol, que permite establecer una comunicacin serie entre mquinas, basada en el estndar RS232, de una forma rpida y sencilla. Para poder utilizar este control en una aplicacin, hay que aadir al proyecto el control ActiveX MSCOMM32.OCX para aplicaciones de 32 bits. Este control tiene los eventos y propiedades siguien- tes: Eventos OnComm Propiedades Break CDHolding CommEvent CommID CAPTULO 3: COMUNICACIONES 149
CommPort CTSHolding DSRHolding DTREnable EOFEnable Handshaking InBufferCount InBufferSize Index Input InputLen InputMode Name NullDiscard Object OutBufferCount OutBufferSize Output Parent ParityReplace PortOpen RThreshold RTSEnable Settings SThreshold Tag Para obtener una amplia informacin sobre cada una de estas propiedades, re- curra a la ayuda en lnea de Visual Basic. Como ejemplo, vamos a realizar la misma aplicacin anterior, pero utilizando ahora un control de comunicaciones. Por lo tanto, cree una nueva aplicacin SDI denominada ControlCom que utilice una caja de dilogo como ventana principal. Para ello, ejecute AppWizard y haga que la clase CControlComView sea una clase derivada de CFormView. A diferencia de la aplicacin anterior, permita que Con- trolCom tenga una barra de estado que utilizaremos para visualizar mensajes. A continuacin, abra el editor de recursos y site sobre la plantilla de dilogo creada por omisin, los controles con las propiedades que se especificaron en la aplicacin anterior. Aada ahora el control ActiveX, Microsoft Communications Control. El resultado que obtendr ser similar al mostrado en la figura siguiente:
Seleccione el control de comunicaciones (IDC_MSCOMM1), invoque a ClassWizard, y vincule con el control una variable m_MSComm1. En este instante ser informado de que el control an no ha sido insertado en el proyecto. Al pul- sar el botn Aceptar, Developer Studio har este trabajo automticamente por us- ted insertando una o ms clases que encapsulan el control; en este caso, aadir al proyecto la clase CMSComm. Esta clase tiene el aspecto siguiente: class CMSComm : public CWnd { // ... 150 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
// Operations public: // ... void SetCommPort(short nNewValue); short GetCommPort(); // ... void SetPortOpen(BOOL bNewValue); BOOL GetPortOpen(); void SetRThreshold(short nNewValue); short GetRThreshold(); void SetRTSEnable(BOOL bNewValue); BOOL GetRTSEnable(); void SetSettings(LPCTSTR lpszNewValue); CString GetSettings(); void SetSThreshold(short nNewValue); short GetSThreshold(); void SetOutput(const VARIANT& newValue); VARIANT GetOutput(); void SetInput(const VARIANT& newValue); VARIANT GetInput(); void SetCommEvent(short nNewValue); short GetCommEvent(); // ... }; Observamos que la funcionalidad de la clase da acceso a cada una de las pro- piedades que expusimos anteriormente para este control. Por ejemplo, para esta- blecer el nmero de puerto que deseamos utilizar escribiramos: m_MSComm1.SetCommPort(2); Si abre ClassWizard y selecciona IDC_MSCOMM1 (objeto control de comu- nicaciones) puede observar en la lista de mensajes el evento OnComm. El evento OnComm se genera siempre que cambia el valor de la propiedad CommEvent para indicar que se ha producido un evento o un error en la comunicacin. La propiedad CommEvent contiene la constante numrica correspondiente al evento o al error que se ha generado. A continuacin indicamos estas constantes. Constantes de eventos: Constante Valor Descripcin comEvSend 1 Evento enviar datos. comEvReceive 2 Evento recibir datos. comEvCTS 3 Cambio en la lnea preparado para enviar (CTS). CAPTULO 3: COMUNICACIONES 151
comEvDSR 4 Cambio en la lnea equipo de datos prepara- do (DSR). comEvCD 5 Cambio en la lnea deteccin de portadora (CD). comEvRing 6 Deteccin de llamada. comEvEOF 7 Fin de fichero. Constantes de errores: Constante Valor Descripcin comEventBreak 1001 Seal de interrupcin recibida. comEventCTSTO 1002 Tiempo de espera de preparado para enviar sobrepasado. comEventDSRTO 1003 Tiempo de espera de equipo de datos prepa- rado sobrepasado. comEventFrame 1004 Error de trama. comEventOverrun 1006 Prdida de informacin en el puerto. comEventCDTO 1007 Tiempo de espera de deteccin de portadora sobrepasado. comEventRxOver 1008 Desbordamiento del buffer de recepcin. comEventRxParity 1009 Error de paridad. comEventTxFull 1010 Buffer de transmisin lleno. comEventDCB 1011 Error inesperado al recuperar el bloque de control de dispositivos (DCB) para el puerto. Constantes de InputMode: Constante Valor Descripcin comInputModeText 0 (Predeterminado) Los datos se recuperan como texto mediante la propiedad Input. comInputModeBinary 1 Los datos se recuperan como datos binarios mediante la propiedad Input. Constantes de protocolos: Constante Valor Descripcin comNone 0 Sin protocolo. comXonXoff 1 Protocolo XON/XOFF. comRTS 2 Protocolo RTS/CTS (Peticin de envo/prepa- rado para enviar). comRTSXOnXOff 3 Ambos protocolos (RTS y XON/XOFF). 152 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Tenga en cuenta que si establece las propiedades RThreshold o SThreshold a 0 (valor predeterminado para ambas propiedades), se desactiva la interceptacin de los eventos comEvReceive y comEvSend, respectivamente. Continuando con la aplicacin, modifique la barra de mens con los mens y las rdenes que se especificaron en la aplicacin anterior. Asimismo, implemente tambin la caja de dilogo Configuracin exactamente igual que lo hizo en la aplicacin anterior, excepto los datos de la caja de texto Control de flujo que ahora sern: Ninguno, Xon/Xoff, Hardware (RTS/CTS) y Am- bos (RTS y XON/XOFF). Aada tambin una nueva lista desplegable identificada por IDC_INPUTMODE y asgnele los datos Modo texto y Modo binario.
Recuerde que tiene que aadir a la aplicacin una clase CParamCom derivada de CDialog, basada en la plantilla IDD_PARAMETROSCOM que acaba de dise- ar. Guarde la declaracin y la definicin de esta clase en los ficheros param- com.h y paramcom.cpp. Despus, aada a la clase CParamCom las variables miembro m_nPuerto, m_nBaudios, m_nParidad, m_nBitsCar, m_nBitsParada y m_nControlFlujo vinculadas a cada una de las listas desplegables del dilogo. Invoque a ClassWizard y aada a la clase CControlComView la funcin miembro OnInitialUpdate y edtela como se indica a continuacin: void CControlComView::OnInitialUpdate() { CFormView::OnInitialUpdate();
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( FALSE ); } CAPTULO 3: COMUNICACIONES 153
Si ahora compila y ejecuta la aplicacin, obtendr un resultado similar al mos- trado en la figura siguiente:
Tipo VARIANT Si nos fijamos en las funciones miembro de la clase CMSComm que encapsula la funcionalidad del control de comunicaciones, en muchas de ellas aparece un tipo identificado por VARIANT. El tipo VARIANT no es ms que un tipo de datos genrico. Permite declarar variables capaces de almacenar datos de cualquier tipo predefinido. La declaracin de este tipo se basa en una estructura en la que cabe destacar dos miembros: vt para almacenar el tipo del dato (por ejemplo, VT_I4, VT_BOOL, VT_BSTR) y una unin con todos los posibles tipos de datos que pue- den ser descritos. typedef struct tagVARIANT { VARTYPE vt; // ... union { unsigned char bVal; // VT_UI1. short iVal; // VT_I2. long lVal; // VT_I4. float fltVal; // VT_R4. double dblVal; // VT_R8. VARIANT_BOOL boolVal; // VT_BOOL. SCODE scode; // VT_ERROR. CY cyVal; // VT_CY. DATE date; // VT_DATE. BSTR bstrVal; // VT_BSTR. // ... }; }; 154 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
El siguiente ejemplo muestra como se utiliza un VARIANT para pasar valo- res a una funcin. #include <OleAuto.h> // constantes y macros
// Calcular la longitud V_I4(vLong) = ::SysStringByteLen(V_BSTR(&vBstr));
return true; }
void MiFuncion() { bool vr; long lon; char *cad1 = "Cadena de caracteres", cad2[30]; BSTR bstrCadena; // un puntero de 32 bits a char bstrCadena = ::SysAllocString((const unsigned short *)cad1);
// Declarar las variables VARIANT vLong; VARIANT vBstr;
// Especificar los tipos vLong.vt = VT_I4; // long vBstr.vt = VT_BSTR;
// Asignar valores. // La razn de utilizar un & en las lneas siguientes, // es porque las macros requieren punteros. V_I4(&vLong) = 0L; V_BSTR(&vBstr) = bstrCadena;
// Llamar a la funcin vr = LongCadena(&vLong, vBstr); // ... // Acceder a los valores lon = V_I4(&vLong); strcpy(cad2, (const char *)V_BSTR(&vBstr)); // ...
// Liberar la memoria asignada a la cadena ::SysFreeString(bstrCadena); } CAPTULO 3: COMUNICACIONES 155
Como puede observar, hay que asignar el tipo de los datos manualmente y uti- lizar macros para asignar los datos. Cada tipo de datos tiene una macro asociada para acceder a los datos. Por ejemplo, el tipo VT_BSTR tiene asociada la macros V_BSTR. Visual C++proporciona tres clases diseadas para facilitar la utilizacin de datos de tipo VARIANT: COleVariant (MFC), CComVariant (ATL) y _variant_t. Esta ltima clase fue incorporada a Visual C++a partir de la versin 5 y es mucho ms funcional que las otras dos. Un objeto _variant_t encapsula el tipo de datos VARIANT. La clase maneja la asignacin y desasignacin del recurso, y llama a VariantInit (para iniciar vt a VT_EMPTY) y a VariantClear (para limpiar la estructura VARIANT) cuando es necesario. Esta clase, adems de otras funciones, define varios constructores que permiten construir un objeto _variant_t a partir de cualquier dato de un tipo pre- definido (incluyendo cadenas de caracteres), la funcin miembro SetString que permite asignar una cadena de caracteres a un objeto _variant_t y los operadores de asignacin (=), de comparacin (==, !=) y extractores u operadores de conver- sin de _variant_t a cada uno de los tipos predefinidos. Otra clase de inters es _bstr_t, que encapsula el tipo de datos BSTR. La cla- se maneja la asignacin y desasignacin del recurso llamando a SysAllocString y SysFreeString. Esta clase, adems de otras funciones, define varios constructores que permiten construir un objeto _bstr_t a partir de: char *, BSTR, etc., la fun- cin miembro length que permite obtener la longitud del objeto BSTR encapsu- lado, y los operadores de asignacin (=, +=), de concatenacin (+, ), de negacin (!) para verificar si el objeto BSTR encapsulado es NULL, de comparacin (==, !=, <, >, <=, >=) y extractores u operadores de conversin de _bstr_t a wchar_t * y char *. Ambas clases utilizan el fichero de cabecera comdef.h. Veamos el ejemplo anterior utilizando ahora esta clase: #include <comdef.h> // definiciones
// Calcular la longitud _bstr_t bstr = vBstr; *vLong = (long)bstr.length(); 156 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
return true; }
void MiFuncion() { bool vr; long lon; char *cad1 = "Cadena de caracteres", cad2[30];
// Declarar las variables _variant_t vLong; _variant_t vBstr;
// Asignar valores vLong = 0L; vBstr = cad1;
// Llamar a la funcin vr = LongCadena(&vLong, vBstr); // ... // Acceder a los valores lon = vLong; strcpy(cad2, (_bstr_t)vBstr); // ... } Manipular las comunicaciones El control de comunicaciones proporciona dos formas de manipular las comunica- ciones: 1. Notificando cundo ocurre un evento; por ejemplo, ha llegado un carcter o ha ocurrido un cambio en la lnea DCD (deteccin de portadora) o RTS (peti- cin de envo). Para manipular estos eventos y los posibles errores en las co- municaciones, implementaremos el procedimiento conducido por el evento OnComm del control de comunicaciones. 2. Verificando el valor de la propiedad CommEvent del control de comunica- ciones despus de cada funcin crtica en la aplicacin, para saber qu evento se ha dado o qu error ha ocurrido. Esta alternativa es preferible cuando la aplicacin es pequea; por ejemplo, un marcador de llamadas telefnicas, ya que no tiene sentido generar un evento despus de recibir cada carcter puesto que los nicos caracteres que se recibirn son las respuestas del mdem. CAPTULO 3: COMUNICACIONES 157
Cada control de comunicaciones que utilice se corresponde con un nico puerto serie. Esto es, si necesitamos acceder a ms de un puerto serie, hay que uti- lizar ms de un control de comunicaciones. Por ejemplo, para establecer una comunicacin a travs del puerto COM2 uti- lizando un control de comunicaciones m_MSComm1, los pasos son los siguientes: 1. Especifique el puerto que va a abrir. Para realizar esta operacin, asigne a la propiedad CommPort de m_MSComm1 el valor correspondiente a ese puerto. m_MSComm1.SetCommPort(2); Puede asignar a la propiedad CommPort cualquier nmero entre 1 y 16 (el valor predeterminado es 1). Cualquier otro valor producir un error. 2. Especifique las caractersticas de comunicacin. Para ello, asigne a la propie- dad Settings de m_MSComm1 los valores que definen las mismas: ' 19200 baudios, paridad ninguna, 8 bits por carcter ' y 1 bit de parada m_MSComm1.SetSettings("19200,N,8,1"); La propiedad Settings permite especificar la velocidad en baudios, la paridad y el nmero de bits de datos y de parada. De forma predeterminada, la veloci- dad en baudios es de 9600. La paridad sirve para la validacin de los datos. Normalmente no se utiliza y se establece a N. El valor de bits de datos indi- ca el nmero de bits que representan un bloque de datos. El bit de parada in- dica cundo se ha recibido un bloque de datos. 3. Abra el puerto de comunicacin. Para realizar esta operacin, asigne el valor True a la propiedad PortOpen de m_MSComm1: m_MSComm1.SetPortOpen(true); Una vez especificado el puerto que se desea abrir y la forma en que se reali- zar la transferencia de los datos, establecemos la conexin poniendo la pro- piedad PortOpen a true. No obstante, si la propiedad CommPort no se ha establecido correctamente o si el dispositivo no admite la configuracin espe- cificada, se producir un error, o bien puede ocurrir que el dispositivo externo no funcione correctamente. 4. Cuando quiera enviar datos al puerto de comunicaciones, utilice la propiedad Output de m_MSComm1. Esta propiedad permite escribir caracteres en el buffer de transmisin: 158 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
CString str; // ... _variant_t var(str); m_MSComm1.SetOutput(var); 5. Para recibir datos a travs del puerto, implemente la funcin que responda al evento OnComm y utilice la propiedad Input para obtener los datos. Esta propiedad retorna una cadena de caracteres tomados del buffer de recepcin. Los caracteres ledos son eliminados automticamente. void CControlComView::OnComm1() // responde al evento OnComm { // de m_MSComm1 _bstr_t bs; CString s;
switch(m_MSComm1.GetCommEvent()) { case 1: // vbMSCommEvSend: // ... break; case 2: // vbMSCommEvReceive: bs = m_MSComm1.GetInput(); s = (char *)bs; AfxMessageBox(s); OnVisualizarCars(s); break; // ... } // ... } La propiedad CommEvent retorna el evento o el error ms reciente ocurrido durante un proceso de comunicaciones. 6. Utilice la propiedad PortOpen de m_MSComm1, para cerrar el puerto de co- municaciones cuando stas finalicen. Para ello, asigne a esta propiedad el va- lor false. m_MSComm1.SetPortOpen(false); Interfaz de comunicaciones Siguiendo los pasos de la aplicacin anterior, vamos a implementar una interfaz con las operaciones ms comunes. Dicha interfaz, que resumimos en la tabla si- guiente, la integraremos en la clase CControlComView: Funcin Descripcin CAPTULO 3: COMUNICACIONES 159
Iniciar Lee del registro de Windows la configuracin inicial. Terminar Guarda en el registro de Windows la configuracin actual. EstablecerConexion Abre el puerto de comunicaciones. ConfigurarDisCom Establece los parmetros con los que se realizarn las comunicaciones. LeerCaracteresPuerto Lee un byte de la cola de entrada del puerto de co- municaciones. EscribirCarsPuerto Escribe un byte en la cola de salida del puerto de comunicaciones. CortarConexion Cierra el puerto de comunicaciones. OnComm1 Funcin que responde a los eventos que ocurren so- bre el puerto de comunicaciones. Aada la declaracin de estas funciones y de las variables necesarias para su implementacin a la declaracin de la clase CControlComView. class CControlComView : public CFormView { // ... // Operations public: ///////////////////////////////////////////////// // Interfaz para comunicaciones static int m_indPuerto; static int m_indBaudios; static int m_indParidad; static int m_indBitsCar; static int m_indBitsParada; static int m_indControlFlujo; static int m_indModoLectura;
UINT m_wTablaBaudios[13]; // tabla de velocidades BYTE m_TablaParidad[5]; // tabla de paridades BYTE m_TablaBitsParada[3]; // tabla bits de parada bool m_ConexionEstablecida; // true si el puerto fue abierto
}; Las variables m_ind... contienen el ndice del elemento seleccionado de las listas de la caja de dilogo Configuracin. Inicie la variable m_ConexionEstablecida a false en el constructor de su cla- se. Asigne valores a los arrays m_wTablaBaudios, m_TablaParidad y m_Tabla- BitsParada miembros de la clase CControlComView. Realice esta asignacin en el constructor de su clase, como se indica a continuacin: CControlComView::CControlComView() : CFormView(CControlComView::IDD) { m_wTablaBaudios[0] = 110; m_wTablaBaudios[1] = 300; m_wTablaBaudios[2] = 600; m_wTablaBaudios[3] = 1200; m_wTablaBaudios[4] = 2400; m_wTablaBaudios[5] = 4800; m_wTablaBaudios[6] = 9600; m_wTablaBaudios[7] = 14400; m_wTablaBaudios[8] = 19200; m_wTablaBaudios[9] = 38400; m_wTablaBaudios[10] = 56000; m_wTablaBaudios[11] = 128000; m_wTablaBaudios[12] = 256000;
m_TablaParidad[0] = 'N'; // ninguna m_TablaParidad[1] = 'E'; // par m_TablaParidad[2] = 'O'; // impar m_TablaParidad[3] = 'M'; // marca m_TablaParidad[4] = 'S'; // espacio //{{AFX_DATA_INIT(CControlComView) m_rx = _T(""); m_tx = _T(""); //}}AFX_DATA_INIT // TODO: add construction code here } A continuacin escribiremos cada una de las funciones descritas. Antes, inicie las variables estticas y defina las cadenas de caracteres necesarias para el registro de Windows en la implementacin de la clase CControlComView. int CControlComView::m_indPuerto = 1; // COM2 int CControlComView::m_indBaudios = 6; // 9600 int CControlComView::m_indParidad = 0; // ninguna CAPTULO 3: COMUNICACIONES 161
int CControlComView::m_indBitsCar = 4; // 8 int CControlComView::m_indBitsParada = 0; // 1 int CControlComView::m_indControlFlujo = 1; // Xon/Xoff int CControlComView::m_indModoLectura = 0; // texto/binario
static char szComu[] = "Comunicaciones"; static char szPuerto[] = "Puerto"; static char szBaudios[] = "Baudios"; static char szParidad[] = "Paridad"; static char szBitsCar[] = "BitsCar"; static char szBitsParada[] = "BitsParada"; static char szControlFlujo[] = "ControlFlujo"; Funcin Iniciar La funcin Iniciar obtiene del registro de Windows la configuracin por omisin del dispositivo de comunicaciones. void CControlComView::Iniciar() { CWinApp *pApp = AfxGetApp(); // Recuperar configuracin del registro de Windows m_indPuerto = pApp->GetProfileInt(szComu, szPuerto, 1); m_indBaudios = pApp->GetProfileInt(szComu, szBaudios, 6); m_indParidad = pApp->GetProfileInt(szComu, szParidad, 0); m_indBitsCar = pApp->GetProfileInt(szComu, szBitsCar, 4); m_indBitsParada = pApp->GetProfileInt(szComu, szBitsParada, 0); m_indControlFlujo = pApp->GetProfileInt(szComu, szControlFlujo, 1); } El lugar de donde se obtiene esta informacin del registro de Windows se es- pecifica de forma predeterminada en CControlCom.cpp as: SetRegistryKey(_T("Local AppWizard-Generated Applications")); Funcin Terminar La funcin Terminar ser invocada cuando se corta la comunicacin para guardar en el registro de Windows la configuracin actual del dispositivo de comunica- ciones. Esta configuracin es la que ser utilizada por omisin la prxima vez que se ejecute la aplicacin. void CControlComView::Terminar() { CWinApp *pApp= AfxGetApp(); // Guardar configuracin en el registro de Windows pApp->WriteProfileInt(szComu, szPuerto, m_indPuerto); pApp->WriteProfileInt(szComu, szBaudios, m_indBaudios); 162 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
pApp->WriteProfileInt(szComu, szParidad, m_indParidad); pApp->WriteProfileInt(szComu, szBitsCar, m_indBitsCar); pApp->WriteProfileInt(szComu, szBitsParada, m_indBitsParada); pApp->WriteProfileInt(szComu, szControlFlujo, m_indControlFlujo); } Funcin EstablecerConexion La funcin EstablecerConexion permite abrir el puerto de comunicaciones especi- ficado por la variable miembro m_indPuerto. Segn todo lo expuesto anterior- mente, la funcin EstablecerConexion puede ser as: bool CControlComView::EstablecerConexion() { // Cerrar el puerto si estuviera abierto if (m_MSComm1.GetPortOpen()) m_MSComm1.SetPortOpen(false);
// Especificar el puerto COM que se desea abrir m_MSComm1.SetCommPort(m_indPuerto + 1);
// Establecer el tamao de las colas de recepcin y de transmisin m_MSComm1.SetInBufferSize(COLARX); m_MSComm1.SetOutBufferSize(COLATX);
// Limpiar las colas Rx y Tx m_MSComm1.SetInBufferCount(0); m_MSComm1.SetOutBufferCount(0);
// Establecer los parmetros de la comunicacin ConfigurarDisCom();
// Caracteres que puede admitir el buffer de transmisin antes de que // el control genere el evento OnComm. Su valor predeterminado 0. m_MSComm1.SetSThreshold(1);
// Caracteres que se van a recibir antes de que el control // genere el evento OnComm. Su valor predeterminado 0. m_MSComm1.SetRThreshold(1);
// Abrir el puerto de comunicaciones m_MSComm1.SetPortOpen(true);
if (!m_MSComm1.GetPortOpen()) { AfxMessageBox( "Error: No se puede abrir el puerto COM", MB_OK | MB_ICONEXCLAMATION ); MessageBeep(0xFFFFFFFF); return false; } CAPTULO 3: COMUNICACIONES 163
m_ConexionEstablecida = true; return true; } Defina las constantes COLARX y COLATX, que definen los tamaos de las colas de recepcin y transmisin respectivamente, en el fichero ControlCom- View.cpp: #define COLARX 4096 #define COLATX 4096 Funcin ConfigurarDisCom Para construir el bloque de control del dispositivo (DCB) y configurar el disposi- tivo de comunicaciones, EstablecerConexion invoca a la funcin ConfigurarDis- Com, que se muestra a continuacin: void CControlComView::ConfigurarDisCom() { bool bEstablecer; char Settings[20]; char BitsParada[4]; switch(m_indBitsParada) { case 0: strcpy(BitsParada, "1"); break; case 1: strcpy(BitsParada, "1.5"); break; case 2: strcpy(BitsParada, "2"); break; } // Baudios, paridad, nmero de bits de datos y de parada wsprintf(Settings, "%ld,%c,%d,%s", m_wTablaBaudios[m_indBaudios], m_TablaParidad[m_indParidad], m_indBitsCar + 4, BitsParada); m_MSComm1.SetSettings(Settings);
// Establecer el control de flujo software bEstablecer = (m_indControlFlujo == 1 || m_indControlFlujo == 3); // Xon/Xoff if (bEstablecer) m_MSComm1.SetHandshaking(1); // comXOnXOff
// Establecer el control de flujo hardware bEstablecer = (m_indControlFlujo == 2 || m_indControlFlujo == 3); // RTS/CTS if (bEstablecer) m_MSComm1.SetHandshaking(2); // comRTS
// Cmo se leern los datos del puerto 164 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
m_MSComm1.SetInputMode(m_indModoLectura); // texto/binario } Controlar eventos Vimos que la funcin EstablecerConexion, antes de abrir el puerto de comunica- ciones, establece las propiedades SetSThreshold y SetRThreshold, que indican, respectivamente, los caracteres que puede admitir el buffer de transmisin antes de que el control genere el evento OnComm y los caracteres que se van a recibir antes de que el control genere el evento OnComm. El valor predeterminado para estas propiedades es cero. Si SetSThreshold es cero, no se generar el evento OnComm en la transmisin, y si SetRThreshold es cero, no se generar el even- to OnComm en la recepcin. Segn lo expuesto, ejecute ClassWizard, seleccione el control IDC_MS- COMM1 y aada la funcin OnComm1 miembro de CControlCom que respon- der al evento (mensaje) OnComm. OnComm1 ser invocada automticamente cada vez que se produzca sobre el puerto de comunicaciones un evento o un error de los expuestos anteriormente. Para saber de qu evento o error se trata y aplicar el tratamiento correspondiente, la funcin OnComm implementar bsicamente una sentencia switch. Al mismo tiempo visualizar sobre la barra de estado el mensaje correspondiente al evento o error ocurrido. Estos mensajes los definiremos como recursos en la tabla de cade- nas de caracteres de la aplicacin. Por ejemplo, si el evento es que se recibi informacin en la cola de recepcin del dispositivo de comunicaciones (comEvReceive), esta funcin leer dicha in- formacin y la procesar; en nuestro caso la visualizar en la caja de texto corres- pondiente. Por lo tanto, edite esta funcin como se indica a continuacin: void CControlComView::OnComm1() { CString strEvento, strError; _bstr_t bstrRecibida; // inluir <comdef.h>
switch(m_MSComm1.GetCommEvent()) { case 1: // comEvSend: strEvento.LoadString(IDS_COMEVSEND); break; case 2: // comEvReceive: strEvento.LoadString(IDS_COMEVRECEIVE); // Leer datos del puerto if ( LeerCaracteresPuerto(&bstrRecibida) ) OnVisualizarCars( (char *)bstrRecibida ); CAPTULO 3: COMUNICACIONES 165
break; case 3: // comEvCTS: strEvento.LoadString(IDS_COMMEVCTS); break; case 4: // comEvDSR: strEvento.LoadString(IDS_COMEVDSR); break; case 5: // comEvCD: strEvento.LoadString(IDS_COMEVCD); break; case 6: // comEvRing: strEvento.LoadString(IDS_COMEVRING); break; case 7: // comEvEOF: strEvento.LoadString(IDS_COMEVEOF); break; case 1001: // comErBreak: strError.LoadString(IDS_COMERBREAK); break; case 1002: // comErCTSTO: strError.LoadString(IDS_COMERCTSTO); break; case 1003: // comErDSRTO: strError.LoadString(IDS_COMERDSRTO); break; case 1004: // comErFrame: strError.LoadString(IDS_COMERFRAME); break; case 1006: // comErOverrun: strError.LoadString(IDS_COMEROVERRUN); break; case 1007: // comErCDTO: strError.LoadString(IDS_COMERCDTO); break; case 1008: // comErRxOver: strError.LoadString(IDS_COMERRXOVER); break; case 1009: // comErRxParity: strError.LoadString(IDS_COMERRXPARITY); break; case 1010: // comErTxFull: strError.LoadString(IDS_COMERTXFULL); break; }
if (!strEvento.IsEmpty()) { CWnd *pMainWnd = AfxGetApp()->m_pMainWnd; CStatusBar *pStatusbar = (CStatusBar *)pMainWnd- >GetDescendantWindow(AFX_IDW_STATUS_BAR); 166 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
pStatusbar->SetPaneText(0, strEvento); } else if (!strError.IsEmpty()) { MessageBeep(MB_ICONEXCLAMATION); strError += _T("\nAceptar para ignorar, Cancelar para salir"); int vr = AfxMessageBox(strError, MB_OKCANCEL | MB_ICONEXCLAMATION); if (vr == IDCANCEL) m_MSComm1.SetPortOpen(false); // cerrar el puerto } } Vemos que si el evento que se produjo fue comEvReceive, la funcin On- Comm invoca primero a la funcin LeerCaracteresPuerto para leer los caracteres recibidos y despus a OnVisualizarCars para aadir dicha informacin a la caja de texto referenciada por m_rx de la interfaz de usuario. Funcin LeerCaracteresPuerto La funcin LeerCaracteresPuerto lee datos del puerto COM, los almacena en un array de tipo _bstr_t y devuelve el nmero de caracteres ledos. int CControlComView::LeerCaracteresPuerto(_bstr_t *bstrRecibida) { *bstrRecibida = m_MSComm1.GetInput(); return bstrRecibida->length(); } Funcin EscribirCarsPuerto La funcin EscribirCarsPuerto escribe un bloque en el puerto COM procedente de un array pasado como parmetro. bool CControlComView::EscribirCarsPuerto(CString str) { _variant_t var(str); m_MSComm1.SetOutput(var);
return true; } CAPTULO 3: COMUNICACIONES 167
Funcin CortarConexion La funcin CortarConexion cierra el puerto de comunicaciones. Antes de cerrarlo, verifica si quedan datos en la cola de transmisin. Si hay datos intenta enviarlos, y si no, cierra el puerto de comunicaciones. bool CControlComView::CortarConexion() { if (m_ConexionEstablecida) { // Establecer un periodo de 10 segundos a partir de la // hora actual bool bTiempoSobrepasado = false; CTime TiempoLimite = CTime::GetCurrentTime() + CTimeSpan(0,0,0,10);
while (m_MSComm1.GetOutBufferCount()) { // Procesar todos los mensajes pendientes DoEvents();
if ( (CTime::GetCurrentTime() > TiempoLimite) || bTiempoSobrepasado) { int vr = AfxMessageBox("Datos no enviados", MB_ABORTRETRYIGNORE); switch (vr) { // Intentar enviar los datos durante otros 10 segs. case IDRETRY: TiempoLimite = CTime::GetCurrentTime() + CTimeSpan(0,0,0,10); break; // Ignorar el tiempo lmite case IDIGNORE: bTiempoSobrepasado = true; break; // Abortar el intento case IDABORT: return false; } } } m_MSComm1.SetPortOpen(false); m_ConexionEstablecida = false; } return true; } La funcin CortarConexin, puesto que establece un lazo que impedira que otras aplicaciones se ejecuten, invoca a la funcin DoEvents para permitir ejecutar cualquier otro mensaje que estuviera esperando en alguna cola de cualquier otra aplicacin. 168 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
void CControlComView::DoEvents() { MSG msg;
// Hay mensajes esperando en alguna cola de mensajes? while (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) { // Procesar el mensaje. Si no es posible, retornar. if (!AfxGetThread()->PumpMessage()) return; } } INTERFAZ DEL USUARIO Cuando se ejecute la aplicacin, una de las primeras tareas que hay que hacer es leer la configuracin almacenada en el registro de Windows. Este proceso lo rea- lizaremos desde la funcin OnInitialUpdate de la clase CControlComView. void CControlComView::OnInitialUpdate() { CFormView::OnInitialUpdate();
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( false );
// Iniciar el puerto y los controles de la IU UpdateData(false); m_botonEnviar.EnableWindow(false); Iniciar(); // configuracin inicial del puerto COM } Asimismo, la funcin OnInitialUpdate, adems de ajustar el marco de la ventana al tamao de la vista, inhabilita el botn Enviar. Cuando se establece una conexin entre dos equipos, previamente hay que especificar los parmetros bajos los cuales se realizar la misma. Para realizar esta operacin la aplicacin proporciona la orden Parmetros COM. Cuando el usua- rio ejecute esta orden se visualizar la caja de dilogo Configuracin que le per- mitir establecer los parmetros con los que se realizarn las comunicaciones. La respuesta a esta accin del usuario ser la funcin OnConfigParam. Ejecute ClassWizard, aada esta funcin y edtela como se indica a continuacin: void CControlComView::OnConfigParam() { // Crea el objeto de dilogo CAPTULO 3: COMUNICACIONES 169
CParamCom dlg;
// Actualizar los datos del dilogo dlg.m_nPuerto = m_indPuerto; dlg.m_nBaudios = m_indBaudios; dlg.m_nParidad = m_indParidad; dlg.m_nBitsCar = m_indBitsCar; dlg.m_nBitsParada = m_indBitsParada; dlg.m_nControlFlujo = m_indControlFlujo; dlg.m_nModoLectura = m_indModoLectura;
// Mostrar el cuadro de dilogo y verificar el botn pulsado if (dlg.DoModal() != IDOK) return;
if( EstablecerConexion() ) { m_ConexionEstablecida = true; AfxMessageBox( "Puerto de comunicaciones abierto", MB_OK | MB_ICONEXCLAMATION ); m_botonEnviar.EnableWindow(TRUE); } } Una vez visualizada la caja de dilogo Configuracin, si el usuario hace clic en el botn Aceptar (IDOK) se actualizarn los parmetros de configuracin con los valores seleccionados y se invocar a la funcin EstablecerConexion para abrir el puerto de comunicaciones especificado. Como la funcin anterior hace referencia a la clase CParamCom, es necesario aadir la lnea siguiente al fichero ControlComView.cpp. #include "ParamCom.h" Para no permitir modificar la configuracin del puerto de comunicaciones cuando est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CControlComView, controladora del mensaje UPDATE_COMMAND_UI. void CControlComView::OnUpdateConfigParam(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_ConexionEstablecida); 170 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
} Si el usuario hace clic en el botn Cancelar (IDCANCEL) de la caja de dilo- go Configuracin, no se realizar ninguna accin. En cambio, si el usuario hace clic en el botn Restaurar (IDC_DEFAULT) se establecern como valores por omisin los ltimos que fueron almacenados en el registro de Windows. void CParamCom::OnPorOmision() { // Parmetros por defecto de la comunicacin m_nPuerto = 1; // COM2 m_nBaudios = 6; // 9600 m_nParidad = 0; // Ninguna m_nBitsCar = 4; // 8 m_nBitsParada = 0; // 1 m_nControlFlujo = 1; // Xon/Xoff m_nControlFlujo = 0; // Texto/Binario
UpdateData(false); // Refrescar las listas desplegables } Ejecute ClassWizard y vincule la funcin OnPorOmision miembro de la clase CParamCom al botn Restaurar. La orden Establecer del men Conexin tiene como funcin abrir el puerto de comunicaciones con los parmetros actualmente seleccionados. Cuando el usuario ejecute esta orden, como respuesta ser invocada la funcin OnConexionEstable- cer miembro de la clase CControlComView, que a su vez invocar a la funcin EstablecerConexion. Por lo tanto, ejecute ClassWizard, vincule OnConexionEsta- blecer con la orden Establecer y edtela como se indica a continuacin: void CControlComView::OnConexionEstablecer() { if ( EstablecerConexion() ) { UpdateData(true); m_botonEnviar.EnableWindow(true);
UpdateData(false); } } Si el dispositivo de comunicaciones se abre satisfactoriamente, la funcin OnConexionEstablecer, adems, actualiza las variables miembro m_tx y m_rx li- CAPTULO 3: COMUNICACIONES 171
gadas con las cajas de texto de transmisin y de recepcin, respectivamente, y habilita el botn Enviar. Para no permitir abrir el puerto de comunicaciones cuando ya est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CControlComView, controladora del mensaje UPDATE_COMMAND_UI. void CControlComView::OnUpdateConexionEstablecer(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_ConexionEstablecida); } Para cerrar el puerto de comunicaciones, la interfaz de la aplicacin propor- ciona la orden Cortar del men Conexin. Para hacer operativa esta orden, vinc- lela con la funcin OnConexionCortar y edtela as: void CControlComView::OnConexionCortar() { Terminar(); // guardar la configuracin CortarConexion(); m_botonEnviar.EnableWindow(false); } Observe que la funcin OnConexionCortar primero llama a la funcin Termi- nar para guardar la configuracin actual en el registro de Windows, despus invo- ca a la funcin CortarConexion y finalmente inhabilita el botn Enviar. Para no permitir cerrar el puerto de comunicaciones cuando no est abierto, ejecute ClassWizard y aada la siguiente funcin miembro de CControlComView, controladora del mensaje UPDATE_COMMAND_UI. void CControlComView::OnUpdateConexionCortar(CCmdUI* pCmdUI) { pCmdUI->Enable(m_ConexionEstablecida); } Si el usuario cierra la aplicacin sin haber ejecutado previamente la orden Cortar, lgicamente estando el puerto abierto, el puerto es cerrado automtica- mente cuando el control es destruido. ENVIAR Y RECIBIR DATOS Para enviar datos, el usuario arrancar la aplicacin, establecer las comunicacio- nes, escribir el texto a enviar en la caja de Texto a transmitir y pulsar el botn Enviar. Por lo tanto, aada la funcin OnEnviar controladora del mensaje 172 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
WM_COMMAND que Windows enva al hacer clic en el botn Enviar y edtela como se muestra a continuacin: void CControlComView::OnEnviar() { int vr, n;
UpdateData(true); // Enviar los datos que hay en la caja transmisin if ( n = m_tx.GetLength() ) { vr = EscribirCarsPuerto( m_tx ); // Eliminar los caracteres transmitidos if ( vr ) { m_tx = ""; UpdateData( false ); } } } La funcin OnEnviar enva la informacin a la cola de salida invocando a la funcin EscribirCarsPuerto y despus limpia la caja Texto a transmitir. Cuando los caracteres enviados desde otra mquina se reciben en la cola de recepcin, el dispositivo de comunicaciones lo notifica por medio del mensaje OnComm. Como respuesta a este mensaje se ejecuta la funcin OnComm1, que en este caso, invoca a la funcin LeerCaracteresPuerto para obtener los datos del puerto, y despus a la funcin OnVisualizarCars para visualizarlos en la caja Tex- to recibido. Por lo tanto, ejecute ClassWizard, aada la funcin OnVisualizarCars como miembro de CControlComView y edtela como se indica a continuacin: void CControlComView::OnVisualizarCars(BYTE *pszBytes, int nBytes) { m_rx += strRecibida; // aadir los caracteres recibidos a los ya existentes UpdateData( false ); // visualizarlos GetDlgItem(IDC_TX)->SetFocus(); // enfocar la caja de transmisin } La aplicacin est finalizada. Ahora puede compilarla y ejecutarla. Para reali- zar las pruebas en un solo ordenador, puede unir los hilos numerados dos y tres de su puerto serie. De esta forma lo que transmita lo recibir de nuevo. Otra solucin, es conectar un mdem. Si enva una orden ATZ ms CR, el mdem le devolver OK. CAPTULO 3: COMUNICACIONES 173
Como ejemplo, puede aadir a la aplicacin un men Utilidades con una or- den Enviar fichero que permita transmitir ficheros de texto. A continuacin se presenta un cdigo que realiza esta operacin: void CControlComView::OnEnviarFichero() { // Caja de dilogo Abrir CFileDialog DlgAbrir( TRUE, _T("txt"), NULL, OFN_HIDEREADONLY|OFN_PATHMUSTEXIST, _T("Ficheros de texto (*.txt)|*.txt|\ Todos (*.*)|*.*||"), this);
int nTamBufTx = m_MSComm1.GetOutBufferSize(); int nTamFichTx = FicheroTx.GetLength(); char *pstrBuffer = new char[nTamBufTx+1];
// Leer/transmitir el fichero en bloques del tamao del buffer Tx for (int n = 0; n < nTamFichTx/nTamBufTx; n++) { FicheroTx.Read( pstrBuffer, nTamBufTx ); pstrBuffer[nTamBufTx] = 0; // carcter nulo de terminacin Transmitir( pstrBuffer, nTamBufTx ); }
// Si el tamao del fichero no es mltiplo de nTamBufTx, // enviar los datos restantes int nResto = nTamFichTx % nTamBufTx; if ( nResto ) { FicheroTx.Read( pstrBuffer, nResto ); pstrBuffer[nResto] = 0; // carcter nulo de terminacin Transmitir( pstrBuffer, nResto ); }
void CControlComView::Transmitir(char *pstrBuffer, int nTamBloque) { 174 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
LPWSTR pwstrDatos = new wchar_t[nTamBloque];
// Convertir el texto ASCII a UNICODE MultiByteToWideChar(CP_ACP, 0, pstrBuffer, nTamBloque, pwstrDatos, nTamBloque); _variant_t var(pstrBuffer);
// Enviar los datos al puerto m_MSComm1.SetOutput(var);
// Esperar a que el buffer Tx est vaco while (m_MSComm1.GetOutBufferCount()) DoEvents();
delete pwstrDatos; } La conversin del texto ASCII a UNICODE se ha hecho con fines didcticos. Si lo prefiere, puede enviar el texto directamente en ASCII. El tipo wchar_t (wide character - caracteres que utilizan dos bytes) es til para escribir programas por- tables a nivel internacional. Este tipo se encuentra definido en stddef.h y stdlib.h.
Faltan pginas... Faltan pginas...
CAPTULO 8 F.J.Ceballos/RA-MA
BIBLIOTECAS DINMICAS A pesar de la potencia de Visual C++, en algn momento se nos plantear algn problema que exija extendernos por encima de sus lmites. Afortunadamente, Vi- sual C++ no est limitado a sus capacidades internas. Como ya hemos visto, una aplicacin Visual C++ puede utilizar una amplia variedad de funciones pertene- cientes a la API de Windows. Si esto no es bastante, an podemos ir ms all es- cribiendo bibliotecas dinmicas personalizadas. Qu es una biblioteca dinmica? Una biblioteca dinmica, abreviadamente DLL (Dynamic Link Library), es un fichero ejecutable de funciones, o simple- mente de recursos, tal como mapas de bits o definiciones de fuentes, que pueden ser llamadas por cualquier aplicacin Windows. Una DLL personalizada es una biblioteca dinmica que nosotros mismos escribimos para satisfacer nuestras ne- cesidades. Windows est en gran medida formado a partir de DLL, y si no, eche una mi- rada a su directorio system. Por ejemplo, los ficheros KRNL386.EXE, GDI.EXE y USER.EXE, as como KEYBOARD.DRV, SYSTEM.DRV y SOUND.DRV, son todos DLL. Los ficheros de fuentes, esto es, con extensin .FON, tambin son DLL. Tambin encontrar cantidad de ficheros con extensin DLL; stos tambin son DLL, muchas de ellas pertenecientes a aplicaciones Windows instaladas, co- mo Excel o Visual Basic. Segn esto, es fcil adivinar que una DLL puede tener cualquier extensin, aunque .DLL es la ms estndar. De todas ellas, slo las bi- bliotecas dinmicas con extensin .DLL son cargadas automticamente por Win- dows, mientras que las DLL con otras extensiones tienen que ser cargadas explcitamente. Como todo, la utilizacin de las DLL tiene ventajas e inconvenientes. Por ejemplo, una funcin en una DLL est disponible para ser llamada por cualquier aplicacin Windows. Las ventajas que esto supone son, por una parte, reduccin 460 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
del cdigo de la aplicacin, al no tener que escribirla formando parte del cdigo de la misma, lo que redundar en velocidad de compilacin y de carga de la apli- cacin, as como en ahorro de espacio en el disco, ya que solamente existe una copia de la funcin. Y por otra parte, como estn separadas de la aplicacin, se pueden actualizar sin tener que tocar, y por lo tanto recompilar, las aplicaciones que las utilizan. Como inconvenientes, caben destacar la necesidad de estar pre- sentes y el tiempo que se necesita para acceder a ellas cuando se ejecuta una apli- cacin que las utiliza. Sin embargo, cuando se utilizan bibliotecas estticas, las funciones que la aplicacin necesita se incluyen en la misma durante el proceso de enlace, por lo que ni se pierde tiempo en leerlas ni la biblioteca tiene que estar presente. CREACIN DE UNA DLL EN Win32 El punto de entrada y de salida en una DLL en Win32 es una funcin denominada DllMain; sta es una de las diferencias con respecto a Win16. Esta funcin es op- cional; esto quiere decir que si no se escribe, el compilador asume una que no ha- ce nada, simplemente retorna un valor TRUE. DllMain utiliza el convenio de llamada WINAPI para sus tres parmetros en lugar de FAR PASCAL que ha quedado obsoleto. El prototipo de esta funcin es el siguiente: BOOL WINAPI DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved); y el esqueleto de la definicin de la funcin es as: BOOL WINAPI DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: //... case DLL_THREAD_ATTACH: //... case DLL_THREAD_DETACH: //... case DLL_PROCESS_DETACH: //... } return TRUE; } La funcin retorna el valor TRUE para indicar que se ha ejecutado satisfacto- riamente. Si durante el proceso de iniciacin la funcin retorna FALSE el sistema cancela el proceso. CAPTULO 8: BIBLIOTECAS DINMICAS 461
El parmetro dwReason indica la razn por la que ha sido llamada la funcin DllMain: iniciacin o terminacin, por un proceso o por un hilo (thread). Para recordar las diferencias entre procesos e hilos, repase al captulo Hilos incluido en esta obra. La siguiente tabla describe el significado de los posibles valores del parme- tro dwReason: Valor de dwReason Descripcin DLL_PROCESS_ATTACH Un nuevo proceso intenta acceder a la DLL; se asume un hilo. DLL_THREAD_ATTACH Un nuevo hilo de un proceso existente intenta ac- ceder a la DLL; esta llamada se hace a partir del segundo hilo de un proceso vinculado a la DLL. DLL_PROCESS_DETACH Un proceso abandona la DLL. DLL_THREAD_DETACH Uno de los hilos adicionales (no el primer hilo) de un proceso abandona la DLL. El parmetro lpReserved se reserva para ser utilizado por el sistema. El parmetro hModule es el handle a un ejemplar de la DLL. Cuando necesite acceder a los parmetros emitidos en la lnea de rdenes puede utilizar la funcin GetCommandLine de la API. Para crear una DLL, los pasos a seguir son similares a los ejecutados para es- cribir un programa C/C++. Escribimos los ficheros de cabecera (ficheros .h), los ficheros con la definicin de las funciones (ficheros .c o .cpp), el fichero de defi- nicin de mdulos (fichero .def), si es preciso, y los ficheros del proyecto para au- tomatizar la construccin de la DLL; estos ltimos, normalmente sern generados automticamente por Visual C++. Dependiendo de la utilidad de la DLL, en oca- siones puede ser que necesitemos escribir un fichero de recursos (fichero .rc). Como ejemplo, vamos a desarrollar una DLL denominada strucdll32.dll que incluya dos funciones denominadas Sumar y Restar. Ambas funciones tendrn dos parmetros de tipo double y retornarn, respectivamente, un resultado tam- bin de tipo double que se corresponder con la suma o la resta de los argumentos pasados en la llamada. 462 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Fichero de cabecera (.h) Los ficheros de cabecera contienen declaraciones y definiciones que el preproce- sador de C incluye en el fichero fuente justo antes de la compilacin. Para nuestro ejemplo, escribiremos un fichero strucdll32.h que contenga las declaraciones de las funciones Sumar y Restar: // ---------------------------------------------------- // Nombre del fichero: STCDLL32.H // // Este fichero de cabecera contiene las funciones // prototipo para las funciones exportables por la // DLL denominada STCDLL32 // // Copyright (c) Fco. Javier Ceballos // ----------------------------------------------------
// Variables globales
// Funciones prototipo #ifdef __cplusplus //si los ficheros fuente son .cpp extern "C" { #endif
#ifdef __cplusplus //si el compilador es C++ ... } #endif La macro WINAPI instruye al compilador para que interprete adecuadamente el convenio de llamada utilizado por la aplicacin que invoca a las funciones de la DLL. Fichero fuente (.c o .cpp) Fundamentalmente, el fichero fuente contiene la definicin de las funciones. Para nuestro ejemplo, escribiremos un fichero strucdll32.cpp que contenga los ficheros de cabecera windows.h y strucdll32.h, la funcin de entrada y de salida de la DLL, DllMain, y las funciones que nosotros deseamos incluir en la biblioteca, Sumar y Restar:
// ---------------------------------------------------- // Nombre del fichero: STCDLL32.CPP // CAPTULO 8: BIBLIOTECAS DINMICAS 463
// ste es el fichero fuente principal de la DLL, // el punto de entrada y de salida de la DLL // // Copyright (c) Fco. Javier Ceballos // ----------------------------------------------------
#include <windows.h> #include "stcdll32.h"
// ---------------------------------------------------- // Funcin DllMain // // ste es el punto de entrada y de salida de la DLL. // Esta funcin es llamada por Windows. Usted no tiene // que llamarla desde su aplicacin. // // Parmetros: // hModule - el handle para un ejemplar de la DLL // dwReason - razn por la que ha sido llamada la DLL // lpReserved - reservado para uso del sistema. // // Valor retornado: // TRUE - indicando que la DLL se ha iniciado // satisfactoriamente. // ----------------------------------------------------
BOOL WINAPI DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: // Escriba aqu el cdigo que escriba en LibMain (Win16). // Quiz tenga que hacer alguna modificacin por el hecho de // que puede ser llamada ms de una vez. // Retorne TRUE para salir de la DLL una vez cargada // o FALSE si la carga falla. break;
case DLL_THREAD_ATTACH: // Escriba aqu el cdigo de iniciacin que se tiene // que ejecutar cada vez que se cree un hilo en un proceso // que ya tiene cargada esta DLL. break;
case DLL_THREAD_DETACH: // Escriba aqu el cdigo de terminacin que se tiene // que ejecutar cada vez que un hilo en un proceso sale // de la DLL que dicho proceso ya tiene cargada. break;
case DLL_PROCESS_DETACH: // Escriba aqu el cdigo que escriba en WEP (Win16). // Este cdigo quiz no sea necesario porque el 464 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
// sistema operativo ya se encarga de esta labor. break; }
// ---------------------------------------------------- // Funcin Sumar // // Funcin que suma dos nmeros reales. // // Parmetros: // Param1 - valor real. Primer sumando // Param2 - valor real. Segundo sumando. // Valor retornado: // valor real - Param1 + Param2 // ----------------------------------------------------
// ---------------------------------------------------- // Funcin Restar // // Funcin que suma dos nmeros reales. // // Parmetros: // Param1 - valor real. Minuendo // Param2 - valor real. Sustraendo. // Valor retornado: // valor real - Param1 - Param2 // ----------------------------------------------------
double WINAPI Restar( double Param1, double Param2 ) { return (Param1 - Param2); } Fichero de definicin de mdulos (.def) El fichero de definicin de mdulos informa al enlazador (linker) sobre cmo crear el fichero ejecutable. Para nuestra DLL, puede ser el siguiente: ;------------------------------------------------------ ; Nombre del fichero: STCDLL32.DEF ; ; Mdulo de definicin del fichero ; ; Copyright (c) Fco. Javier Ceballos CAPTULO 8: BIBLIOTECAS DINMICAS 465
EXPORTS Sumar @1 Restar @2 Observe que LIBRARY especifica el nombre de la biblioteca, y que EX- PORTS define los nombres y los atributos de las funciones que explcitamente son puestas a disposicin de otras aplicaciones y DLL; el valor ordinal a conti- nuacin del nombre de la funcin define la localizacin del nombre de la funcin en la tabla de nombres de la aplicacin. La utilizacin del nmero de orden es ms rpida y requiere menos espacio. Este fichero es necesario para que se genere stc- dll32.lib. Si este fichero no se incluye en el proyecto slo se generar stcdll32.dll. Para construir el proyecto que dar lugar a la DLL utilizando Visual C++ (32 bits), ejecute la orden New del men File y elija la pgina Project. Despus, elija el tipo de proyecto Win32 Dynamic-Link Library, ponga nombre al proyecto y pulse el botn OK. A continuacin, si ya tiene editados los ficheros que van a formar parte del proyecto, adalos al mismo utilizando la orden Files del submen Add to Project del men Project y compile el proyecto. LLAMANDO A LAS FUNCIONES DE LA DLL A continuacin, vamos a implementar una aplicacin SDI que llame a las funcio- nes de la DLL. Ejecute AppWizard y cree una aplicacin denominada ApDll. De- rive la clase CApDllView de la clase CFormView. Abra el editor de recursos y personalice los recursos de la aplicacin. Edite la barra de mens para que slo aparezcan los mens Fichero con la orden Salir y Ayuda con la orden Acerca de ApDll... Cree una plantilla de dilogo para que al ejecutar la aplicacin se visualice una ventana como la siguiente: 466 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Cuando el usuario haga clic en el botn Sumar, aparecer en la tercera caja la suma de las dos primeras, y cuando haga clic en Restar, aparecer la diferencia. Para realizar la suma y la resta invocaremos a las funciones Sumar y Restar de la biblioteca dinmica stcdll32.dll. A continuacin se expone el cdigo correspondiente a esta aplicacin. Lo primero que vamos a hacer es, utilizando ClassWizard, vincular la variable m_Operando1 con la caja de texto IDC_OPERANDO1, la variable m_Operando2 con la caja de texto IDC_OPERAND02 y la variable m_Resultado con la caja de texto IDC_RESULTADO, todas de tipo double. A continuacin, aada la funcin OnInitialUpdate a la clase CApDllView y edtela para que permita ajustar el tamao de la ventana marco a la vista. void CApDllView::OnInitialUpdate() { CFormView::OnInitialUpdate();
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( false ); } El siguiente paso es dar funcionalidad a los botones Sumar y Restar. Para ello, edite las funciones que se indican a continuacin para que se ejecuten como respuesta al evento clic sobre cada uno de ellos. void CApDllView::OnSumar() { // Sumar UpdateData( true ); // actualizar variables miembro m_Resultado = Sumar( m_Operandol, m_Operando2 ); UpdateData( false ); // actualizar cajas de texto } CAPTULO 8: BIBLIOTECAS DINMICAS 467
void CApDllView::OnRestar() { // Restar UpdateData( true ); // actualizar variables miembro m_Resultado = Restar( m_Operandol, m_Operando2 ); UpdateData( false ); // actualizar cajas de texto } Observe que para realizar las operaciones de sumar y restar llamamos a las funciones de la biblioteca stcdll32.dll que hemos creado anteriormente. Antes de compilar el programa, hay que hacer todava dos cosas: especificar los prototipos de las funciones de Sumar y Restar e indicar al compilador la bi- blioteca que tiene que utilizar para enlazar estas funciones. Enlace esttico Incluya en el directorio de la aplicacin los ficheros stcdll32.h, stcdll32.dll y stcdll32.lib. Despus, aada al fichero ApDllView.cpp la lnea siguiente: #include "stcdll32.h" // prototipos de funciones El enlace esttico necesita de una biblioteca .lib que le informe acerca de los puntos de la DLL en los que se encuentran las funciones buscadas. Por eso, en nuestro proyecto tenemos que incluir la biblioteca stcdll32.lib. Para ello, ejecute la orden Settings del men Project, elija la pgina Link y aada en Object/library modules el nombre stcdll32.lib.
Ahora guarde la aplicacin, ejectela y observe cmo funciona. Como ejerci- cio, puede modificar la aplicacin y aadir otras funciones para otro tipo de ope- raciones matemticas, financieras, estadsticas, etc. 468 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Enlace dinmico Aunque el enlace esttico es el ms sencillo de realizar, hay veces que es necesa- rio realizar un enlace dinmico. Por ejemplo, cuando slo disponemos del fichero DLL, cuando la aplicacin no conoce el nombre de la DLL hasta la ejecucin, etc. El enlace dinmico consiste en cargar la DLL en memoria durante la ejecu- cin de la aplicacin utilizando la funcin AfxLoadLibrary (o LoadLibrary) de la API de Windows y averiguar posteriormente cul es el punto de entrada a la funcin que nos interesa utilizar. Para obtener el punto de entrada al que nos he- mos referido, utilizaremos la funcin GetProcAddress. Cuando no utilice la DLL descrguela utilizando la funcin AfxFreeLibrary (o FreeLibrary). Las aplica- ciones basadas en las MFC deberan utilizar las funciones Afx... en lugar de sus equivalentes puesto que han sido diseadas para manipular la sincronizacin de hilos. HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName ); BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib ); FARPROC GetProcAddress( HMODULE hModule, // handle al modulo DLL LPCSTR lpProcName // nombre de la funcin ); Para cargar una DLL dinmicamente, la aplicacin debe realizar las siguientes operaciones: Llamar a AfxLoadLibrary (o LoadLibrary) para cargar la DLL y obtener un handle al mdulo que la define. Llamar a GetProcAddress para obtener un puntero a cada una de las funcio- nes exportadas que la aplicacin necesita llamar. Llamar a AfxFreeLibrary (o FreeLibrary) cuando finalice el trabajo con la DLL. Por ejemplo: typedef UINT (CALLBACK* PFNDLLFUNC1)(DWORD,UINT); // ... HINSTANCE hDLL; // handle a la DLL PFNDLLFUNC1 pfnDllFunc1; // puntero a una funcin DWORD dwParam1; UINT uParam2, uValRet;
if (!pfnDllFunc1) { // Manipular el error AfxFreeLibrary(hDLL); return CODIGO_DE_ERROR; } else { // Llamar a la funcin uValRet = pfnDllFunc1(dwParam1, uParam2); } } Como ejemplo, reproduzca la misma aplicacin anterior, pero ahora alma- cnela en el directorio ApDll2. Para este ejemplo slo necesita incluir en el direc- torio de la aplicacin el fichero stcdll32.dll. Aada a la declaracin de la clase CApDllView las siguientes declaraciones y definiciones: typedef double (CALLBACK* PFNDLLFUNC1)(double, double); class CApDllView : public CFormView { private: HINSTANCE m_hDLL; // handle a la DLL PFNDLLFUNC1 m_pfnDllSumar; // puntero a la funcin Sumar PFNDLLFUNC1 m_pfnDllRestar; // puntero a la funcin Restar // ... }; A continuacin, modifique la funcin OnInitialUpdate, OnSumar y OnRes- tar como se indica a continuacin: void CApDllView::OnInitialUpdate() { CFormView::OnInitialUpdate();
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( false );
// Cargar stcdll32.dll m_hDLL = AfxLoadLibrary("stcdll32"); if (m_hDLL != NULL) { m_pfnDllSumar = (PFNDLLFUNC1)GetProcAddress(m_hDLL, "Sumar"); if (!m_pfnDllSumar) { AfxFreeLibrary(m_hDLL); AfxMessageBox("Error al acceder a la funcin Sumar"); } 470 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
m_pfnDllRestar = (PFNDLLFUNC1)GetProcAddress(m_hDLL, "Restar"); if (!m_pfnDllRestar) { AfxFreeLibrary(m_hDLL); AfxMessageBox("Error al acceder a la funcin Restar"); } } else AfxMessageBox("No se puede cargar stcdll32.dll"); }
void CApDllView::OnRestar() { // Restar UpdateData( true ); // actualizar variables miembro m_Resultado = (*m_pfnDllRestar)( m_Operando1, m_Operando2 ); UpdateData( false ); // actualizar cajas de texto } Para decrementar el contador de referencias de la DLL cuando la aplicacin finalice (cuando el contador de referencias sea cero, el sistema descargar la DLL de memoria), invoque a la funcin AfxFreeLibrary desde el destructor de la cla- se CApDllView, as: CApDllView::~CApDllView() { AfxFreeLibrary(m_hDLL); } RECURSOS EN UNA DLL Los recursos que una aplicacin necesita pueden ser aportados por la propia apli- cacin (fichero de recursos) o por una biblioteca dinmica. Esto es, adems de funciones, una biblioteca dinmica puede contener tambin recursos, tal como mapas de bits o ficheros wav, que pueden ser utilizados por cualquier aplicacin Windows que cargue esa biblioteca. Por ejemplo, supongamos una aplicacin Windows que tiene que visualizar un mapa de bits adaptado a la resolucin y nmero de colores que tenga el siste- ma. La solucin puede ser crear distintos mapas de bits en funcin de la resolu- cin de pantalla y del nmero de colores y que la aplicacin cargue el mapa de CAPTULO 8: BIBLIOTECAS DINMICAS 471
bits adecuado a nuestro sistema. Estos recursos, junto con otros, pueden ser alma- cenados en bibliotecas dinmicas para obtener un tiempo de ejecucin satisfacto- rio. Como ejemplo, vamos a crear una DLL denominada recvga.dll con los recursos para un sistema VGA y recsvga.dll con los mismos recursos pero reali- zados para un sistema SVGA. Los contenidos de las DLL sern los siguientes: DLL Recursos Descripcin recvga.dll bm4vga.bmp 640480 - 16 colores bm8vga.bmp 640480 - 256 colores mikeoldf.wav fichero de sonido recsvga.dll bm4svga.bmp 800600 - 16 colores bm8svga.bmp 800600 - 256 colores mikeoldf.wav fichero de sonido Para construir el proyecto que dar lugar a la DLL recvga.dll utilizando Vi- sual C++, ejecute la orden New del men File y elija la pgina Project. Despus, elija el tipo de proyecto Win32 Dynamic-Link Library, ponga el nombre recvga al proyecto y pulse el botn OK. A continuacin edite los ficheros que van a formar del proyecto y adalos al mismo. En este caso, crearemos los ficheros recvga.h (File - New - Files - C/C++ Header File), recvga.cpp (File - New - Files - C/C++ Source File), recvga.def (File - New - Files - Text File) y recvga.rc (File - New - Files - Resource Script). Para nuestros propsitos, el contenido de los ficheros recvga.def, recvga.h y recvga.cpp se reduce a sus esqueletos bsicos: // ---------------------------------------------------- // Nombre del fichero: RECVGA.H // // Este fichero de cabecera contiene las funciones // prototipo para las funciones exportables por la // DLL denominada RECVGA // // Copyright (c) Fco. Javier Ceballos // ----------------------------------------------------
// Variables globales // Funciones prototipo #ifdef __cplusplus //si los ficheros fuente son .cpp extern "C" { #endif
// Declaraciones de la funciones de la DLL
#ifdef __cplusplus //si el compilador es C++ ... } 472 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
#endif
// ---------------------------------------------------- // Nombre del fichero: REGVGA.CPP // // ste es el fichero fuente principal de la DLL, // el punto de entrada y de salida de la DLL // // Copyright (c) Fco. Javier Ceballos // ----------------------------------------------------
#include <windows.h> #include "recvga.h"
// ---------------------------------------------------- // Funcin DllMain // // ste es el punto de entrada y de salida de la DLL. // Esta funcin es llamada por Windows. Usted no tiene // que llamarla desde su aplicacin. // // Parmetros: // hModule - el handle para un ejemplar de la DLL // dwReason - razn por la que ha sido llamada la DLL // lpReserved - reservado para uso del sistema. // // Valor retornado: // TRUE - indicando que la DLL se ha iniciado // satisfactoriamente. // ----------------------------------------------------
BOOL WINAPI DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: // Escriba aqu el cdigo que escriba en LibMain (Win16). // Quiz tenga que hacer alguna modificacin por el hecho de // que puede ser llamada ms de una vez. // Retorne TRUE para salir de la DLL una vez cargada // o FALSE si la carga falla. break;
case DLL_THREAD_ATTACH: // Escriba aqu el cdigo de iniciacin que se tiene // que ejecutar cada vez que se cree un hilo en un proceso // que ya tiene cargada esta DLL. break;
case DLL_THREAD_DETACH: // Escriba aqu el cdigo de terminacin que se tiene // que ejecutar cada vez que un hilo en un proceso sale // de la DLL que dicho proceso ya tiene cargada. CAPTULO 8: BIBLIOTECAS DINMICAS 473
break;
case DLL_PROCESS_DETACH: // Escriba aqu el cdigo que escriba en WEP (Win16). // Este cdigo quiz no sea necesario porque el // sistema operativo ya se encarga de esta labor. break; } return TRUE; // DLL_PROCESS_ATTACH satisfactorio }
// ---------------------------------------------------- // Definicin de las funciones de la DLL // ----------------------------------------------------
;------------------------------------------------------ ; Nombre del fichero: RECVGA.DEF ; ; Mdulo de definicin. Permite crear RECVGA.LIB ; ; Copyright (c) Fco. Javier Ceballos ;------------------------------------------------------
LIBRARY RECVGA
DESCRIPTION 'DLL con recursos'
EXPORTS ;------------------------------------------------------ ; Funciones exportadas explcitamente ;------------------------------------------------------ Antes de editar recvga.rc, cree un directorio recvga\res para almacenar los re- cursos bm4vga.bmp, bm8vga.bmp y mikeoldf.wav correspondientes a esta DLL. Para construir los mapas de bits puede utilizar la utilidad Paint de Windows. Para editar recvga.rc, abra el editor de recursos y aada los recursos anterior- mente especificados.
474 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Finalmente, compile el proyecto. Como resultado obtendr el fichero rec- vga.lib y recvga.dll. Cuando otras aplicaciones utilicen los recursos proporcionados por recvga.dll, necesitarn conocer sus identificadores. Por lo tanto, cree un fichero idrecvga.h con dichos identificadores: // IDRECVGA.H. Identificadores de los recursos
#define IDR_MIKEOLDF_WAV 101 #define IDB_BM4VGA 102 #define IDB_BM8VGA 103 Siguiendo los mismos pasos, cree otro proyecto recsvga que d lugar a la DLL recsvga.dll. Cree tambin el fichero idrecsvga.h con los identificadores de los recursos proporcionados por recsvga.dll. Acceso a los recursos en una DLL Para explicar cmo utilizar las DLLs que acabamos de construir, vamos a crear una aplicacin, Recursos, que utilice los recursos de una u otra DLL en funcin de la resolucin de nuestro monitor. Adems, el fichero wav se ejecutar cuando se visualice el dilogo Acerca de. Para empezar, genere una nueva aplicacin SDI. Despus copie las bibliotecas anteriormente generadas en el directorio de la apli- cacin. Copie tambin los ficheros de cabecera idrecvga.h y idrecsvga.h. A continuacin, aada al fichero Recursos.h las dos lneas siguientes: #include "idrecvga.h" #include "idrecsvga.h" Cuando se ejecute la aplicacin, lo primero que hay que hacer es obtener un handle a la biblioteca de recursos que se vaya a utilizar; esto depender del nme- ro de colores y de la resolucin de pantalla. Para ello, en primer lugar, aada a la clase CRecursosApp las variables miembro m_hDll, m_BitsPorPixel, m_resx y m_resy: class CRecursosApp : public CWinApp { public: CRecursosApp(); HINSTANCE m_hDll; int m_BitsPorPixel, m_resx, m_resy; // ... }; CAPTULO 8: BIBLIOTECAS DINMICAS 475
Despus, aada a la funcin miembro InitInstance de la clase aplicacin el siguiente cdigo: BOOL CRecursosApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization // ... LoadStdProfileSettings();
// Cargar la biblioteca if ((m_hDll = AfxLoadLibrary(strDll)) == NULL) { char mensaje[80]; wsprintf(mensaje, "Error al cargar la DLL %s", strDll); AfxMessageBox( mensaje ); return FALSE; // Finalizar. Vuelve al S.O. }
// ... return TRUE; } Observe que la funcin InitInstance carga la biblioteca dinmica en funcin de la resolucin. Si la biblioteca que se intenta cargar no se encuentra en el direc- torio actual de trabajo, la aplicacin presenta mediante una caja de dilogo un mensaje de error y vuelve al sistema operativo. Cada vez que una aplicacin carga una biblioteca, un contador asociado con la misma es incrementado en una unidad. Cuando la aplicacin que ha cargado la biblioteca finalice, debe liberar la memoria asignada a la misma llamando a la funcin AfxFreeLibrary. Lo que hace esta funcin en realidad es decrementar en una unidad el contador asociado con la biblioteca. Cuando este contador alcanza el valor cero, la biblioteca es descargada de memoria; esto es, la memoria asigna- da a la biblioteca es liberada. 476 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Segn lo expuesto aada a la clase CRecursosApp la funcin miembro Exit- Instance para que invoque a la funcin AfxFreeLibrary. int CRecursosApp::ExitInstance() { if (m_hDll) AfxFreeLibrary( m_hDll );
return CWinApp::ExitInstance(); } Una vez cargada la biblioteca el siguiente paso es ver cmo se accede a los recursos de la misma. Por ejemplo, para acceder al recurso de sonido identificado por la cadena de caracteres IDR_MIKEOLDF_WAV y ejecutar el sonido cuando se visualice el di- logo Acerca de ..., modifique en el fichero Recursos.cpp la funcin OnAppAbout as: void CRecursosApp::OnAppAbout() { static bool bError = false; BOOL bCorrecto = FALSE; if (!bError) { // Obtener el handle a los recursos de la aplicacin HINSTANCE hRecsApp = AfxGetResourceHandle();
// Establecer como recursos de la aplicacin los de la // biblioteca cargada en InitInstance AfxSetResourceHandle( ((CRecursosApp *)AfxGetApp())->m_hDll );
// Tocar el recurso de sonido bCorrecto = PlaySound(MAKEINTRESOURCE(IDR_MIKEOLDF_WAV), ((CRecursosApp *)AfxGetApp())->m_hDll, SND_MEMORY | SND_ASYNC | SND_NODEFAULT | SND_RESOURCE); if (!bCorrecto) { AfxMessageBox("No se puede activar el sonido.\n" "El driver es el adecuado?"); bError = true; } // Restablecer los recursos iniciales de la aplicacin AfxSetResourceHandle( hRecsApp ); }
// Visualizar el dilogo Acerca de ... CAboutDlg aboutDlg; if ( aboutDlg.DoModal() == IDOK ) // visualizar dilogo { if (bCorrecto) PlaySound(NULL, NULL, 0); } CAPTULO 8: BIBLIOTECAS DINMICAS 477
} La funcin anterior primero invoca a AfxGetResourceHandle para obtener un handle a los recursos actuales de la aplicacin. A continuacin, utilizando la funcin AfxSetResourceHandle, establece como nuevos recursos los proporcio- nados por la biblioteca cargada. Una vez hecho esto, utiliza los recursos requeri- dos de la biblioteca (en nuestro caso el recurso de sonido identificado por IDR_MIKEOLDF_WAV) y cuando termina de utilizarlos, restablece los recursos iniciales de la aplicacin. Para reproducir el sonido proporcionado por IDR_MIKEOLDF_WAV la fun- cin OnAppAbout invoca a la funcin de la API PlaySound (esta funcin fue co- mentada en el captulo Multimedia). Recuerde que para utilizar esta funcin tiene que incluir el fichero de cabecera mmsystem.h e indicar al enlazador que uti- lice la biblioteca winmm.lib. Para acceder a los mapas de bits IDB_BMxxx de la biblioteca dinmica, pro- ceda de forma anloga. Por ejemplo, vamos a hacer que cuando se ejecute la apli- cacin, se cargue un mapa de bits y se visualice en la vista. El mapa de bits cargado ser adecuado para la resolucin y nmero colores establecidos en nues- tro sistema. Para realizar este proceso declare, en primer lugar, las siguientes va- riables miembro de la clase CRecursosView: class CRecursosView : public CFormView { private: CDC *m_pMemDCPantalla; // DC del rea de trabajo HBITMAP m_hBitmapAnterior; int m_nAncho, m_nAlto; // tamao del mapa de bits // ... }; Despus, aada a la clase CRecursosView la funcin miembro OnInitialUp- date. Esta funcin obtiene de los recursos almacenados en la biblioteca dinmica el mapa de bits adecuado a la resolucin y nmero de colores del sistema para ser seleccionado por un contexto de dispositivo de memoria compatible con la vista; dicho contexto ser utilizado posteriormente por la funcin OnDraw para pintar el mapa de bits en la vista. void CRecursosView::OnInitialUpdate() { CFormView::OnInitialUpdate();
BOOL bCorrecto = FALSE;
// Crear un DC en memoria compatible con el rea de trabajo CClientDC dc( this ); 478 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
m_pMemDCPantalla = new CDC; m_pMemDCPantalla->CreateCompatibleDC( &dc );
// Obtener el handle a los recursos iniciales de la aplicacin HINSTANCE hRecsApp = AfxGetResourceHandle(); // Establecer como recursos de la aplicacin los de la biblioteca AfxSetResourceHandle( ((CRecursosApp *)AfxGetApp())->m_hDll );
// Cargar el mapa de bits que se va a visualizar CRecursosApp *pApp = (CRecursosApp *)AfxGetApp(); CBitmap *pBitmapAnterior, *pBitmapActual = new CBitmap; if ((pApp->m_resx >= 800) && (pApp->m_resy >= 600)) // SVGA if (pApp->m_BitsPorPixel >= 8) // 256 o ms colores bCorrecto = pBitmapActual->LoadBitmap( IDB_BM8SVGA ); else bCorrecto = pBitmapActual->LoadBitmap( IDB_BM4SVGA ); else if (pApp->m_BitsPorPixel >= 8) // 256 o ms colores bCorrecto = pBitmapActual->LoadBitmap( IDB_BM8VGA ); else bCorrecto = pBitmapActual->LoadBitmap( IDB_BM4VGA ); if (!bCorrecto) AfxMessageBox("No se puede cargar el mapa de bits");
// Seleccionar el mapa de bits para el DC en memoria pBitmapAnterior = m_pMemDCPantalla->SelectObject(pBitmapActual);
// Guardar el handle del mapa de bits anterior m_hBitmapAnterior = (HBITMAP)pBitmapAnterior->GetSafeHandle();
// Restablecer los recursos propios de la aplicacin AfxSetResourceHandle( hRecsApp );
// Dimensiones del mapa de bits fuente BITMAP bm; // estructura de datos BITMAP pBitmapActual->GetObject( sizeof(bm), &bm ); CRect rect(0, 0, bm.bmWidth, bm.bmHeight); dc.DPtoLP( &rect ); // tamao del mapa de bits en unidades lgicas m_nAncho = rect.Width(); m_nAlto = rect.Height(); } Como hemos dicho, la funcin OnDraw miembro de CRecursosView pintar el mapa de bits seleccionado en el contexto de dispositivo de memoria m_pMem- DCPantalla cada vez que la ventana se repinte. void CRecursosView::OnDraw(CDC* pDC) { // Ver si hay un mapa de bits presente if (m_pMemDCPantalla == NULL) return; // No hay mapa de bits
// Visualizar el mapa de bits CAPTULO 8: BIBLIOTECAS DINMICAS 479
pDC->BitBlt( // DC destino 0, 0, // origen m_nAncho, // ancho m_nAlto, // alto m_pMemDCPantalla, // DC fuente 0, 0, // origen SRCCOPY ); // operacin } Finalmente, utilice el destructor de la clase CRecursosView para liberar los recursos asignados cuando la vista deje de existir. CRecursosView::~CRecursosView() { // Eliminar el mapa de bits if ( m_hBitmapAnterior ) { CBitmap *pbm = CBitmap::FromHandle( m_hBitmapAnterior ); delete m_pMemDCPantalla->SelectObject( pbm ); } // Eliminar el DC de memoria delete m_pMemDCPantalla; } OBJETOS COM COMO ALTERNATIVA A LAS DLLs En un captulo anterior expusimos cmo crear objetos COM utilizando la biblio- teca ATL. En esa exposicin vimos que Visual C++ proporciona un asistente, ATL COM AppWizard, que permite crear, entre otros, proyectos ATL de tipo DLL.
480 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
El proyecto creado por ATL COM AppWizard est inicialmente sin objetos COM. Para aadir un objeto COM utilizaremos la orden New ATL Object de ClassView. Esta orden abre un asistente que expone los distintos tipos de objetos que podemos aadir. Por ejemplo, en la categora objetos hay un objeto COM con una funcionalidad mnima (Simple object) que puede ser el idneo para presentar una alternativa a las DLL que se han expuesto anteriormente. Como ejemplo, vamos a construir un objeto COM como alternativa a la DLL stcdll32.dll que construimos al principio de este captulo. Para ello: 1. Cree un nuevo proyecto de tipo ATL COM Wizard denominado stccom32. Es- te proyecto dar lugar al fichero stccom32.dll. 2. Seleccione como tipo de servidor, Dynamic Link Library (DLL). Una vez creado el proyecto, procedemos a aadir un objeto COM simple. Pa- ra ello, abra ATL Object Wizard desde ClassView o, ejecutando la orden New ATL Object del men Insert de Developer Studio, seleccione Objects en el panel iz- quierdo de ATL Object Wizard y Simple Object en el panel derecho.
Despus de pulsar el botn Next en el dilogo anterior, se muestra el dilogo de propiedades que nos permitir definir el nombre del objeto, de la clase C++ y de la clase COM que soportarn el objeto. Seleccione la pgina Names y escriba en la caja Short Name el nombre MathCOM. El resto de las cajas se llenarn au- tomticamente. CAPTULO 8: BIBLIOTECAS DINMICAS 481
Esqueleto de la aplicacin El esqueleto de la aplicacin que hemos generado a travs de los asistentes ATL COM Wizard y ATL Object Wizard queda resumido en la figura siguiente:
Observamos tres partes bien diferenciadas: las funciones globales, la clase que encapsula el objeto COM y la interfaz que nos da acceso a la funcionalidad del objeto COM. Un objeto COM es parte de una biblioteca dinmica. Por lo tanto, primero ha- br que escribir el cdigo que d lugar a la biblioteca dinmica y despus aadi- remos a la misma objetos con sus interfaces. Precisamente lo que hace ATL COM Wizard es generar los ficheros necesarios para construir la DLL; en nuestro caso, estos ficheros son bsicamente: stccom32.def, stccom32.cpp, stccom32.idl y stc- com32.rc. Stccom32.cpp es el fichero principal de la aplicacin y contiene las funciones globales: entre ellas cabe destacar la funcin DllMain, que es el punto de entrada y de salida para la DLL; stccom32.def declara los parmetros del mdulo stc- com32.dll que se construir finalmente; stccom32.idl describe las interfaces de los objetos utilizando el lenguaje IDL (Interface Definition Language); y stccom32.rc aporta los recursos para la biblioteca. 482 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Cuando compile el proyecto ATL, el compilador MIDL generar un fichero stccom32.h, el cual define, desde el punto de vista de C++, las interfaces y clases disponibles en el fichero stccom32.idl. Cuando aadimos un objeto COM a la biblioteca, se genera una clase que en- capsula el objeto, as como una interfaz de acceso a la funcionalidad del objeto. Este trabajo es realizado por ATL Object Wizard. En nuestro caso fue aadida al proyecto la clase CMathCOM, cuyo cdigo lo podemos localizar en los ficheros MathCOM.h y MathCOM.cpp. Inicialmente dicho objeto no aporta ninguna fun- cionalidad. Precisamente el trabajo que tenemos que realizar a continuacin es proveer al objeto de la interfaz requerida para que cumpla el objetivo del diseo. Concretamente nuestro objetivo requiere los mtodos Sumar y Restar. Aadir mtodos Para aadir un mtodo al objeto COM, seleccione ClassView en la ventana WorkSpace, apunte con el ratn a la interfaz IMathCOM y haga clic con el botn derecho del ratn. En el men contextual que se visualiza, seleccione la orden Add Method. Aparecer un dilogo como el que se muestra a continuacin que le per- mitir introducir el nombre y los parmetros del mtodo:
Observe que la funcin devuelve un valor de tipo HRESULT que general- mente se corresponde con un valor distinto de cero si la funcin se ejecuta con xito, o cero en caso contrario. Por este motivo, nuestra funcin Sumar tiene tres parmetros, los dos primeros para los operandos y el tercero para el resultado. Una vez introducidos los datos que se muestran en la figura anterior, pulse el botn OK. En este instante acaba de aadir la funcin Sumar como miembro de la clase CMathCOM. Dicha funcin es accesible a travs de la interfaz IMathCOM. A continuacin, edite la funcin Sumar as: STDMETHODIMP CMathCOM::Sumar(double Param1, double Param2, double * Param3) { *Param3 = Param1 + Param2;
CAPTULO 8: BIBLIOTECAS DINMICAS 483
return S_OK; } Segn expusimos en el captulo de ATL, es aconsejable realizar las siguientes modificaciones en el fichero stccom32.idl: // stccom32.idl // ...
interface IMathCOM : IDispatch { [id(DISPID_SUMAR), helpstring("method Sumar")] HRESULT Sumar(double Param1, double Param2, double *Param3); }; A continuacin, procediendo de forma anloga, aada la funcin Restar. Aada tambin el identificador DISPID_RESTAR. STDMETHODIMP CMathCOM::Restar(double Param1, double Param2, double * Param3) { *Param3 = Param1 - Param2; return S_OK; } Con esto, hemos finalizado la implementacin de la biblioteca dinmica. Compile ahora el proyecto ATL para obtener el fichero stccom32.dll. Cuando Mi- crosoft Developer Studio finaliza la compilacin de la biblioteca dinmica, la re- gistra en el registro de Windows. Posteriormente, cuando una aplicacin utilice esa biblioteca, Windows recurrir a su registro para saber en qu directorio se en- cuentra. Por lo tanto, no sirve cambiar la biblioteca de directorio; ni siquiera al di- rectorio System. Qu tiene que hacer para registrar la biblioteca en otra posicin? Dos cosas: desregistrarla de la posicin actual y volverla a registrar en la nueva posicin, uti- lizando el programa regsvr32 que se encuentra en el directorio System. Por ejem- plo, eligiendo la orden Ejecutar del men Inicio, puede emitir las siguientes rdenes: Desregistrar stccom32.dll del directorio actual: regsvr32 /u "C:\Ejemplos\stccom32\Release\stccom32.dll" 484 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
Registrar stccom32.dll en el directorio System. Primero copie stccom32.dll en el directorio System: regsvr32 "C:\Windows\System\stccom32.dll" Utilizacin del servidor COM Una parte crtica de COM es cmo interactan los clientes y servidores. Un servi- dor COM es cualquier objeto que proporciona servicios a los clientes. Estos servi- cios aparecen en forma de implementaciones de interfaces COM que pueden ser llamadas por cualquier cliente que puede conseguir un puntero a una de las inter- faces en el objeto servidor. Hay dos tipos principales de servidores, in-process (se ejecuta en el espacio de proceso del controlador) y out-of-process (se ejecuta en su propio espacio de proceso). Los servidores in-process son implementados en una biblioteca dinmica (DLL), y los servidores out-of-process son implementa- dos en un archivo EXE. Adems, los servidores out-of-process pueden residir en una mquina local o remota. Como ejemplo de utilizacin del servidor stccom32.dll que acabamos de construir, vamos a reproducir la aplicacin ApDll que realizamos al principio de este captulo, y que ahora guardaremos en un directorio ApDll3. Incluya en el di- rectorio de la aplicacin los ficheros stccom32.h (definicin de la interfaz IMath- COM) y stccom32_i.c (identificadores COM). En el captulo de Componentes Software vimos que para que un cliente tu- viera acceso a un componente software (anteriormente llamado componente OLE y ahora objeto COM), era necesario que dicho cliente iniciara la biblioteca din- mica OLE, para lo cual tena que invocar a la funcin AfxOleInit, operacin que realizaremos desde InitInstance as: BOOL CApDllApp::InitInstance() { AfxOleInit(); // necesaria para COM // ... } Como ya sabemos, una aplicacin cliente, como es ApDll, puede acceder a un objeto COM solamente a travs de un puntero a una de sus interfaces, el cual, a su vez, permitir al cliente llamar a cualquiera de los mtodos que componen la in- terfaz. Nuestro objeto COM tiene una sola interfaz, IMathCOM. Por lo tanto, lo que tenemos que hacer ahora es obtener un puntero a esta interfaz. CAPTULO 8: BIBLIOTECAS DINMICAS 485
Obtener un puntero a una interfaz Realmente, una instancia de la interfaz IMathCOM es un puntero a un array de punteros a los mtodos especificados en dicha interfaz (para ms detalles, consulte el captulo de Componentes Software). Ya que COM no tiene un modelo de clase estricto, hay varias maneras de ob- tener un puntero a una interfaz de un objeto. Una de ellas puede ser llamar a una funcin de la API de la biblioteca COM que pueda crear un objeto en funcin de un identificador de clase (CLSID) y que devuelva un puntero a la interfaz solicita- da. Por ejemplo: IMathCOM *m_pIMathCOM; CoCreateInstance(CLSID_MathCOM, NULL, CLSCTX_INPROC_SERVER, IID_IMathCOM, (void **)&m_pIMathCOM)) La funcin CoCreateInstance crea un objeto sin iniciar, de la clase que tiene el CLSID especificado, en el sistema local (para crear un nico objeto en un sis- tema remoto hay que utilizar CoCreateInstanceEx, y para crear mltiples objetos con el mismo CLSID dispone de la funcin CoGetClassObject). El primer par- metro es el CLSID asociado con los datos y el cdigo que sera utilizado para crear el objeto. El segundo parmetro, si es NULL indica que el objeto no se construye a partir de otro; si no es NULL, entonces es un puntero a la interfaz IUnknown del objeto agregado. El tercer parmetro indica el contexto en el que se ejecutar el cdigo que manipula el nuevo objeto. El cuarto parmetro es el identificador de la interfaz utilizada para comunicar con el objeto. Y el quinto pa- rmetro es la direccin de la variable puntero que almacenar el puntero a la inter- faz requerida. Segn esto, aada el siguiente cdigo al fichero ApDllView.h: // ApDllView.h : interface of the CApDllView class // ... // Necesario para utilizar la biblioteca stccom32.dll #include "stccom32.h" // interfaz IMathCOM
class CApDllView : public CFormView { // ... protected: IMathCOM *m_pIMathCOM; // ... }; 486 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
A continuacin, aada a la funcin OnInitialUpdate el cdigo necesario para obtener el puntero m_pIMathCOM a la interfaz IMathCOM: void CApDllView::OnInitialUpdate() { CFormView::OnInitialUpdate();
m_pIMathCOM = 0; try { if (FAILED(CoCreateInstance(CLSID_MathCOM, NULL, CLSCTX_INPROC_SERVER, IID_IMathCOM, (void **)&m_pIMathCOM))) throw(_T("Tiene registrado el objeto COM?")); } catch(_com_error ErrorCom) { // Error COM. throw(ErrorCom.ErrorMessage()); } catch(TCHAR* pChar) { MessageBox( pChar, _T("Error en la aplicacin"), MB_ICONERROR); GetParentFrame()->PostMessage(WM_CLOSE); }
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( false ); } La funcin CoCreateInstance es una forma breve de conectar con un objeto de la clase asociada con el CLSID especificado, creando una instancia no iniciada, y liberando el objeto de la clase. As que, encapsula la funcionalidad siguiente: IClassFactory *pCF; CoGetClassObject(CLSID_MathCOM, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF); HRESULT hresult = pCF->CreateInstance(NULL, IID_IMathCOM, (void **)&m_pIMathCOM); pCF->Release(); // decrementa el contador de referencias para // la interfaz IClassFactory Para entender el cdigo anterior tiene que saber que cada objeto COM de un servidor implementa automticamente una interfaz IClassFactory. Esta clase es la responsable de crear instancias de la clase de objeto COM que la soporta (es CAPTULO 8: BIBLIOTECAS DINMICAS 487
anloga al operador new de C++). IClassFactory est derivada de IUnknown y contiene los mtodos: CreateInstance y LockServer. CreateInstance se utiliza para crear una instancia de una clase COM y LockServer incrementa o decremen- ta un contador de referencias dentro del servidor COM; cuando este contador es mayor que cero, el servidor no puede ser descargado de memoria. Segn lo ex- puesto, es posible obtener un puntero a una interfaz del objeto COM a travs del puntero a su interfaz IClassFactory as: 1. Determinar el identificador de la clase del objeto COM del cual se quiere crear una instancia. En nuestro caso CLSID_MathCOM. 2. Obtener el puntero a la interfaz IClassFactory para el CLSID especificado. IClassFactory *pCF; CoGetClassObject(CLSID_MathCOM, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF); 3. Crear una instancia no iniciada de la clase del objeto COM utilizando el m- todo CreateInstance de IClassFactory. La interfaz requerida desde el objeto debe ser aquella que el cliente pide cuando crea una instancia de la clase del objeto COM. De esta forma se obtiene un puntero a dicha interfaz. HRESULT hresult = pCF->CreateInstance(NULL, IID_IMathCOM, (void **)&m_pIMathCOM); pCF->Release(); La macro FAILED permite verificar si existe algn fallo. Si CoCreateIns- tance devuelve un valor negativo es que ha ocurrido un fallo. Un objeto _com_error es una excepcin detectada por los manipuladores de error en los ficheros de cabecera generados a partir de la biblioteca de tipos o por alguna de las clases que soportan COM. La clase _com_error est definida en comdef.h. Por lo tanto, debe incluir este fichero en ApDllView.cpp. Los identificadores de clase (CLSID) y de interfaz (IID) pasados como argu- mentos en la llamada a la funcin CoCreateInstance, estn definidos en el fiche- ro stccom32_i.c creado por el compilador MIDL cuando generamos la biblioteca dinmica stccom32.dll. Por lo tanto, incluya este fichero en ApDllView.cpp: // ApDllView.cpp : implementation of the CApDllView class // ...
#include "ApDllDoc.h" #include "ApDllView.h"
// Necesario para utilizar la biblioteca stccom32.dll 488 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
#include "stccom32_i.c" // identificadores COM #include "comdef.h" // necesario para _com_error Llamando a las funciones de la interfaz Obtenido el puntero a la interfaz y suponiendo que ya ha reconstruido la aplica- cin ApDll para que muestre la siguiente interfaz grfica, el siguiente paso es asignar funcionalidad a los botones Sumar y Restar.
Anlogamente a como procedimos cuando desarrollamos esta aplicacin al principio de este captulo, edite las siguientes funciones que tienen que ejecutarse como respuesta al evento clic sobre cada uno de los botones. void CApDllView::OnSumar() { // Sumar UpdateData( true ); // actualizar variables miembro m_pIMathCOM->Sumar( m_Operando1, m_Operando2, &m_Resultado ); UpdateData( false ); // actualizar cajas de texto }
void CApDllView::OnRestar() { // Restar UpdateData( true ); // actualizar variables miembro m_pIMathCOM->Restar( m_Operando1, m_Operando2, &m_Resultado ); UpdateData( false ); // actualizar cajas de texto } Observe que para realizar las operaciones de sumar y restar, llamamos a las funciones Sumar y Restar de la biblioteca stccom32.dll a travs del puntero m_pIMathCOM que acabamos de obtener. Los prototipos de estas funciones estn declarados en el fichero stccom32.h. Ahora ya puede compilar la aplicacin. En este caso no es necesario indicarle al enlazador la biblioteca que tiene que utilizar para enlazar estas funciones, por- que, como dijimos anteriormente, esta informacin la obtiene el sistema del regis- tro de Windows por tratarse de un objeto COM registrado. CAPTULO 8: BIBLIOTECAS DINMICAS 489
Clase _com_ptr_t Otra alternativa para obtener un puntero a una interfaz de un objeto COM es utili- zar la funcionalidad de la plantilla de clase _com_ptr_t definida en comdef.h. Un objeto _com_ptr_t encapsula un puntero a una interfaz COM (smart poin- ter). La plantilla _com_ptr_t manipula la asignacin y liberacin de los recursos necesarios, a travs de llamadas a las funciones miembro de la interfaz IUnknown: QueryInterface, AddRef, y Release, lo cual significa que estamos liberados de realizar este tipo de llamadas. Un puntero de este tipo es un objeto de una clase, obtenida a partir de la plan- tilla _com_ptr_t, particularizada para una determinada interfaz COM. Esta clase se obtiene a travs de la macro: _COM_SMARTPTR_TYPEDEF(IMiInterfaz, __uuidof(IMiInterfaz)); Esta macro toma como parmetros el nombre de la interfaz y su IID y cons- truye una clase particularizada para dicha interfaz de nombre, el nombre de la in- terfaz ms el sufijo Ptr. Por ejemplo, la lnea anterior dara lugar a la clase IMiInterfazPtr. A su vez, la macro __uuidof recupera el GUID asociado con el parmetro especificado. Como ejemplo, vamos a modificar la aplicacin ApDll creada en el ejemplo anterior para que ahora utilice la plantilla _com_ptr_t para obtener el puntero a la interfaz IMathCOM. Esta versin la guardaremos en el directorio ApDll4. En este caso procederemos as: 1. Asegrese de que InitInstance invoca a AfxOleInit: BOOL CApDllApp::InitInstance() { AfxOleInit(); // necesaria para COM // ... } 2. Aada al fichero ApDllView.h el cdigo indicado a continuacin: // ApDllView.h : interface of the CApDllView class // ... #include "stccom32.h" // interfaz IMathCOM #include "comdef.h" // necesario para _com_ptr_t y _com_error
// La siguiente macro define la clase IMathCOMPtr // a partir de la plantilla _com_ptr_t _COM_SMARTPTR_TYPEDEF(IMathCOM, __uuidof(IMathCOM)); 490 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
class CApDllView : public CFormView { // ... protected: // m_pIMathCOM encapsula el puntero a la interfaz IMathCOM IMathCOMPtr m_pIMathCOM;
// ... }; 3. Aada al fichero ApDllView.cpp el cdigo indicado a continuacin: // ApDllView.cpp : implementation of the CApDllView class // ...
#include "stccom32_i.c" // identificadores COM de IMathCOM // ...
try { if ( FAILED(m_pIMathCOM.CreateInstance(CLSID_MathCOM))) throw(_T("Tiene registrado el objeto COM?")); } catch(_com_error ErrorCom) { // Error COM. throw(ErrorCom.ErrorMessage()); } catch(TCHAR* pChar) { MessageBox( pChar, _T("Error en la aplicacin"), MB_ICONERROR); GetParentFrame()->PostMessage(WM_CLOSE); }
// Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); ResizeParentToFit( false ); } 4. Edite las funciones OnSumar y OnRestar de la misma forma que lo hizo ante- riormente. La funcin CreateInstance miembro de _com_ptr_t llama a CoCreateIns- tance para crear una instancia de un objeto de la clase asociada con el CLSID es- pecificado y obtener as el puntero a su interfaz IMathCOM, encapsulado en el objeto m_pIMathCOM de la clase IMathCOMPtr derivada de la plantilla CAPTULO 8: BIBLIOTECAS DINMICAS 491
_com_ptr_t. Tambin es llamada la funcin miembro Release para decrementar el contador de referencias del puntero previamente encapsulado. Esta rutina de- vuelve un valor HRESULT para indicar el xito o fallo de la operacin. Observe que las lneas: IMathCOMPtr m_pIMathCOM; m_pIMathCOM.CreateInstance(CLSID_MathCOM); equivalen a: IMathCOMPtr m_pIMathCOM(CLSID_MathCOM); Como m_pIMathCOM es un objeto de la clase IMathCOMPtr, una llamada de la forma: m_pIMathCOM->Sumar( m_Operando1, m_Operando2, &m_Resultado ); invoca a la funcin miembro operator- > () (sobrecarga al operador - > ) que de- vuelve el puntero a la interfaz, encapsulado en dicho objeto. Directriz #import Otra alternativa para obtener un puntero a una interfaz de un objeto COM es utili- zar la directriz #import. Esta directriz permite incorporar informacin de una bi- blioteca de tipos. El contenido de la biblioteca de tipos es convertido en clases C++ que describen las interfaces COM. Su sintaxis es de la forma siguiente: #import "fichero" [atributos] #import <fichero> [atributos] donde fichero es el nombre del fichero que contiene la informacin de la bibliote- ca de tipos. El fichero puede ser alguno de los tipos siguientes: Una biblioteca de tipos (fichero .TLB o .ODL). Un fichero ejecutable (.EXE). Una biblioteca dinmica que contenga los recursos de la biblioteca de tipos (tal como un .OCX o un .DLL). Un documento compuesto que posea la biblioteca de tipos. Cualquier otro formato de fichero que pueda ser admitido por la funcin de la API LoadTypeLib. Los atributos especificados en #import indican al compilador acciones espe- ciales que debe tomar. Por ejemplo, los contenidos de la biblioteca de tipos impor- tada a travs de un fichero de cabecera son normalmente definidos en un espacio 492 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
de nombres (namespace) cuyo nombre se especifica en la sentencia library en el fichero IDL original. Para indicarle al compilador que no genere de nuevo este espacio de nombres, hay que especificar el atributo no_namespace: #import "stccom32.tlb" no_namespace La directriz #import genera dos ficheros de cabecera, con el mismo nombre de la biblioteca y extensiones .TLH y .TLI, que reconstruyen la biblioteca de tipos en clases C++. Por ejemplo, la directriz anterior generara stccom32.tlh y stc- com32.tli. Estos ficheros pueden ser incluidos en la aplicacin cliente, utilizando una directriz #include, en el lugar donde sean necesarios. El cdigo del fichero .TLH de forma resumida hace lo siguiente: Incluye el fichero de cabecera comdef.h que contiene declaraciones como las correspondientes a _com_ptr_t y _com_error. Define las interfaces y clases de los objetos incluidas en el fichero IDL origi- nal. Por ejemplo IMathCOM y MathCOM. Invoca a la macro _COM_SMARTPTR_TYPEDEF que permite generar una clase (de la que hemos hablado en el apartado anterior) a partir de la plantilla _com_ptr_t, particularizada para una determinada interfaz COM. Un objeto de esta clase envuelve un puntero a dicha interfaz. Incluye el fichero .TLI que contiene las definiciones de las funciones miem- bro de las interfaces. Ambos ficheros, .TLH y .TLI, una vez generados son ledos y compilados por el compilador como si se hubiera incluido en el cdigo de la aplicacin la directriz #include para el fichero .TLH. Por ejemplo: #include "stccom32.tlh" Como ejemplo, vamos a modificar la aplicacin ApDll creada en el ejemplo anterior para que ahora utilice la directriz #import para obtener el puntero a la in- terfaz IMathCOM. Esta versin la guardaremos en el directorio ApDll5. En este caso procederemos as: 1. Copie en el directorio de la aplicacin cliente slo el fichero stccom32.tlb. 2. Asegrese de que InitInstance invoca a AfxOleInit: BOOL CApDllApp::InitInstance() CAPTULO 8: BIBLIOTECAS DINMICAS 493
{ AfxOleInit(); // necesaria para COM // ... } 3. Aada al fichero ApDllView.h el cdigo indicado a continuacin: // ApDllView.h : interface of the CApDllView class // ... // Importar la biblioteca de tipos. Permite, a travs de la // macro _COM_SMARTPTR_TYPEDEF, definir la clase IMathCOMPtr // a partir de la plantilla _com_ptr_t
#import "stccom32.tlb" no_namespace
class CApDllView : public CFormView { // ... protected: // m_pIMathCOM encapsula el puntero a la interfaz IMathCOM IMathCOMPtr m_pIMathCOM;
// ... }; 4. Aada al fichero ApDllView.cpp las mismas funciones OnInitialUpdate, On- Sumar y OnRestar que utiliz en el apartado anterior y realice sobre la fun- cin OnInitialUpdate la siguiente modificacin: sustituya CLSID_MathCOM por la expresin __uuidof(MathCOM). El resultado es el mismo, el CLSID de la clase pero sin tener que incluir otro fichero de cabecera. void CApDllView::OnInitialUpdate() { CFormView::OnInitialUpdate(); try { if ( FAILED(m_pIMathCOM.CreateInstance(__uuidof(MathCOM))) throw(_T("Tiene registrado el objeto COM?")); } catch(_com_error ErrorCom) { // Error COM. throw(ErrorCom.ErrorMessage()); } catch(TCHAR* pChar) { MessageBox( pChar, _T("Error en la aplicacin"), MB_ICONERROR); GetParentFrame()->PostMessage(WM_CLOSE); } // Ajustar el tamao de la ventana marco a la vista GetParentFrame()->RecalcLayout(); 494 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
ResizeParentToFit( false ); } Aadir otra interfaz En algunas ocasiones necesitaremos aadir una nueva interfaz a un objeto COM existente. En este apartado vamos a exponer los pasos que debe seguir para reali- zar este proceso. Vamos a realizar un ejemplo partiendo del objeto COM que creamos ante- riormente y que almacenamos en la biblioteca stccom32.dll. Cargue, entonces, el proyecto stccom32 y abra el fichero stccom32.idl (en el disco que acompaa al li- bro, este proyecto est almacenado en el directorio interfaz2 de este captulo). A continuacin aada una nueva interfaz IMathExCOM derivada de IUnknown. Es- to requiere generar un identificador global nico para asignrselo al atributo uuid de la interfaz. Genere el UUID (universally unique identifier) utilizando el pro- grama guidgen.exe proporcionado por Visual C++. Finalmente, aada a la defini- cin coclass MathCOM de la sentencia library el nombre de la nueva interfaz. La sentencia library contiene toda la informacin que el compilador MIDL necesita para generar la biblioteca de tipos. // stccom32.idl : IDL source for stccom32.dll // // This file will be processed by the MIDL tool to // produce the type library (stccom32.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; typedef enum prop_dispid { DISPID_SUMAR = 1, DISPID_RESTAR = 2, }PROP_DISPID;
[ uuid(934D4BA0-1C0B-11D2-8197-896206EF2C3A), helpstring("MathCOM Class") ] coclass MathCOM { [default] interface IMathCOM; interface IMathExCOM; }; }; El siguiente paso es editar el fichero MathCOM.h para especificar que la clase del objeto CMathCOM se derivar tambin de IMathExCOM. A su vez, sabemos que el mapa COM conecta la interfaz IUnknown con to- das las interfaces soportadas por el objeto. Todos los objetos COM deben imple- mentar una interfaz IUnknown para que a travs de su funcin miembro QueryInterface podamos determinar qu otras interfaces soporta el control y ob- tener, cuando sea preciso, un puntero a ellas. Por lo tanto, debemos aadir tam- bin a este mapa una entrada que especifique la nueva interfaz. // MathCOM.h : Declaration of the CMathCOM #ifndef __MATHCOM_H_ #define __MATHCOM_H_
#include "resource.h" // main symbols
496 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
////////////////////////////////////////////////////////////////// // CMathCOM class ATL_NO_VTABLE CMathCOM : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMathCOM, &CLSID_MathCOM>, public IDispatchImpl<IMathCOM,&IID_IMathCOM,&LIBID_STCCOM32Lib>, public IMathExCOM { public: CMathCOM() { }
// IMathCOM public: STDMETHOD(Restar)(double Param1, double Param2, double *Param3); STDMETHOD(Sumar)(double Param1, double Param2, double *Param3); }; #endif //__MATHCOM_H_ Con esto ha finalizado el proceso de aadir una nueva interfaz. Ahora, desde ClassView, puede aadir los mtodos que crea necesarios, igual que hizo cuando aadi los mtodos Sumar y Restar a la interfaz IMathCom. Como ejemplo, aada los mtodos Multiplicar y Dividir que se muestran a continuacin: typedef enum prop_dispid { DISPID_SUMAR = 1, DISPID_RESTAR = 2, DISPID_MULTIPLICAR = 3, DISPID_DIVIDIR = 4, }PROP_DISPID;
return S_OK; } Despus de esto, ha finalizado la implementacin de la nueva interfaz. Ahora, puede compilar el proyecto. Para probar la nueva interfaz de nuestro objeto COM, vamos a modificar el proyecto ApDll anterior (en el disco que acompaa al libro, el proyecto resultante est almacenado en el directorio interfaz2\ApDll6 de este captulo). La idea es aadir a la interfaz grfica dos nuevos botones, Multiplicar y Dividir, y asociarles con las funciones manipuladoras correspondientes, para que utilizando los mto- dos Multiplicar y Dividir de la interfaz IMathExCOM realicen las operaciones es- peradas. Segn lo expuesto, siga los siguientes pasos: Copie el fichero stccom32.tbl que acaba de generar en el proyecto stccom32, en el directorio ApDll6 de la aplicacin. Abra el editor de recursos y aada dos nuevos botones, Multiplicar y Dividir, a la interfaz grfica. Edite las funciones manipuladoras del evento clic para estos botones: void CApDllView::OnMultiplicar() { // Multiplicar UpdateData( true ); // actualizar variables miembro m_pIMathExCOM->Multiplicar( m_Operando1, m_Operando2, &m_Resultado ); UpdateData( false ); // actualizar cajas de texto }
void CApDllView::OnDividir() 498 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32
{ // Dividir UpdateData( true ); // actualizar variables miembro m_pIMathExCOM->Dividir( m_Operando1, m_Operando2, &m_Resultado ); UpdateData( false ); // actualizar cajas de texto } Defina, anlogamente a como defini el objeto m_pIMathCOM, el objeto m_pIMathExCOM como miembro de la clase CApDllView, para que a travs de l podamos referenciar la nueva interfaz IMathExCOM del objeto COM. // Importar la biblioteca de tipos #import "stccom32.tlb" no_namespace class CApDllView : public CFormView { // ... protected: // m_pIMathCOM encapsula el puntero a la interfaz IMathCOM IMathCOMPtr m_pIMathCOM; IMathExCOMPtr m_pIMathExCOM; // puntero a la interfaz IMathExCOM // ... }; Inicie el objeto m_pIMathExCOM en la funcin OnInitialUpdate de la clase CApDllView para que permita acceder a la interfaz IMathExCOM. void CApDllView::OnInitialUpdate() { CFormView::OnInitialUpdate();
try { if ( FAILED(m_pIMathCOM.CreateInstance(__uuidof(MathCOM)))) throw(_T("Tiene registrado el objeto COM?")); m_pIMathExCOM = m_pIMathCOM; // llama a QueryInterface } // ... } La sentencia m_pIMathExCOM = m_pIMathCOM invoca a la funcin miem- bro operator= de la clase de los objetos, que a su vez invoca a la funcin miem- bro QueryInterface que permite obtener en su segundo argumento un puntero a la interfaz identificada por su primer argumento. Para entenderlo mejor, el cdigo que se muestra a continuacin indica cmo utilizar QueryInterface para obtener un puntero a la interfaz IMathExCOM: IMathExCOM *p; m_pIMathCOM->QueryInterface(m_pIMathExCOM.GetIID(), (void **)(&p)); p->Dividir( m_Operando1, m_Operando2, &m_Resultado ); CAPTULO 8: BIBLIOTECAS DINMICAS 499
La funcin GetIID obtiene el identificador de la interfaz que representa el ob- jeto m_pIMathExCOM. Aplicacin de tipo consola Seguramente que en alguna ocasin necesitar utilizar una biblioteca COM en una aplicacin de tipo consola. La forma de proceder en estos casos es similar a la que acabamos de exponer para una aplicacin basada en la biblioteca MFC. Como ejemplo, vamos a construir un proyecto denominado ApDll7 de tipo Win32 Console Application que utilice la biblioteca stccom32.dll para realizar las operaciones que sta permite con la funcionalidad que expone a travs de sus in- terfaces. Cuando haya construido el proyecto, aada un fichero .cpp denominado ApDll y edtelo como se indica a continuacin: #include "iostream.h" void math(void); int menu(void);
#import "stccom32.tlb" no_namespace
int main() { OleInitialize(NULL); // iniciar la biblioteca COM math(); OleUninitialize(); // cerrar la biblioteca COM return 0; }
void math() { // pIMathCOM encapsula el puntero a la interfaz IMathCOM IMathCOMPtr pIMathCOM(__uuidof(MathCOM)); // Obtener un puntero a la interfaz IMathExCOM IMathExCOMPtr pIMathExCOM = pIMathCOM;
int menu(void) { int op; do { cout << "1. Sumar\n"; cout << "2. Restar\n"; cout << "3. Multiplicar\n"; cout << "4. Dividir\n"; cout << "5. Salir\n\n"; cout << "Seleccione la opcin deseada: "; cin >> op; } while (op < 1 || op > 5); return op; } Observe el cdigo sombreado. Comprobar que la forma de proceder no difie- re casi en nada de la expuesta en el apartado anterior. La directriz #import impor- ta la biblioteca de tipos stccom32, las funciones OleInitialize y OleUninitialize hacen el trabajo que haca AfxOleInit, y los punteros para acceder a las interfaces de nuestro objeto COM se han obtenido a partir de los objetos pIMathCOM y pIMathExCOM de las clase IMathCOMPtr e IMathExCOMPtr proporcionadas por #import. Compile la aplicacin, ejectela y compruebe que los resultados son los espe- rados. CAPTULO 8: BIBLIOTECAS DINMICAS 501
Faltan pginas... Faltan pginas...
Del mismo autor
Curso de programacin con PASCAL ISBN: 978-84-86381-36-3 224 pgs. Curso de programacin GW BASIC/BASICA ISBN: 978-84-86381-87-5 320 pgs. Manual para TURBO BASIC Gua del programador ISBN: 978-84-86381-43-1 444 pgs. Manual para Quick C 2 Gua del programador ISBN: 978-84-86381-65-3 540 pgs. Manual para Quick BASIC 4.5 Gua del programador ISBN: 978-84-86381-74-5 496 pgs. Curso de programacin Microsoft COBOL ISBN: 978-84-7897-001-8 480 pgs. Enciclopedia del lenguaje C ISBN: 978-84-7897-053-7 888 pgs. Curso de programacin QBASIC y MS-DOS 5 ISBN: 978-84-7897-059-9 384 pgs. Curso de programacin RM/COBOL-85 ISBN: 978-84-7897-070-4 396 pgs. El abec de MS-DOS 6 ISBN: 978-84-7897-114-5 224 pgs. Microsoft Visual C ++ (ver. 1.5x de 16 bits) Aplicaciones para Windows ISBN: 978-84-7897-180-0 846 pgs. + 2 disquetes Microsoft Visual C ++ Aplicaciones para Win32 (2 edicin) ISBN: 978-84-7897-561-7 792 pgs. + disquete Microsoft Visual C ++ Programacin avanzada en Win32 ISBN: 978-84-7897-344-6 888 pgs. + CD-ROM Visual Basic 6 Curso de programacin (2 edicin) ISBN: 978-84-7897-357-6 528 pgs. + disquete Enciclopedia de Microsoft Visual Basic 6 ISBN: 978-84-7897-386-6 1.072 pgs. + CD-ROM El lenguaje de programacin Java ISBN: 978-84-7897-485-6 320 pgs. + CD-ROM El lenguaje de programacin C# ISBN: 978-84-7897-500-6 320 pgs. + CD-ROM
Del mismo autor
El lenguaje de programacin Visual Basic.NET ISBN: 978-84-7897-525-9 464 pgs. + CD-ROM Java 2 Lenguaje y aplicaciones ISBN: 978-84-7897-745-1 392 pgs. + CD-ROM Programacin orientada a objetos con C ++ (4 edicin) ISBN: 978-84-7897-761-1 648 pgs. + CD-ROM C/C++ Curso de programacin (3 edicin) ISBN: 978-84-7897-762-8 708 pgs. + CD-ROM Microsoft C# Lenguaje y aplicaciones (2 edicin) ISBN: 978-84-7897-813-7 520 pgs. + CD-ROM Java 2. Interfaces grficas y aplicaciones para Internet (3 edicin) ISBN: 978-84-7897-859-5 718 pgs. + CD-ROM Aplicaciones .Net multiplataforma (Proyecto Mono) ISBN: 978-84-7897-880-9 212 pgs. + CD-ROM Enciclopedia del lenguaje C ++ (2 edicin) ISBN: 978-84-7897-915-8 902 pgs. + CD-ROM Enciclopedia de Microsoft Visual C# (3 edicin) ISBN: 978-84-7897-986-8 1.110 pgs. + CD-ROM Enciclopedia de Microsoft Visual Basic (2 edicin) ISBN: 978-84-7897-987-5 1.090 pgs. + CD-ROM Microsoft Visual Basic .NET Lenguaje y aplicaciones (3 edicin) ISBN: 978-84-9964-020-4 520 pgs. + CD-ROM Java 2 Curso de programacin (4 edicin) ISBN: 978-84-9964-032-7 820 pgs. + CD-ROM Microsoft C# Curso de programacin (2 edicin) ISBN: 978-84-9964-068-6 850 pgs. + CD-ROM Visual C#. Interfaces grficas y aplicaciones para Internet con WPF, WCF y Silverlight ISBN: 978-84-9964-203-1 956 pgs. + CD-ROM Visual Basic. Interfaces grficas y aplicaciones para Internet con WPF, WCF y Silverlight ISBN: 978-84-9964-204-8 938 pgs. + CD-ROM