Está en la página 1de 133

Visual C++

Programacin Avanzada en Win32


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);

if( m_hDisCom == INVALID_HANDLE_VALUE )
{
// Visualizar el error ocurrido.
MensajeDeError( GetLastError() );
MessageBeep(0xFFFFFFFF);
return FALSE;
}

// 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;

::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
nError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsg,
0,
NULL
);

// Mostrar el error
AfxMessageBox( (LPCTSTR)lpMsg, MB_OK | MB_ICONEXCLAMATION );
CAPTULO 3: COMUNICACIONES 131


// 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;

dcb.DCBlength = sizeof(DCB);
GetCommState(m_hDisCom, &dcb);

dcb.BaudRate = m_wTablaBaudios[m_indBaudios];
dcb.Parity = m_TablaParidad[m_indParidad];
dcb.ByteSize = (BYTE)(m_indBitsCar + 4);
dcb.StopBits = m_TablaBitsParada[m_indBitsParada];

// Establecer el control de flujo software
bEstablecer = (BYTE)(m_indControlFlujo == 1); // Xon/Xoff
dcb.fInX = dcb.fOutX = bEstablecer;
dcb.XonChar = 0x11; // ASCII_XON
dcb.XoffChar = 0x13; // ASCII_XOFF
dcb.XonLim = 100;
dcb.XoffLim = 100;

// Establecer el control de flujo hardware
bEstablecer = (BYTE)(m_indControlFlujo == 2); // DTR/DSR
dcb.fOutxDsrFlow = bEstablecer;
if (bEstablecer)
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
else
dcb.fDtrControl = DTR_CONTROL_ENABLE;

bEstablecer = (BYTE)(m_indControlFlujo == 3); // RTS/CTS
dcb.fOutxCtsFlow = bEstablecer;
if (bEstablecer)
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
else
dcb.fRtsControl = RTS_CONTROL_ENABLE;

// Otras especificaciones
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
132 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32


if(SetCommState(m_hDisCom, &dcb) == 0)
{
// Visualizar el error ocurrido.
MensajeDeError( GetLastError() );
MessageBeep(0xFFFFFFFF);
return FALSE;
}

// 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

m_wTablaBaudios[9] = CBR_38400;
m_wTablaBaudios[10] = CBR_56000;
m_wTablaBaudios[11] = CBR_128000;
m_wTablaBaudios[12] = CBR_256000;

m_TablaParidad[0] = NOPARITY;
m_TablaParidad[1] = EVENPARITY;
m_TablaParidad[2] = ODDPARITY;
m_TablaParidad[3] = MARKPARITY;
m_TablaParidad[4] = SPACEPARITY;

m_TablaBitsParada[0] = ONESTOPBIT;
m_TablaBitsParada[1] = ONE5STOPBITS;
m_TablaBitsParada[2] = TWOSTOPBITS;

//{{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];

bEscribir = WriteFile( m_hDisCom, pBytesAEscribir, dwBytes,
&dwNumBytes, &m_sOverWrite );

// 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.


while(!GetOverlappedResult( m_hDisCom,
&m_sOverWrite, &dwNumBytes, FALSE ))
{
142 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32

dwError = GetLastError();
// Si no termin, dwError vale ERROR_IO_INCOMPLETE
if(dwError == ERROR_IO_INCOMPLETE)
{
dwBytesEnviados += dwNumBytes;
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;
}
}
dwBytesEnviados += dwNumBytes;
if( dwBytesEnviados != dwBytes )
wsprintf(szError,"\nProbablemente se ha sobrepasado el "
"tiempo lmite.\nBytes enviados %ld", dwBytesEnviados);
else
wsprintf(szError,"\n%ld bytes escritos", dwBytesEnviados);
}
else // error distinto de ERROR_IO_PENDING
{
ClearCommError( m_hDisCom, &dwCodsError, &EstadoCom );
if ( dwCodsError > 0 )
{
wsprintf( szError, "Com: <CE-%u>", dwCodsError );
AfxMessageBox( szError, MB_OK | MB_ICONEXCLAMATION );
}
return FALSE;
}
}
return TRUE;
}
Funcin CortarConexion
La funcin CortarConexion cierra el puerto de comunicaciones. Para ello, pone la
variable m_ConexionEstablecida a valor FALSE para que el hilo ControlarEven-
tos finalice, inhabilita los eventos, desactiva la lnea DTR, termina las operaciones
de E/S, limpia las colas de recepcin y de transmisin, y cierra el puerto de comu-
nicaciones.
CAPTULO 3: COMUNICACIONES 143

BOOL CCommView::CortarConexion()
{
m_ConexionEstablecida = FALSE; // finaliza el hilo

// Inhabilitar todos los eventos
SetCommMask( m_hDisCom, 0 );

// Esperar hasta que el hilo finalice
while( m_bHiloActivo );

// Desactivar DTR
EscapeCommFunction( m_hDisCom, CLRDTR );

// 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;

// Actualizar la configuracin
m_indPuerto = dlg.m_nPuerto;
m_indBaudios = dlg.m_nBaudios;
m_indParidad = dlg.m_nParidad;
m_indBitsCar = dlg.m_nBitsCar;
m_indBitsParada = dlg.m_nBitsParada;
m_indControlFlujo = dlg.m_nControlFlujo;

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

bool LongCadena(VARIANT *vLong, VARIANT vBstr)
{
// Validar los parmetros
if ( vLong->vt != VT_I4 || vBstr.vt != VT_BSTR)
return false;

// 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

bool LongCadena(_variant_t *vLong, _variant_t vBstr)
{
// Validar los parmetros
if ( vLong->vt != VT_I4 || vBstr.vt != VT_BSTR)
return false;

// 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

static void Iniciar();
static void Terminar();
bool EstablecerConexion();
void ConfigurarDisCom();
bool CortarConexion();
int LeerCaracteresPuerto(_bstr_t *bstr);
bool EscribirCarsPuerto(CString str);
/////////////////////////////////////////////////
// ...
160 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32

};
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;

// Actualizar la configuracin
m_indPuerto = dlg.m_nPuerto;
m_indBaudios = dlg.m_nBaudios;
m_indParidad = dlg.m_nParidad;
m_indBitsCar = dlg.m_nBitsCar;
m_indBitsParada = dlg.m_nBitsParada;
m_indControlFlujo = dlg.m_nControlFlujo;
m_indModoLectura = dlg.m_nModoLectura;

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);

if (DlgAbrir.DoModal() != IDOK) return;

CFile FicheroTx( DlgAbrir.GetPathName(), CFile::modeRead );

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 );
}

delete pstrBuffer;
FicheroTx.Close();
GetDlgItem(IDC_TX)->SetFocus();
}

void CControlComView::OnUpdateUtenviarfichero(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_ConexionEstablecida);
}

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

double WINAPI Sumar( double Param1, double Param2 );
double WINAPI Restar( double Param1, double Param2 );

#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;
}

return TRUE; // DLL_PROCESS_ATTACH satisfactorio
}

// ----------------------------------------------------
// 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
// ----------------------------------------------------

double WINAPI Sumar( double Param1, double Param2 )
{
return (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

;------------------------------------------------------

LIBRARY STCDLL32

DESCRIPTION 'Ejemplo de creacin de una DLL'

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;

hDLL = AfxLoadLibrary("MiDLL");
if (hDLL != NULL)
{
pfnDllFunc1 = (PFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
CAPTULO 8: BIBLIOTECAS DINMICAS 469

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::OnSumar()
{
// Sumar
UpdateData( true ); // actualizar variables miembro
m_Resultado = (*m_pfnDllSumar)( m_Operando1, m_Operando2 );
UpdateData( false ); // actualizar cajas de texto
}

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();

// Comprobar qu DLL hay que cargar
CString strDll;
CDC dc;
// Obtener informacin del contexto de dispositivo
dc.CreateIC("DISPLAY", NULL, NULL, NULL);
m_BitsPorPixel = dc.GetDeviceCaps( BITSPIXEL );
m_resx = dc.GetDeviceCaps( HORZRES );
m_resy = dc.GetDeviceCaps( VERTRES );
if ((m_resx >= 800) && (m_resy >= 600)) // SVGA
strDll = ".\\recsvga.dll";
else
strDll = ".\\recvga.dll"; // VGA

// 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
// ...

typedef enum prop_dispid
{
DISPID_SUMAR = 1,
}PROP_DISPID;

// ...

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
// ...

void CApDllView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();

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;

[
object,
uuid(934D4B9F-1C0B-11D2-8197-896206EF2C3A),
dual,
helpstring("IMathCOM Interface"),
pointer_default(unique)
]
interface IMathCOM : IDispatch
{
[id(DISPID_SUMAR), helpstring("method Sumar")]
HRESULT Sumar(
double Param1, double Param2, double *Param3);
[id(DISPID_RESTAR), helpstring("method Restar")]
HRESULT Restar(
double Param1, double Param2, double *Param3);
};
CAPTULO 8: BIBLIOTECAS DINMICAS 495


[
object,
uuid(AF9CD1C0-2023-11d2-8197-F60ED797973D),
dual,
helpstring("IMathExCOM Interface"),
pointer_default(unique)
]
interface IMathExCOM : IUnknown
{

};

[
uuid(934D4B92-1C0B-11D2-8197-896206EF2C3A),
version(1.0),
helpstring("stccom32 1.0 Type Library")
]

library STCCOM32Lib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
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()
{
}

DECLARE_REGISTRY_RESOURCEID(IDR_MATHCOM)

BEGIN_COM_MAP(CMathCOM)
COM_INTERFACE_ENTRY(IMathCOM)
COM_INTERFACE_ENTRY(IMathExCOM)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// IMathExCOM
public:

// 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;

// ...

interface IMathExCOM : IUnknown
{
[id(DISPID_MULTIPLICAR), helpstring("method Multiplicar")]
HRESULT Multiplicar(
double Param1, double Param2, double *Param3);
[id(DISPID_DIVIDIR), helpstring("method Dividir")]
CAPTULO 8: BIBLIOTECAS DINMICAS 497

HRESULT Dividir(
double Param1, double Param2, double *Param3);
};
A continuacin, edite las funciones que acaba de aadir:
STDMETHODIMP CMathCOM::Multiplicar(double Param1, double Param2, double * Param3)
{
*Param3 = Param1 * Param2;

return S_OK;
}

STDMETHODIMP CMathCOM::Dividir(double Param1, double Param2, double * Param3)
{
*Param3 = Param1 / Param2;

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;

if (pIMathCOM == 0 || pIMathExCOM == 0)
return;

double dato1, dato2, resultado;
int operacion;

while(1)
{
operacion = menu();
if (operacion != 5)
{
cout << "dato 1: ";
cin >> dato1;
cout << "dato 2: ";
500 VISUAL C++. PROGRAMACIN AVANZADA EN WIN32

cin >> dato2;
switch (operacion)
{
case 1:
pIMathCOM->Sumar(dato1, dato2, &resultado);
break;
case 2:
pIMathCOM->Restar(dato1, dato2, &resultado);
break;
case 3:
pIMathExCOM->Multiplicar(dato1, dato2, &resultado);
break;
case 4:
pIMathExCOM->Dividir(dato1, dato2, &resultado);
break;
}
cout << "resultado: " << resultado << endl << endl;
}
else
break;
}
}

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