Manual C++6.0
Manual C++6.0
Agradecimientos.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Introducción.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
Lo que ya debería saber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv
Breve historia de Visual C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
Lo que contiene este libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
Código de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
El CD que se acompaña . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xx
Unas cuantas definiciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxii
Lecturas complementarias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii
Parte l. Fundamentos
Capítulo 1. El entorno ........................................... 3
Barras de herramientas y menús . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Ventanas de entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Ayuda en línea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Biblioteca MSDN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Trabajar fuera del entono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Capítulo 2. AppWizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Ventajas de AppWizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Ejecución de AppWizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Creación de una DLL con AppWizard . . . . . . . . . . . . . . . . . . . . . . . . . 45
vi¡¡ CONTENIDO
Parte VI . Apéndices
Apéndice A . Formatos de archivo ASCll y ANSl . . . . . . . . . . . . . . . . . . . . . . . 531
Apéndice B. Clases MFC soportadas por ClassWizard . . . . . . . . . . . . . . . . . . 537
Apéndice C. Introducción a VBScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544
Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
Control del flujo del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
Depuración de una macro VBScript . . . . . . . . . . . . . . . . . . . . . . . . . . 554
Funciones de librería . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
Agradecimientos
Las segundas ediciones son más fáciles de escribir que las primeras, una máxima de escri-
tor que uno comienza a cuestionarse a mitad de camino de la segunda edición. Mas he
disfrutado escribiendo (o al menos ampliando) este libro, sobre todo debido a que he
tenido otra vez la ayuda de gente dedicada. Aunque el equipo de Microsoft Press que ha
manejado la edición anterior se ha desplazado a otras tareas, un regalo en el dinámico
universo de la publicación de libros, sus contribuciones todavía perduran en el libro, sella-
das para siempre con su cuidado y competencia.
Para la primera edición, Lucinda Rowley hizo de editora del proyecto, dedicando mu-
cho tiempo y una atenta mirada en la revisión de los manuscritos, mientras gestionaba mil
otras tareas (en esta edición, titulada Microsoft Visual C++ 6.0. Manual del programador,
apodé a Lucinda como «la editora con la que sueña todo escritor». Todavía lo siento así).
La editora del manuscrito Vicky Thulman leyó y releyó cada frase, y los editores técnicos
Linda Ebenstein y Jim Johnson aseguraron que aquellas frases eran precisas.
Saul Candib actuó como editor del proyecto para esta edición, mientras que Jim Fuchs,
Mary DeJong y Michael Hochberg sirvieron como editores técnicos, revisando cuidadosa-
mente el nuevo material. Labrecque Publishing, de San Francisco, proporcionó la edición
del manuscrito, la composición de páginas, la corrección y la gestión de la producción; por
estos servicios, gracias a Chrisa Hotchkiss, Curtis Philips, Lisa Bravo, Andrea Fox y Lisa
Labrecque.
Por supuesto, la ayuda no vino solamente de las oficinas de Press y Labrecque, sino
también de los laberínticos vestíbulos del Edificio 42 del campus de Microsoft, sede del
departamento de Visual C++. Como anteriormente, Laura Hamilton actuó generosamente
como enlace, de lo cual estoy contento por no haber tenido que escribir un libro como este
sin su ayuda. Laura es una magnífica editora, y puede reclamar reconocimiento sobre
muchas de las cosas buenas tanto de este libro como de la versión en línea del Manual del
usuario de Visual C + + .
x¡¡ AGRADECIMIENTOS
Esta edición se construye con la ayuda de muchas personas del grupo de Visual C++ (y
de otras partes) que ofrecieron valiosas sugerencias y correcciones de la primera edición.
Otros revisaron el nuevo material de esta edición, representando un esfuerzo colectivo que
asegura la continua precisión y viabilidad del libro. Mi agradecimiento a (en orden alfabé-
tico) Dennis Andersen, Cathy Anderson, Chuck Bell, Diane Berkeley, Patricia Cornette,
Stacey Doerr, Chris Flaat, Jocelyn Garner, Anita George, Eric Gunnerson, Karl Hilsmann,
Mark Hopkins, Simon Koeman, Chris Koziarz, Louis Lafreniere, Martin Lovell, Michael
Maio, Bruce McKinney, Diane Melde, Daryn Robbins, Steve Ross, David Schwartz, Scott
Semyan, Terri Sharkey, George Shepherd, Kathy Shoesmith, Suzanne Sowinska, Yefim
Sigal, Chuck Sphar, Yeong-Kah Tam, Donn Trenton y Laura Wall. Algunas de estas per-
sonas son amigos que conozco desde hace años. Otros nunca los he visto cara a cara,
comunicándonos solamente a través de los milagros gemelos del correo electrónico y de
Federal Express.
Barbara Ellsworth, de Microsoft, merece una mención especial, ya que sin ella el libro
nunca se habría escrito. Gracias, Barb.
Introducción
Este libro trata de Microsoft Visual C++. No sobre el lenguaje C++ ni sobre la librería
MFC; sólo de Visual C++ en sí mismo.
Cierto, Visual C++ ya viene con un tipo de guía del programador, llamada ayuda en
línea. La amplitud del sistema de ayuda le inspirará probablemente la segura creencia de
que lo que quiere conocer está allí en algún lugar. Pero ese es el problema de la ayuda en
línea: trabaja mejor cuando conoce lo que está buscando. Este libro complementa la ayuda
en línea, pero no la sustituye. Los propósitos y estilos de los sistemas de ayuda y de la
palabra escrita son inherentemente demasiado diferentes como para que uno sustituya al
otro. Donde uno proporciona información, el otro enseña; si entiende lo que quiero decir.
Donde uno tiene amplitud, el otro tiene profundidad. Si se presentan los hechos fríos tan
bruscamente como sea posible, la ayuda en línea no se puede permitir su elaboración,
dándole en cambio una lista de pasos a seguir para completar alguna tarea, pero que rara
vez se toma tiempo para dibujar una visión amplia. Obtiene el cómo que le informa pero
no el porqué que le enseña.
Este libro pretende hacerle usuario experto de Visual C++. Se despliega en una lógica
progresión de material, que demuestra cómo interactúan las partes del todo, clarifica con
ejemplos de código, y generalmente actúa como tutor. Es más, puede sentarse con él en su
silla favorita. Estas son exactamente las ventajas que no tiene la ayuda en línea. La ayuda,
por otra parte, ofrece inmediatez y amplitud. Los muchos megabytes del texto de ayuda
pueden tratar cada oscura esquina del Visual C++, mientras que este libro cubre solamente
lo esencial.
Comience con este libro para adquirir una sólida base en el arte de Visual C++, luego
vuelva a la ayuda en línea a medida que se haga más experto y sus preguntas sean más
difíciles. Paradójicamente, cuanto más adepto sea al producto, de mayor servicio le será la
ayuda en línea.
xiv INTRODUCCI~N
El libro es más viejo que su título. La primera edición apareció como Manual del
propietario de Microsoji Visual C++, convirtiéndose en la guía oficial de Microsoft para
la versión 5 de Visual C++. Pero Microsoft ha retitulado esta segunda edición para situarla
como parte de un conjunto de cinco volúmenes de Manuales del Programador que docu-
menta las herramientas de desarrollo de Visual Studio 98, incluyendo Visual Basic, Visual
J++, Visual FoxPro y Visual InterDev. Las Guías funcionan, no obstante, independiente-
mente, por lo que si su interés se restringe a Visual C++, ha ido al lugar correcto. Cuales-
quiera similitudes que existan entre este Manual y los otros se detienen en la cubierta, ya
que los otros libros son copias impresas de la ayuda en línea, reproducciones exactas de la
documentación en línea que viene con cada producto. Encontrará este libro muy diferente
de los otros Manuales del conjunto.
Microsoft tiene buenas razones para renombrar el libro, pero me apena perder el título
original. Elijo Manual del propietario para transmitir tan claramente como sea posible el
objeto del libro para asegurar que usted, el lector, tenga una idea de lo que cubre y de lo
que no. Hace cien años, en una era mucho más tolerante con los títulos largos, podría haber
añadido algo como Es un Tutorial, Guía y Referencia dirigida a obtener mayor conoci-
miento y familiaridad con el compilador Microsoft Visual C++, sin apartarse de los inte-
resantes aunque antiguos temas del lenguaje de programación C+ + y la biblioteca Micro-
soji Foundation Class. Reconozco que ese título escolar no sería del todo preciso. Visual
C++ está tan integralmente vinculado al lenguaje C++ y a la librería MFC que es imposi-
ble hablar inteligentemente de Visual C++ y mientras permanecer callados sobre los otros
dos temas. Los capítulos que siguen presentan muchos ejemplos con fragmentos y progra-
mas, el propósito de los cuales es ilustrar algunos aspectos de Visual C++. El código debe
tener comentarios, no es útil de otro modo, y las descripciones de los programas de ejem-
plo necesariamente desbordan de términos técnicos y MFC. Pero estas apariciones son
aisladas y no nos distraen del principal objetivo sobre cómo utilizar el compilador. Hay
otros libros excelentes que explican la programación C++ y la biblioteca MFC.
Este libro describe la versión 6 de Visual C++, pero los propietarios de versiones
anteriores pueden también beneficiarse de su lectura. Algunos aspectos de Visual C++ han
cambiado considerablemente desde las versiones anteriores, pero muchas otras áreas han
cambiado poco o nada. Hoy en día, Visual C++ incluye un paquete engañoso y delgado
que contiene unos cuantos folletos, algún material impreso y un CD-ROM o dos. Pero
como ha leído hasta ahora, probablemente se haya dado cuenta de la inmensa.cantidad de
material que existe de Visual C++. Yo lo llamo «compilador» solamente p6rque no en-
cuentro un nombre mejor. Además de un compilador, Visual C++ proporcion~un enlaza-
dor, una utilidad de construcción, un depurador, un editor de textos, editores de recursos,
un entorno de desarrollo, la biblioteca Microsoft Foundation Class (MFC), bibliotecas de
tiempo de ejecución, muchos miles de líneas de código fuente y mucho más. Repetimos:
este libro no examina todas las cosas. Mi intención es ayudarle a ser un maestro de Visual
C++, no enterrarle con minucias.
LO QUE YA DEBER~ASABER
Un libro de esta clase tiene que comenzar en la curva de aprendizaje en algún lugar por
encima del punto cero. Comience demasiado bajo, y las discusiones se harán desesperada-
mente desordenadas con explicaciones preliminares. Comience demasiado alto, y el autor
perderá a mucha de su audiencia (además de parecer un tonto). El truco está en hablar al
lector con una amplia variedad de habilidades e intereses, aunque sin perder nadie como
cuando se habla de esoterismo e insultos ni cuando se presentan los fundamentos. El libro
no hace grandes demandas. Supongo que ya está familiarizado con los lenguajes de pro-
gramación C y C++, que ha programado antes en Windows, y tiene al menos unos conoci-
mientos mínimos de MFC. No tiene necesidad de ser un experto, pero encontrará el código
y texto de los ejemplos más fácil de seguir si comprende las ideas básicas, como punteros,
clases y mensajes. Afortunadamente, no hay nada abstracto sobre el compilador. Es sola-
mente software.
productos no eran muy buenos (el editor de QuickC fue posteriormente incorporado en el
Microsoft QuickBasic y todavía existe hoy en Microsoft Windows 95 como el editor de
DOS Edit.com). Otro problema con los IDE bajo DOS era que consumían mucha memo-
ria, dejando poca para ejecutar el programa que se estaba desarrollando. A menudo se
tenía que salir del IDE para ejecutar y depurar su programa. Muchos programadores que
utilizaban QuickC en el trabajo de desarrollo (incluyéndome a mí mismo) confiaban sola-
mente en la versión de línea de órdenes.
Pero entonces llegó Windows 3.0.
Windows 3.0 y especialmente 3.1 nos introdujeron en el área de los IDE serios para
ordenadores personales. Las restricciones de memoria desaparecieron. Y si iba a progra-
mar en Windows, un entorno Windows parecía el lugar natural para hacerlo. Estaba claro
que programar para Windows en Windows produce mejores productos. Windows es algo
pensado con la mente, y trabajar en este entorno todo el día proporciona un mejor instinto
sobre lo que un programa debería hacer o no.
Para sorpresa de muchos, Microsoft concentró sus esfuerzos en apuntalar lo interno de
su compilador C, en vez de actualizar su interfaz para la nueva era. Cuando salió la ver-
sión 7, era todavía un producto basado en DOS que se ejecutaba tanto en una caja DOS de
Windows o con un gestor de memoria extendida (venía con el 386Max de Qualitas inclui-
do en la caja). Como concesión, la versión 7 ofrecía un IDE en modo carácter llamado
Entorno de trabajo del Programador que era incómodo para los estándares de hoy. Sin
embargo, el Entorno de trabajo demostraba una evolución natural desde los días del
QuickC. Muchas órdenes de sus menús parecen aún modernas, como Nuevo, Abrir, Guar-
dar como, Construir y Abrir proyecto.
La contribución importante que hizo la versión 7 al mundo de la programación no era
su IDE, sino el soporte para C++. Por primera vez, Microsoft diseñó su compilador
«C/C++» para enfatizar su nueva naturaleza dual. Era como ver a una célula sufrir mitosis.
El soporte afectaba a algo más que una simple expansión del compilador para que recono-
ciera nuevas órdenes del superconjunto C++. La versión 7 de C/C++ introducía además la
versión 1 de la biblioteca Microsoft Foundation Class, junto con código fuente. C++ no
sería un vehículo tan popular de la programación Windows de hoy sin este competente
conjunto de clases prescritas, que Microsoft sabiamente proporcionó a los desarrolladores.
Con la siguiente versión definitiva, Microsoft abandonó la mayoría de sus productos
orientados a DOS. Microsoft C/C++ versión 8, que lucía un IDE Windows real, se conocía
como Visual C++ versión l. El nombre centró el éxito del primer Visual Basic, pero los
dos productos nunca fueron demasiado buenos. Donde Visual Basic permite al desarrolla-
dor construir un programa de trabajo de Windows con muchos clics y poco código, Visual
C++ crea solamente archivos fuente iniciales mediante bibliotecas especiales de enlace
dinámico llamadas asistentes. Como veremos en el Capítulo 2, los asistentes ahorran bas-
tante trabajo repetitivo de la presentación de trabajo de desarrollo, la clase de trabajo
común a muchos programas Windows escritos con MFC.
Después de Visual C++ 1.5, Microsoft decidió no invertir ningún esfuerzo más en el
soporte de la programación de 16 bits. Visual C++ 2 todavía ofrecía soporte de 16 bits, pero
desde entonces Visual C++ crea solamente aplicaciones de 32 bits. Nunca hubo Visual
C++ 3. El número de las versiones saltó desde la 2 a la 4 para sincronizar Visual C++ y
MFC, creando así una pequeña fuente de confusión. La consolidación tuvo una corta vida,
no obstante, ya que Visual C++ y MFC utilizaron de nuevo diferentes números de versiones.
La popularidad de Internet ha influido claramente en el diseño de productos, y en su
cuarta versión Visual C++ introdujo nuevas clases de bibliotecas diseñadas para la progra-
mación en Internet. La versión 5 añadió además algunas nuevas clases, pero se concentró
más en mejorar la interfaz del producto para proporcionar un sistema mejor de ayuda en
línea, mejores posibilidades de macros y soporte para compartir clases y otro código den-
tro de un equipo de desarrolladores. La versión 5 integraba la Active Template Library
(Biblioteca activa de plantillas) y mejoraba significativamente la capacidad del compila-
dor para optimizar código. Como veremos en posteriores capítulos, la versión 6 amplía
estas mejoras bastante más.
El libro se divide en seis partes principales, cada una cubre un tema general de Visual C++
y su entorno de desarrollo. Las discusiones son intencionadamente básicas hasta el Capítu-
lo 3, que cubre el editor de textos. Esto nos ayuda a garantizar que cualquier lector, sea
iniciado o experto, es capaz de navegar con éxito por el entorno de desarrollo de Visual
C++ y escribir código fuente con el editor de textos. Al comenzar el Capítulo 4, las discu-
siones se hacen gradualmente más técnicas.
Parte l. Fundamentos
Visual C++ proporciona tres editores diferentes, uno para crear el texto del código fuente,
otro para los menús y archivos gráficos y el tercero para los cuadros de diálogo. Cada
editor tiene su propio capítulo, comenzando por el editor de textos en el Capítulo 3. Este
capítulo examina las órdenes del menú importantes, muestra las teclas rápidas para abrir
documentos de texto e introduce las macros.
El Capítulo 4 describe el editor de gráficos multitalento de Visual C++, utilizado para
crear recursos que incluyen menús, mapas de bits, iconos y barras de herramientas. Este
capítulo es extenso, como consecuencia de la cantidad de material que necesita cubrir. Un
programa de ejemplo denominado DiskPiel toma forma a medida que progresa el capítu-
lo. Cada sección principal describe primero cómo utilizar el editor de gráficos para crear
un elemento particular de la interfaz, tal como un menú o una barra de herramientas, y
luego lo demuestra agregando el elemento al programa DiskPiel. Al final del capítulo el
programa es una utilidad práctica que visualiza el uso del disco y la memoria en forma de
un diagrama de pastel.
El Capítulo 5 cubre el editor de diálogos y muestra cómo utilizar Visual C++ para
diseñar cuadros de diálogo y crear aplicaciones basadas en cuadros de diálogo, como el
Mapa de caracteres de Windows y las utilidades de Marcador de teléfonos. El capítulo
muestra varios ejemplos, incluyendo uno que crea una hoja de propiedades, también cono-
cida como diálogo con pestañas.
CÓDIGO DE EJEMPLO
Casi todos los ejemplos de este libro están escritos en C++ y utilizan MFC (las dos excep-
ciones son un programa demostración del cursor en el Capítulo 4 y una pequeña utilidad
basada en la consola que se presenta en el Capítulo 13). Pero me baso en C en algunos de
los fragmentos de código dentro del texto. No encuentro a C++ tan bueno como C como
medio para ilustrar sucintamente una idea de programación, y además de las ventajas de la
claridad y la brevedad, C sirve como un tipo de lengua franca entre los programadores de
hoy. En teoría, los programadores de C++ comprenden el C a solas, pero lo inverso no es
necesariamente cierto. Por otra parte, C no tiene forma de mostrar las aplicaciones MFC.
Algunas veces presento código C y C++ equivalente cuando creo que la idea es suficien-
temente importante y las diferencias significativas para garantizar la traducción.
Muchos de los capítulos del libro cubren temas que se muestran mejor con ejemplos, y
he intentado incluir programas de ejemplo que sean a la vez interesantes, útiles e ilustrati-
vos. Algunos de los programas se crean con el AppWizard y otros no, simulando así tan
amplio abanico de prácticas de programación como sea posible. Casi todos los programas
van acompañados de una discusión en el texto del libro. El texto incluye además el listado
del código fuente, por lo que no necesita abrir un archivo en el editor para seguir la
discusión. El código de los programas opta por la claridad sobre la elegancia, por lo que no
dude que verá secciones de código que usted gestionaría de forma diferente en sus propios
desarrollos. Por ejemplo, he incluido muy poca comprobación de errores en los programas.
Los programas fueron creados en Windows 95, pero muchos han sido probados bajo Mi-
crosoft Windows NT.
EL CD QUE SE ACOMPAÑA
Los archivos del proyecto de todos los programas de ejemplo están en el CD que se adjun-
ta en la cubierta final del libro. Para copiar todos los proyectos en su disco duro, ejecute el
programa Instalar siguiendo estos pasos:
1. Haga clic en el botón de Inicio de la barra de tareas de Windows y seleccione la
orden Ejecutar.
2. Teclee «d:\instalar» en el diálogo de Ejecutar, donde d representa la letra de la
unidad de su CD-ROM.
El programa Instalar copia más de 3 Mb de archivos desde el CD en su disco duro,
ubicándolos en una subcarpeta denominada Manual del programador de Visual C++ (o
cualquier nombre que especifique). La ejecución de Instalar es totalmente opcional, y
puede recuperar los archivos manualmente del CD si lo prefiere. Encontrará todos los
ficheros situados en la subcarpeta Codigo.
Las subcarpetas anidadas se refieren al número de capítulo donde se describe el pro-
grama y el nombre del proyecto. La subcarpeta Capitulo.O5\MfcTree, por ejemplo, contie-
ne todos los archivos necesarios para construir el programa MfcTree presentado en el
Capítulo 5. Cada carpeta de proyecto tiene una subcarpeta que contiene el archivo ejecuta-
ble del programa, por lo que puede ejecutar un programa ejemplo sin tener que construirlo.
Si quiere seguir la discusión en el texto construyendo el ejemplo, inicie Visual C++ y elija
la orden Open Workspace del menú File. Explore la carpeta del proyecto de su disco duro
y haga doble clic sobre el archivo DSW del proyecto.
Los nombres de proyecto de los programas de ejemplo ocupan ocho caracteres o me-
nos. Este convenio se acomoda a aquellos lectores que prefieren utilizar un editor de textos
antiguo que pueda no reconocer nombres de archivo largos. Algunas unidades de CD
antiguas tienen también problemas con los nombres de archivo largos.
El CD que se acompaña incluye un programa que escribí llamado Index*. Index no es
un programa de ejemplo, por lo que no lo encontrará en ningún lugar de los capítulos del
libro. Index proporciona el índice del libro, realizando una búsqueda del texto completo a
través de todos los capítulos y de los apéndices. Le asegura que si se menciona un tema en
* Nota del Editor: El programa Index hace referencia a la versión inglesa del libro, reconociendo tan sólo
texto en inglés. La correspondencia de páginas con la versión española del libro es tan sólo aproximada. El
programa Index no se ha adaptado a la versión española por ser técnicamente inviable dicha adaptación.
cualquier parte del libro, puede encontrarla. El programa es realmente un formulario elec-
trónico de lo que los bibliógrafos llaman concordancia, dada una o más palabras, le dice en
qué páginas y en qué párrafos aparecen las palabras. Para utilizar el programa, copie los
archivos Index.exe, 1ndex.hlp e Index.key desde el CD a su disco duro, asegurándose de
que sitúa los tres archivos en la misma carpeta. O puede ejecutar Index directamente desde
el CD si lo prefiere. Aquí tiene cómo aparece el programa:
Las cuatro cajas de combinación de la ventana de diálogo de Index aceptan una sola
palabra. Las palabras pueden formar una frase tal como «Active Template Library», o
simplemente especifican palabras no conectadas que aparecen juntas en el mismo párrafo
o en la misma página. El programa también busca plurales y variaciones de la palabra
formadas por -ed e -ing, y es suficientemente inteligente para contabilizar ligeros cambios
en el deletreo. Si se buscan las palabras edit, handle y debugg, por ejemplo, también
localiza apariciones de las palabras edits, handling y debugged. No distingue entre mayús-
culas y minúsculas, una simplificación que en raras ocasiones puede generar coincidencias
inesperadas, como cuando Index localiza la palabra guiding cuando se busca el acrónimo
GUID. Para ejecutar una búsqueda, haga clic en el botón Search o bien en el icono del
libro.
Los cuatro cuadros de combinación recuerdan búsquedas anteriores de palabras, por lo
que no tiene que volver a teclear una entrada. Para volver a llamar una palabra que introdu-
jo anteriormente, despliegue el cuadro de lista y seleccione la palabra. Los mensajes de
ayuda desplegables explican otras características del programa. Simplemente haga clic en
el botón con el pequeño signo de interrogación de la esquina superior derecha del diálogo
y luego haga clic en la ventana de control o en el área de un grupo. Los usuarios de
Windows NT 3.51 o superior deben presionar la tecla FI para obtener la ayuda.
Index identifica cada párrafo de cada página por un número, tal como 2 o 7. Así puede
recorrer una página para encontrar un párrafo particular indicado por el programa, recor-
dando estas reglas que determinan lo que el programa considera lo que es un párrafo:
El título de una figura constituye un párrafo separado, como ocurre con cada fila de
una tabla.
xxii INTRODUCCI~N
Cada línea de código fuente (exceptuando las líneas en blanco) representa un párrafo.
Un párrafo parcial al principio de la página no cuenta como un párrafo separado,
porque Index supone que el texto pertenece al párrafo del final de la página prece-
dente.
El programa Index reconoce los operadores booleanos AND, OR y NOT. Si tiene un
poco oxidada la lógica booleana de las búsquedas de texto completo, el Capítulo 1 descri-
be cómo utilizar los mismos operadores cuando se busca en el sistema de ayuda en línea de
MSDN. Consulte la Tabla 1.1 (pág. 22) para ver los ejemplos.
Retroalimentación
Si tiene cualquier sugerencia sobre futuras ediciones de este libro, escníame una línea.
Intentaré leer cada parte de los correos electrónicos que reciba (soy bastante diligente en
estas cosas), aunque no puedo prometer una respuesta. Puede llegar a mí a través de Inter-
net en beckz@witzendsoj?.com.
El entorno
El paquete de Visual C++ incluye muchas piezas separadas, como por ejemplo editores,
compiladores, enlazadores, utilidad make, un depurador y otras herramientas diseñadas
para la tarea de desarrollar los programas C/C++ para Microsoft Windows. Afortunada-
mente, el paquete también incluye un entorno de desarrollo llamado Developer Studio.
Developer Studio reúne todas las herramientas de Visual C++ en un todo integrado, permi-
tiéndole visualizar y controlar el proceso de desarrollo completo a través de un sistema
consistente de ventanas, diálogos, menús, barras de herramientas, teclas de atajo y macros.
Para utilizar una analogía, el entorno es como una habitación de control con monitores,
marcadores y palancas desde donde una sola persona puede operar con las máquinas de
una fábrica en crecimiento. El entorno es prácticamente todo lo que ve en Visual C++.
Todo lo demás corre entre bastidores bajo su administración.
La distinción entre el producto Visual C++ y su entorno Developer Studio sirve de
poco, porque el último representa completamente al primero. En vez de tratar con otro
nombre más, este libro aplica el término Visual C++ en un sentido general que se refiere
de forma intercambiable tanto al producto entero como a su entorno de desarrollo. Micro-
soft sí que ha adoptado este curso, y los usuarios de versiones anteriores notarán que las
ventanas etiquetadas anteriormente Developer Studio se han vuelto a titular Visual C++.
Haremos resucitar el nombre antiguo en el último capítulo, sin embargo, cuando discuta-
mos cómo pueden integrarse los programas de utilidad con el programa de Developer
Studio para convertirse en parte del entorno.
Vamos a empezar este capítulo con un resumen de algunos de los muchos servicios
que proporciona el entorno Visual C++ y que están diseñados para ayudarle en el desarro-
llo de programas. Los números de capítulo entre paréntesis indican en qué parte del libro
examinaremos estos servicios con detalle:
4 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Cube cpp
Cube rc
Cubedoc cpp
Cubeview cpl
Mainfim cpp
Stdafu cpp
ader Files
source Files
Compiling resources
Compi ling
Stdafx con
VENTANAS DE ENTORNO
Además de sus muchos cuadros de diálogos, Visual C++ viualiza dos tipos de ventanas,
llamadas ventanas de documento y ventanas acoplables. Las ventanas de documento son
ventanas hijas enmarcadas que contienen texto de código de fuente y documentos de gráfi-
co. El menú Window enumera las órdenes que visualizan las ventanas de documento en la
pantalla en una administración en cascada o repetida. Todas las demás ventanas de Visual
C++, incluyendo las barras de herramientas e incluso la barra de menú, son acoplables. El
entorno tiene dos ventanas principales acoplables, llamadas Workspace y Output, que se
hacen visibles por medio de las órdenes en el menú View. Otras ventanas acoplables,
descritas en el Capítulo 11, «El depurador», aparecen durante una sesión de depuración.
Esta sección primero mira algunas de las características comunes a todas las ventanas
acoplables y a continuación examina las ventanas Workspace y Output individualmente.
Una ventana acoplable se puede adjuntar a la parte superior, inferior o a los bordes
laterales del área de cliente de Visual C++, o se puede desconectar para que flote libre-
mente en cualquier parte de la pantalla. Las ventanas acoplables, tanto si flotan como si se
acoplan, siempre aparecen en la parte superior de las ventanas de documento. Esto le
asegura que las barras de herramientas flotantes permanecen visibles a medida que cambia
el centro de atención de una ventana a otra, pero también significa que las ventanas del
documento pueden perderse ocasionalmente. Esto puede ser desconcertante las primeras
veces que ocurra, pero tenga fe en que la ventana de documento todavía está ahí. Si está
trabajando con código de fuente en el editor de texto, por ejemplo, y a continuación activa
una ventana acoplable que ocupa el área de cliente de Visual C++ en su totalidad, el
documento de código fuente puede desaparecer, enterrado bajo la ventana nueva. Si la
ventana superpuesta se acopla en la posición, no puede traer la ventana de documento
fuente de vuelta a la parte superior. La única solución es desactivar la ventana superpuesta
o arrastrarla fuera del camino. Veremos cómo activar y desactivar las ventanas acoplables
en un momento.
A medida que arrastre una ventana acoplable, aparece un esquema que se desplaza y
que muestra que la nueva ubicación de la ventana estará donde suelte el botón izquierdo
del ratón. El esquema es una confusa línea gris hasta que se pone en contacto con un borde
del área de cliente de entorno o el borde de otra ventana acoplada, en cuyo punto el
esquema cambia a una fina línea blanca. El cambio es una clave visual para notificar que
el arrastre de la ventana hará que se acople en su lugar junto al borde más cercano. Una
barra de herramientas se acopla en una posición horizontal diferente junto al borde supe-
rior o inferior del área de cliente y en una posición vertical cuando se coloca junto al lado
izquierdo o derecho. Puede volver a orientar la ubicación de la barra de herramientas
presionando la tecla MAYÚS mientras que arrastra la barra de herramientas.
Hacer que una ventana se acople en el tamaño y posición deseados, algunas veces lleva
varios intentos. Para acoplar una ventana de modo que ocupe el área de cliente entera,
arrástrela hacia arriba hasta que el cursor del ratón entre en contacto con el borde superior
del área de cliente, y a continuación suelte el botón del ratón. Para lograr que la ventana
acoplada vuelva al tamaño más pequeño, arrastre la ventana hasta que el cursor toque el
borde izquierdo del área de cliente. Esto fuerza a la ventana a abrirse, permitiéndole arras-
trar la ventana por su barra de título a una ubicación diferente.
Cuando arrastra una ventana acoplable alrededor de la pantalla, la ventana puede pare-
cer que tenga inteligencia propia, aferrándose tenazmente a uno de los bordes de la venta-
na principal de Visual C++ o a cualquier otra ventana acoplada con la que entre en contac-
to. Puede impedir esto de dos formas: el primer método es presionar la tecla CTRL mientras
que desplaza la ventana para suprimir temporalmente su rasgo de acoplamiento; el segundo
método funciona sólo para ventanas, no para barra de herramientas, deshabilitando la posibi-
lidad de acoplamiento de la ventana hasta que la habilite otra vez. Haga clic a la derecha
dentro de la ventana y elija el comando Docking View desde el menú contexhial de la
ventana para desactivar el icono de cuadro de comprobación del comando. El menú Window
también proporciona acceso al comando Docking View, como se muestra en la Figura 1.3.
La inhabilitación de un rasgo de acoplamiento de ventana afecta el comportamiento de
la ventana de varios modos:
ELENTORNO 9
La ventana aparece como una ventana de documento normal, con botones en la barra
de título que minimizan, maximizan y cierran la ventana.
La posición de la ventana se administra junto con cualquier ventana de documento
abierta cuando elige el comando Cascade o Tile desde el menú Window.
La ventana no se puede desplazar por encima del área de cliente de la ventana princi-
pal de Visual C++ como lo puede hacer cuando está en modo de acoplamiento.
Dado el centro de entrada, la ventana puede cerrarse con el comando Close en el
menú Window. De lo contrario, el comando Close no afecta a una ventana en modo
de acoplamiento, incluso si se le ha prestado atención.
Cuando se acopla una ventana o barra de herramientas, aparecen en la parte superior o
en el borde izquierdo de la ventana cordoncillos elevados fácilmente distinguibles, llama-
dos a veces barras de agarre, como se muestra en la Figura 1.4. Al hacer dos veces clic en
las barras de agarre hace que una ventana o barra de herramientas flote libremente; hacien-
do dos veces clic en la barra de título de la ventana o barra de herramientas flotante hace
que vuelva a su posición acoplada anterior. También puede arrastrar una ventana por sus
barras de agarre a otra ubicación acoplada o que flote libremente.
La configuración de ventanas que crea en Visual C++ dura tanto como el proyecto o
hasta que usted la cambie. La próxima vez que abra el proyecto, las ventanas aparecen
como las dejó. Sin embargo, las ventanas que pertenecen a programas de utilidad ejecuta-
dos dentro del entorno no están sujetas a las reglas del entorno. Dichas ventanas no son
Barras de agarre
EC CCubeApp
IíI CCubeDoc
m.ilCCubeView
@ CMainFrarne
Fl 0Globals
Figura 1.4. Cuando están acopladas, las ventanas, barras de herramientas y barras
de menú tienen barras de agarre realzadas.
10 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
1 CDernoApp
CD~~U~DDI~ ! 1 ' 3 Dialog
@ IDDABOUTBOX 1
O O~~pAboutll - .-g Menu
: + ':CDemoDoc m IDR-MAINFRAME
+ 5 CDernoView + J Slrrng Table
+ * CMainFrarne - r d Toalbar
- Globals SS IDR-MAINFRAME
@ theApp +t Version
1 - Demo bler #I
A Source Fies
( 3 Demo cpp
3
Derno c
3
DernoDoc cpp
3 DernoView cpp
3 MamFirn cpp
3 Std4fu cpp
4 J Header Files
+ J Resource Files
i Data View. Visualiza información sobre las fuentes de datos para proyectos de bases
de datos. La pestaña Data View sólo aparece en los proyectos de base de datos hospe-
dados por la Edición Empresarial de Visual C++ que están conectados a la fuente de
datos y que cumple el estándar de Conectividad de bases de datos abiertas (ODBC).
Al hacer clic en el botón de la derecha del ratón en la ventana Workspace visualiza un
menú contextual que contiene comandos utilizados a menudo. Los comandos del menú
dependen del elemento sobre el que se ha hecho clic. Al hacer clic en el archivo fuente del
panel File View, por ejemplo, visualiza un menú contextual que le permite abrir o compilar
rápidamente el archivo. También puede activar y desactivar los paneles individuales de
Workspace. Haga clic en el botón de la derecha de cualquier pestaña en la parte inferior de la
ventana Workspace para visualizar un menú contextual y elija a continuación el comando
deseado de la lista de menú para hacer el panel visible o invisible.
La ventana Output (que se muestra en la Figura 1.7) tiene cuatro pestañas, llamadas
Build, Debug, Find In Files 1 y Find In Files 2. La pestaña Build visualiza los mensajes de
estado desde el compilador, enlazador y otras herramientas. La pestaña Debug se reserva
para notificaciones desde el depurador, advirtiéndole de ciertas condiciones, como por ejem-
plo las excepciones inmanejables y las violaciones de memoria. Cualquier mensaje que
genere su aplicación a través de la función API OutputDebugString o la biblioteca afxDump
también aparece en la pestaña Debug.
Las dos pestañas restantes de la ventana Output visualizan los resultados de la orden
Find In Files elegida desde el menú Edit (esta útil característica, parecida al comando grep
de UNIX, se examina con más detalle en el Capítulo 3, «El editor de texto»). Por defecto, los
resultados de la búsqueda Find In Files aparecen en la pestaña Find In Files 1 de la ventana
Output, pero un cuadro de comprobación en el diálogo Find In Files le permite desviar la
salida a la pestaña Find In Files 2. La ventana Output también puede contener otras pestañas.
Veremos en el Capítulo 13 cómo añadir una herramienta personalizada para que Visual C++
pueda visualizar los mensajes en su propia pestaña de la ventana Output.
AYUDA EN LINEA
Visual C++ proporciona tres fuentes diferentes de ayuda en línea:
Archivos HLP estándar visualizados con el visualizador WinHlp32.
Mensajes de ayuda emergentes en diálogos.
La Microsoft Developer Network Library, conocida como MSDN.
Compiling
Cube cpp
Cube ob] - O error(s) O warning(=)
El efecto de presionar FI en este caso depende de la posición del signo caret que parpa-
dea en la ventana del editor de texto. Si el signo caret está dentro o al principio del nombre
de la macro y la extensión de ayuda está desactivada, al presionar FI abre la ventana MSDN
Library y visualiza la información sobre la macro DECLARE-MESSAGE-MAP. Si, por el
contrario, el signo caret está en una línea en blanco, no hay contexto claro para la ayuda en
línea. En este caso, al pulsar la tecla FI produce información sobre la ventana del editor de
texto, visualizada en el visualizador WinHlp32:
The Text Editor window displays text files of any type, such as
language source and header hles
La tercera fuente para ayuda en línea es la que probablemente utilizará más a menudo
mientras trabaje en Visual C++. MSDN es generalmente lógica y fácil de utilizar, mas es
también inmensa. Como veremos en la sección siguiente, al utilizar la biblioteca MSDN
en todo su potencial, requiere algo de práctica.
BIBLIOTECA MSDN
Anteriormente disponible sólo por suscripción, la MSDN Library sirve ahora como siste-
ma de ayuda en línea para todo el equipo de herramientas de desarrollo de Visual Studio,
incluyendo Visual C++. Como se comparte igual en todas las herramientas, MSDN corre
como una aplicación separada y no está estrechamente integrada en un solo entorno de
desarrollo. Para acceder a MSDN desde dentro de Visual C++, no debe estar seleccionado
el comando Use Extension Help del menú Help. Al elegir los comandos Contents, Search
o Index del menú Help de Visual C++ hace que el entorno corra MSDN ejecutando el
programa Windows\HH.exe, que carga la tabla MSDN de contenidos desde el archivo
MSDNVS98.co1, ubicado en la carpeta MSDN98\98VS\1033 (el nombre de la carpeta que
contiene refleja los ajustes de localización de sistema; 1033 es un código de lenguaje para
el inglés de Estados Unidos).
La Biblioteca proporciona un inmenso tesoro de información, tocando casi cada faceta
de las herramientas de programación de Microsoft y programación de Win32. Comprime
miles de artículos, cubriendo todo, desde Visual C++ a Visual J++, desde MCF a ActiveX,
y desde la función abs a la z-ordenación. MSDN también incluye el texto completo de
varios libros respetados publicados por Microsoft, como por ejemplo Hardcore Visual
Basic, de Bruce McKinney, e Inside Ole, 2nd Edition, de Kraig Brockschmidt. También
puede encontrar los artículos de la Knowledge Base, asuntos recientes de Microsoft System
Journal, documentación completa de aplicaciones y kits de desarrollo de controlador de
dispositivo, textos de conferencias, código de fuente de ejemplo y mucho más. La interfaz
no está perfectamente concebida para esta versión, pero todo el volumen de información
de MSDN es verdaderamente asombroso.
El sistema de ayuda MSDN almacena su texto en una serie de archivos «amigos»,
reconocibles por su extensión CHM; la extensión se refiere al formato de HMTL compila-
do en el que están escritos los archivos. Los archivos CHM son volúmenes individuales de
la enciclopedia MSDN, cada uno contiene artículos dedicados a un tema particular, como
por ejemplo ActiveX o la referencia API completa de Win32. Cada archivo está empareja-
do con un archivo de índice separado que tiene el mismo nombre y una extensión CHI.
Durante la instalación de MSDN, la instalación de programa escribe todos los archivos de
índice CHI a su disco duro pero copia del CD-ROM sólo aquellos archivos CHM que
solicite de forma explícita. Los archivos CHM toman una lista de todo el espacio de disco,
de modo que probablemente preferirá instalar sólo aquellos temas que con probabilidad
visitará más a menudo. Durante la ejecución de MSDN tiene acceso a todos los archivos
CHM, tanto si están en su disco duro como si se han quedado en los CD. Si el programa no
puede localizar un archivo CHM solicitado en su disco duro, le pide que reemplace el CD
correcto. Los temas de MSDN que debería entonces instalar en su sistema depende así de
la frecuencia con la que prevé utilizar MSDN, los temas que le interesan más y las ganas
que tiene de intercambiar CD.
La primera vez que invoca la MSDN Library para buscar un artículo, crea el archivo de
palabra clave MSDNVS98.chw, que contiene una lista de palabras individuales utilizadas
en todos los artículos junto con los punteros de dónde debe aparecer cada palabra en el
texto. Compilar referencias de palabras clave de este modo acelera las búsquedas de pala-
bras y frases particulares, como veremos en un momento. La creación de un archivo de
palabras clave es cosa de una sola vez que puede tardar varios minutos, durante lo cual el
mensaje animado le informa de lo que está sucediendo:
Ct+DacmcrddnnMap (803
Getting Started with Vis
Pestaña Contenido
MSDN agrupa los temas según el tema del asunto bajo cabeceras y subcabeceras, una
administración que forma una tabla de contenidos. Es como la tabla de contenidos de un
libro, sólo que interactiva. Empieza buscando un tema general, a continuación explora los
caminos de información que empiezan a ser cada vez más específicos para encontrar obje-
tos que le interesen. La tabla de contenidos da mejor servicio cuando tiene en la cabeza
una idea general, el depurador por ejemplo, o programación con OpenGL, y desea ver qué
documentos están disponibles para ese tema.
Si elige la orden Contents del menú Help de Visual C++, abre la ventana MSDN y
visualiza la tabla de contenidos. Expanda la tabla hasta que encuentre el título del artículo
que está buscando, haciendo dos veces clic en las cabeceras (identificadas por iconos de
libros) o haciendo clic en los botones del signo más pequeño (+) que se muestran en la
Figura 1.9. Los títulos de artículos en la tabla de contenidos yacen al final de una cadena
jerárquica, cada uno distinguido por un icono que representa una lámina de papel con un
ángulo en forma de oreja de perro. Haciendo dos veces clic en un título en la lista abre el
artículo en el panel de la derecha de MSDN.
Por defecto, la tabla de contenidos resume toda la colección de artículos de MSDN.
Puede estrechar la visualización definiendo una rama de la tabla de la jerarquía de conteni-
dos como un subconjunto de información. Los subconjuntos le permiten centrarse en te-
mas de una categoría particular. Como ejemplo, aquí se muestra cómo crear un subconjun-
to de artículos que pertenezcan sólo a la MFC Reference:
1. Elija el comando Definir subconjunto del menú Ver de MSDN.
2. En el diálogo Definir subconjunto, expanda la tabla de contenidos haciendo dos
veces clic en la cabecera multivolumen etiquetada «MSDN Library Visual Studio
6.0», y a continuación haga lo mismo con las subcabeceras anidadas «Visual C++
Documentation» y «Referente». Seleccione la subcabecera llamada «Microsoft
Foundation Class Library and Templates» y haga clic en el botón Agregar para
crear el subconjunto.
3. Teclee un nombre para el nuevo subconjunto en el cuadro de edición en la parte
superior del diálogo Definir subconjuntos, y a continuación haga clic en los boto-
nes Guardar y Cerrar.
Para cambiar de subconjuntos cuando esté utilizando la ayuda interactiva, seleccione
un subconjunto de la lista desplegable etiquetada Subconjunto activo:
18 MICROSOFT VISUAL C++ 6.0. M A N U A L DEL PROGRAMADOR
m e - , I
;IMFC
Reference
djl
Pestaña índice
La pestaña Índice está normalmente donde debería dirigirse primero para buscar ayuda en
línea, en particular cuando tiene una idea razonablemente clara del tema en el que está
buscando. La pestaña Índice visualiza un índice exhaustivo de todo el conjunto de archivo
de MSDN, muy parecido al índice de un libro impreso. Para localizar una entrada de
índice, teclee una palabra clave en el cuadro de edición en la parte superior del diálogo.
A medida que teclea, el índice en el cuadro de la lista se desplaza automáticamente hacia
la contraseña tecleada. Por ejemplo, el índice MSDN incluye las entradas «exception ha-
ndling», «handling exceptions» y «C++ exception handling», así que tecleando cualquiera
de estos términos localiza los temas que pertenecen al tema de manejo de excepción.
Cuando encuentra la entrada del índice que desea, haga dos veces clic. Si la entrada sólo
tiene como destino un solo artículo, MSDN lo visualiza inmediatamente; de lo contrario
aparece el diálogo Temas encontrados que enumera todos los artículos a los que se refiere
la entrada de índice, como se muestra en la Figura 1.1 1. Abra un artículo en el diálogo
haciendo dos veces clic en el título de la lista o seleccionando el título y haciendo clic en el
botón Visualizar.
Pestaña Búsqueda
MSDN es más que un conjunto pasivo de archivos de ayuda. También incluye un motor de
búsqueda que escanea el archivo de palabra clave MSDNVS98.chw para determinar qué
archivos de tema contienen una palabra o frase específica, un proceso llamado búsqueda de
texto entero. Las búsquedas de texto entero se lanzan desde la pestaña de Búsqueda de
MSDN (Fig. 1.12), permitiéndole buscar los temas que contienen una palabra o frase especí-
fica. El motor de búsqueda MSDN es inteligente, capaz de entender variaciones de palabras,
comodines, asociaciones booleanas y el operador de proximidad NEAR. Aunque el uso
eficaz de estas características requiere más cálculos y programación de su parte, le permiten
refinar tales parámetros para aumentar las probabilidades de encontrar sólo los temas que
más le interesan. Después de examinar las distintas opciones disponibles desde la pestaña
Búsqueda, nos centraremos en cómo refinar una búsqueda utilizando comodines y operadores.
En la parte superior de la pestaña, teclee la palabra o frase que desee buscar, encerran-
do las frases entre comillas dobles para distinguirlas de las palabras individuales (las co-
millas simples se ignoran). Por ejemplo, al buscar las palabras visualizadas en la Figu-
ra 1.12 encuentra sólo temas que contienen la frase «exception handling*. Tecleando las
EL ENTORNO 19
mismas palabras sin las comillas significa que desea buscar temas que contengan las pala-
bras «exception» y «handling», pero no necesariamente coincidiendo juntas como frase.
La búsqueda de comillas no es posible.
Tres cuadros de comprobación en la pestaña Búsqueda gobiernan los cambios, a través
de los cuales puede especificar además cómo y dónde buscar. El cuadro de comprobación
Buscar en anteriores le permite limitar las búsquedas sólo para los artículos ya enumerados
en la pestaña Búsqueda. Al activar el cuadro de diálogo Buscar palabras similares le indica
Figura 1.12. La pestaña Búsqueda permite buscar temas que contienen palabras
o temas específicos.
20 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
a MSDN que acepte palabras que son variaciones gramaticales de la palabra de búsqueda
(o palabras) que ha tecleado en el primer cuadro de texto. Las variaciones implican sufijos
de palabras comunes, tales como S, ed e ing, obligando a MSDN a reconocer las palabras
edits y edited, por ejemplo, como correspondiente a la palabra clave edit. Si se amplían los
criterios de búsqueda de este modo, es por supuesto posible encontrar más temas. El cua-
dro de Buscar palabras similares se aplica a todas las palabras de búsqueda tecleadas en el
cuadro de edición, de modo que la búsqueda de la frase handle exception con el cuadro
activado también encuentra temas que contienen variaciones de cierre, como por ejemplo
handled exceptions. MSDN reconoce incluso esas variaciones que no contienen la palabra
clave completa, encuentra palabras como handler y handled cuando busca la palabra clave
handling.
Al activar el cuadro de comprobación Buscar sólo títulos reduce considerablemente la
búsqueda, porque hace que MSDN busque sólo los títulos de artículos, no el cuerpo del
texto dentro de los artículos. De este modo, la búsqueda de la frase exception handling con
el cuadro de comprobación activado encuentra títulos tales como «Exception Handling
Topics (SEH)» y «Type-Safe Exception Handling», pero no otros temas relacionados tales
como «Compiler Warning C4530», que menciona el manejo de excepción dentro de su
texto.
Cuando se completa la búsqueda, MSDN enumera los títulos de todos los artículos que
mencionan la cadena de búsqueda y visualiza el número de artículos localizados en el
ángulo superior derecho de la lista. La lista se clasifica en rango descendente, determinada
por el número de veces que la cadena de búsqueda solicitada aparece en el documento del
tema. Para clasificar la lista por título o ubicación de artículo, haga clic en el botón en la
parte superior de la columna de la lista apropiada. A1 hacer dos veces clic en una entrada
de lista en la pestaña Búsqueda visualiza el artículo con todas las cadenas parejas que
concuerdan con el texto, permitiéndole localizar rápidamente cada aparición de una cade-
na. Las cadenas realzadas se repiten a menudo en el texto y puede parecer que distrae un
poco, dando al artículo el aspecto de una nota de rescate. Para eliminar los realces de la
visualización, elija dos veces el comando Resaltes del menú Ver o haga clic en los botones
de herramientas Anterior y Siguiente para desplazarse temporalmente a otro artículo y a
continuación volver. También puede utilizar el comando Buscar en este tema del menú
Edición para encontrar texto dentro del artículo visualizado.
Aquí tiene algunas reglas básicas y unas cuantas advertencias para formular los pará-
metros de búsqueda en la pestaña Búsqueda:
Las búsquedas no son sensibles a las mayúsculas, así que puede teclear una frase de
búsqueda en mayúsculas o minúsculas.
Por defecto, MSDN encuentra sólo palabras enteras. Por ejemplo, una búsqueda de
key no encuentra «keyboard». Los comodines pueden anular este comportamiento
predeterminado, como se ha explicado brevemente.
Puede buscar cualquier combinación de letras y números, incluyendo los caracteres
sencillos (a, b, c, 1, 2, 3, etc.), pero no palabras simples tales como an, and, as, at, be,
but, by, do, for, from, have, he, in, it, near, not, of, on, or, she, that, the, there, these,
they, this, to, we, when, which, with y you. MSDN ignora estas palabras cuando
intenta que concuerde el texto, de modo que la búsqueda handle exceptions puede
ELENTORNO 21
también encontrar temas que contengan la frase «handle the exception» o ~handlean
exception».
MSDN acepta apóstrofos en una cadena de búsqueda, pero ignora otros signos de
puntuación, como puntos, comas, dos puntos, punto y coma y guiones. Esto asegura
que se encontrarán las cadenas independientemente del contexto, pero también abre
oportunidades a las combinaciones falsas. La búsqueda de la frase exception han-
dling, por ejemplo, puede que localice un tema no relacionado que contenga texto
como este:
Messages are an exception. Handling a message...
Comodines y operadores
Se puede formar una cadena de búsqueda como una expresión general utilizando el signo
de interrogación estándar (?) y asterisco (*), si los caracteres no están dentro de de signos
de interrogación dobles. El comodín del signo de interrogación representa un único carácter
en la expresión, de modo que la búsqueda de la cadena 80?86 pueda encontrar «80286»,
«80386» y «80486» (pero no «8086»). El comodín de asterisco representa cualquier secuen-
cia de cero o más caracteres. La búsqueda de *wnd*, por ejemplo, localiza texto tal como
«wnd», «Cwnd», «HWND» y «wndproc». El comodín de asterisco asegura que MSDN
encuentra todas las palabras relacionadas por una palabra raíz común. Para localizar palabras
como «keyboard», «keystroke»y «keypress»,por ejemplo, teclee key* en lugar de key como
cadena de búsqueda. Naturalmente, este enfoque puede resultar en aciertos de búsquedas no
relacionadas, como «keyword» y «key-type». Los operadores pueden refinar más los crite-
rios de búsqueda para minimizar tales efectos secundarios no deseados.
MSDN reconoce los operadores booleanos AND, OR y NOT y el operador de proxi-
midad NEAR. El mejor modo de describir los efectos de estos operadores es a través de los
ejemplos que se muestran en la Tabla 1.1. El operador NEAR presupone que las cadenas
están «próximas unas a otras» cuando se separan por no más de ocho palabras reconocidas.
MSDN no proporciona medios de especificar un criterio diferente para determinar la proxi-
midad.
Para conectar dos palabras por un operador, teclee el operador entre las palabras sepa-
radas por espacios, como se muestran en la Tabla 1. l ; no tiene importancia si las letras
están en mayúsculas o minúsculas. También puede hacer clic en el botón de la flecha (b)
adyacente al cuadro de combinación y seleccionar el operador deseado del menú emergen-
te pequeño.
Los operadores no tienen órdenes implicadas de precedencia, y MSDN evalúa las
expresiones en el orden normal de izquierda a derecha. Utilice paréntesis si es necesario
para asociar cadenas con operadores sin dejar lugar a dudas. MSDN ignora los paréntesis
dentro de las comillas dobles, de modo que no es posible buscar los archivos de temas para
observación parentética. La pestaña Búsqueda trata cada espacio en blanco como una
cadena de búsqueda como un operador AND, presuponiendo AND en la ausencia de los
operadores, paréntesis o comillas dobles. De este modo, introduciendo cualquiera de las
cadenas de búsqueda en la pestaña Búsqueda tiene el mismo efecto:
debug AND window AND breakpoint debug window AND breakpoint
(debug AND window) breakpoint debug window breakpoint
22 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
AND debug ANDwindow Encuentra temas que contienen tanto la cadena debug
como window en cualquier lugar dentro del texto, pero
no temas que contienen sólo una de las cadenas.
OR mfc OR «foundation Encuentra temas que contienen una o las dos cadenas.
1i b r a r y »
NOT ellipseNOTcdc Encuentra temas que sólo contienen la primera de las
cadenas dadas, pero no las dos. El ejemplo de la izquier-
da especifica que los temas que contienen la cadena
ellipse deberian obviarse si también contienen la pala-
bra cdc, ignorando así los temas sobre la función
CDC::Ellipse.
NEAR handl* NEAR exception Encuentra temas en los que las cadenas dadas están sepa-
radas por no más de ocho palabras.
Las versiones anteriores del sistema de ayuda InfoViewer permitían el uso de los equi-
valentes del lenguaje C de los operadores booleanos, reemplazando AND, OR y NOT con
los operadores ampersand (&), la barra vertical (1) y el signo de exclamación (!). MSDN
ignora estos caracteres, así que todos tienen el efecto del operador AND.
Estrategias de búsqueda
El método que debería utilizar para buscar en la ayuda en línea depende no tanto de lo que
esté buscando como de lo bien que pueda describir lo que está buscando. Si puede asociar
una o dos palabras clave con un tema, la búsqueda a través del índice de MSDN es normal-
mente el modo más eficaz de encontrar temas de interés. Al igual que el índice de un libro,
el índice de MSDN proporciona una conexión entre una palabra clave y una lista relativa-
mente pequeña de artículos relevantes, permitiéndole concentrarse en la información que
necesita. Por otro lado, una búsqueda de texto entero extiende una red más grande, que se
presenta a menudo con muchos más artículos a seleccionar que los que ha referenciado en
el índice. Los resultados de su búsqueda dependen de lo cuidadosamente que escriba las
cadenas de búsqueda y haga uso de los operadores de cadena. Después de llevar a cabo una
búsqueda de texto completo, puede resultar una tarea ardua el estudio minucioso de cada
artículo en la lista de búsqueda buscando sólo lo que mejor responde a su pregunta.
Si el área del tema es nueva para usted, puede que prefiera una visión e información de
fondo general. En este caso, la tabla MSDN de contenidos puede ser su mejor recurso.
Empiece mirando la organización general de la tabla de contenidos para ver qué hay ahí.
Algunas veces unos cuantos índices o búsquedas de texto completo le ayudarán a ubicar
una región de la tabla sobre la que centrar la atención. Después de que haya encontrado un
tema interesante de este modo, puede determinar dónde aparece el título del tema en la
tabla de contenidos haciendo clic en el botón Buscar de la barra de herramientas de
MSDN. Los botones Anterior y Siguiente seleccionan el artículo adyacente listado en la
tabla de contenidos, permitiéndole explorar a través de los artículos relacionados en se-
cuencias. Muchos temas empiezan con una fila útil de enlaces de hipertexto estándar que
le llevan a una página inicial, una vista general de un tema, una lista de preguntas hechas
con frecuencia, etc.
Pestaña Favoritos
Cuando recorra las sendas de texto de ayuda yendo de un artículo a otro, deseará inevita-
blemente volver atrás a un artículo por el que ya ha pasado. La pestaña Favoritos, que se
muestra en la Figura 1.13, le ayuda en esta tarea, manteniendo una lista de marcadores que
etiquetan los artículos seleccionados de modo que pueda volver a ellos inmediatamente.
Las etiquetas son como la lista de lugares favoritos o marcadores mantenidos por un ex-
plorador Web, y los encontrará de un valor incalculable para volver a trazar sus pasos
cuando esté explorando la ayuda en línea. Los títulos añadidos a la lista quedan permanen-
temente enumerados hasta que se eliminen, así que la pestaña Favoritos aparece a su
izquierda tal y como la dejó cuando inició el programa MSDN.
El título del artículo actual, es decir, el artículo visualizado en el panel derecho, apare-
ce en la parte inferior de la pestaña Favoritos. Haga clic en el botón Añadir para añadir el
título a la lista; haciendo dos veces clic en la lista se vuelve a invocar el artículo.
Figura 1.13. La pestaña Favoritos guarda una lista de los artículos que puede desear
volver a visitar.
24 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
net (localizadores fuente universales o URL) como enlaces de hipertexto, de modo que
MSDN abra una página Web igual de bien que cualquier otro artículo de la biblioteca.
Para especificar un sitio Web destino, elija el comando URL del menú Ir e introduzca
la dirección del sitio. La Figura 1.14 muestra un ejemplo.
.
M i c r o s ~ fYl el Ano 2 0 0 0
C 2 r L c t i 0. -' i): .-
ti!,a*1 OPATI^
,....
:.
,.,,, :. .,
.
G --fr.\
. e,,,,.:
n
..<* :e
m,, >,
VENTAJAS DE APPWIZARD
Archivo Descripción
entre objetos. Si el tipo de programa que tiene en mente es del tipo en que AppWizard se
especializa, no lo dude. Puede ahorrarse mucho tiempo configurando el proyecto con App-
Wizard.
EJECUCIÓN DE APPWIZARD
Un proyecto AppWizard comienza con el comando New en el menú File del entorno:
Haciendo clic en New visualiza la pestaña Projects del cuadro de diálogo New, que
enumera los asistentes de Visual C++. Para ejecutar el AppWizard que crea un proyecto para
una aplicación típica de Windows, seleccione el icono etiquetado MFC AppWizard (exe),
como se muestra en la Figura 2.2. Nos centraremos en este AppWizard por ahora. Un App-
Wizard hermano invocado por el icono MFC AppWizard (dll) ajusta su proyecto para el
desarrollo de una biblioteca de enlace dinámico, como veremos más adelante en el capítulo.
Introduzca un nombre para el proyecto. Como se mencionaba anteriormente, App-
Wizard utiliza el nombre del proyecto para identificar distintos archivos en el proyecto, así
que mantenga el nombre razonablemente corto. Una vez que se crea un proyecto, no hay
un modo práctico de cambiar su nombre. Por defecto, Visual C++ coloca los proyectos
Figura 2.2. Para crear un proyecto para una aplicación típica de Windows, seleccione
el icono MFC AppWizard (exe).
AppWizard en la carpeta CommonVMsDev98VMyProjects; si prefiere otra ubicación, espe-
cifique un aamino en el cuadro de texto Location. El botón OK no se activa hasta que
seleccione qn icono en la lista e introduzca un nombre de proyecto.
Cuando haga clic en OK, AppWizard presenta una serie de hasta seis pasos en forma
de cuadros Qediálogo. En cada paso, el lado izquierdo del cuadro de diálogo visualiza una
imagen que le da una clave visual de los ajustes que el diálogo solicita. Haga clic en el
botón Finisl) en cualquier paso para terminar AppWizard y acepte los ajustes predetermi-
nados en lo$ pasos siguientes. Para dar un paso hacia delante o hacia atrás a través de la
serie de cuqdros de diálogo, haga clic en el botón Next o en el botón Back.
En el paso ldel AppWizard, que se muestra en la Figura 2.3, especifica el tipo de aplicación
que desea, eligiendo o la interfaz de sólo documento (SDI), la interfaz de múltiples docu-
mentos (MDI) o la interfaz basada en un diálogo. Para crear una aplicación Windows simple
que no requiera un objeto de documento para leer información de un archivo de dis-
co, desactive el cuadro de comprobación etiquetado DocumentNiew Architecture Support.
Para una aplicación SDI que maneje sólo un objeto de documento cada vez, active el
botón de radio Single document. Esta selección también es adecuada para una aplicación
que no se ajuste de forma explícita con la arquitectura documento/visualización. Una apli-
cación SDI tiene menos sobrecarga que una aplicación MDI comparable, de manera que el
archivo ejecutable de la aplicación SDI es más pequeño.
Una aplicación MDI tiene la ventaja de ser capaz de manejar cualquier número de
documentos en seguida, visualizando cada documento en una ventana separada. El usuario
puede trabajar en ventanas de documento diferentes y guardar cada documento como un
archivo separado. Como veremos en los siguientes capítulos, el entorno de Visual C++ es
en sí mismo un ejemplo de una aplicación MDI, capaz de visualizar tanto texto como
información de no texto en distintas ventanas del editor.
La tercera opción de interfaz crea una aplicación basada en un diálogo. Esta selección
es adecuada para un programa de utilidad pequeño que no requiera una ventana principal,
porque el usuario interactúa con el programa a través de un solo cuadro de diálogo. Una
interfaz basada en un diálogo no limita tanto como pueda parecer, y el Capítulo 5, «Cua-
dros de diálogo y controles», demuestra cómo crear una aplicación basada en un diálogo
que visualice un cuadro de diálogo de hoja de propiedad que pueda aceptar y visualizar
una gran cantidad de información. La utilidad Marcador de teléfono que viene con Win-
dows es un ejemplo de una aplicación basada en diálogo.
Como el Capítulo 5 trata en detalle las aplicaciones basadas en diálogo, la opción de
interfaz basada en diálogo no se describe aquí. Sin embargo, mucha de la información de
este capítulo se aplica a las aplicaciones basadas en diálogos.
El paso 1 de AppWizard también consulta el lenguaje nacional en el que desea la
interfaz de su programa. Las lenguas disponibles dependen de las bibliotecas App-
Wizard que haya instalado en su sistema; haga clic en el botón de la flecha adyacente al
cuadro de texto para visualizar las opciones de lenguaje. Cada lenguaje depende de su
biblioteca de enlace dinámica instalada por defecto en la carpeta Common\MsDev98\bin\
ide. El nombre de un archivo de biblioteca toma la forma Appwzxxx.dl1, donde xxx re-
presenta un código de tres letras para el lenguaje; por ejemplo, enu para inglés de los
Estados Unidos, deu para alemán y esp para español estándar. La Figura 2.4 muestra
cómo es el menú Archivo en tres idiomas diferentes para una aplicación generada por
AppWizard.
El paso 2 de AppWizard (que se muestra en la Figura 2.5) pregunta qué soporte de base de
datos desea instalar para su proyecto. Este paso y los pasos siguientes presuponen que
seleccionó la opción Single document o Multiple documents con el soporte documen-
to/visualización en el paso 1. Si su proyecto no utiliza una base de datos, haga clic en el
botón Next para saltar este paso y continuar con el paso 3. Como se muestra en la Fi-
Español Inglés Alemán
gura 2.5, cuatro botones de radio determinan la extensión del soporte de la base de datos
que AppWizard añade al proyecto:
None. Excluye las bibliotecas de soporte de base de datos de la construcción del
proyecto. Si su proyecto no utiliza una base de datos, seleccione el botón de radio
None para impedir que se añada código innecesario a los archivos de proyecto. Pue-
de añadir soporte de base de datos a su proyecto un poco más tarde.
Header files only. Incluye archivos y bibliotecas de cabecera de bases de datos en
la construcción, pero AppWizard no genera ningún código fuente para las clases de
bases de datos. Puede escribir todo el código fuente usted mismo. Esta opción es
adecuada para un proyecto que no utilice inicialmente una base de datos pero al que
planifique añadir soporte de base de datos en el futuro.
Database view without file support. Incluye archivos de cabecera de bases de da-
tos y bibliotecas, y también crea una visualización de registro y conjunto de regis-
tros. La aplicación resultante soporta documentos pero no serialización.
Database view with file support. El mismo ajuste que el anterior, excepto que la
aplicación resultante tiene soporte tanto para documentos de base de datos como
para serialización.
Si decide incluir una visualización de base de datos utilizando una de las dos últimas
opciones, no puede continuar con el paso siguiente hasta que defina una fuente para los
datos.
Fuentes de datos
Para definir una fuente de datos, haga clic en el botón Data Source para visualizar el
cuadro de diálogo Database Options que se muestra en la Figura 2.6.
32 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Figura 2.6. Identifica una fuente de datos en el cuadro de diálogo Database Options.
El cuadro de diálogo Database Options solicita una fuente de datos que cumple los
estándares de Conectividad de base de datos abierta (ODBC), Objectos de acceso a datos
Microsoft (DAO) o base de datos OLE (OLE DB). Las funciones ODBC están implemen-
tadas en controladores específicos a un sistema de administración de base de datos, como
por ejemplo Microsoft Access, Oracle o dBase. Visual C++ proporciona una colección de
controladores ODBC; hay otros disponibles de distintos vendedores. Para obtener una lista
de controladores incluidos con Visual C++, consulte el artículo titulado «ODBC Driver
List» de la ayuda en línea.
Cuando selecciona ODBC como el tipo de fuente de datos para su programa, App-
Wizard genera código que invoca al ODBC Driver Manager, que pasa cada invocación al
controlador apropiado. El controlador interactúa a su vez con el sistema de administración
de base de datos de destino utilizando el Lenguaje de consultas estructurado (SQL). El
soporte ODBC le asegura que una aplicación puede acceder a datos en diferentes formatos
y configuraciones.
Al seleccionar ODBC, activa una lista desplegable de todas las fuentes de datos regis-
tradas con el Administrador de fuente de datos de ODBC. Una fuente de datos incluye
tanto datos como la información necesaria para acceder a los datos. Para registrar o dejar
de registrar una fuente de datos, ejecute el Administrador haciendo dos veces clic en el
icono de ODBC de 32 bits en el Panel de control. Visual C++ normalmente ajusta el
Administrador durante la instalación, pero si necesitase una instalación personalizada de
Visual C++, el Administrador puede que no exista en su sistema. Si el icono de ODBC de
32 bits no aparece en Panel de control, ejecute el programa Setup de Visual C++ otra vez e
instale los archivos de soporte de base de datos ODBC necesarios.
D A 0 es el estándar para los productos de Microsoft tales como Access y Visual Basic.
Al utilizar el motor de base de datos Microsoft Jet, D A 0 proporciona un conjunto de
objetos de acceso, incluyendo objetos de base de datos, objetos tabledef y querydef y
objetos recordset. Aunque DA0 funciona mejor con los archivos MDB como los creados
por Microsoft Access, un programa D A 0 también puede acceder a fuentes de datos ODBC
a través de Microsoft Jet.
OLE DB es una nueva estrategia de acceso a datos que permite a una aplicación de
cliente, llamado consumidor, recuperar información desde cualquier fuente de datos equi-
pada con un traductor de datos, llamado proveedor. El proveedor, que aparece en la aplica-
ción de consumidor como un conjunto de objetos de Modelo de objeto de componente
(COM), generalmente no crea los datos, pero en cambio sirve como intermediario para
acceder a la información en su forma nativa (sea la que sea) y la pasa al consumidor en
forma reconocible. La Figura 2.7 ilustra cómo el consumidor comunica con el proveedor,
no con el creador original de la fuente de datos.
Una ventaja de OLE DB es que no tiene que haber ningún acuerdo a priori entre
consumidor y proveedor sobre el formato de los datos. Como mínimo, el proveedor es
responsable de la traducción de datos en una forma que el consumidor entienda, normal-
mente en un formato tabular. Un proveedor también puede añadir mejoras a los datos en
bruto, como por ejemplo procesamiento de consulta o clasificación según criterios especí-
ficos. La selección de la opción OLE DB en el diálogo Database Options de AppWizard es
el primer paso para la creación de una aplicación de consumidor de datos, no un proveedor
de datos. La opción genera código tomado de una biblioteca de plantillas de clase, llama-
das Plantillas de consumidor OLE DB, que proporcionan ajustadores para los objetos de
clase OLE DB, como CDataSource y CSession. Visual C++ proporciona otro asistente,
ATL COM AppWizard, que ayuda a escribir aplicaciones de proveedor. El Capítulo 10,
«Escritura de controles ActiveX utilizando ATL», tiene más que decir sobre ATL COM
AppWizard y la Biblioteca de plantillas activa, aunque desde la perspectiva de la escritura
de los controles ActiveX, no de los proveedores de OLE DB.
Tipo recordset
Especifique el tipo recordset que utilizará su programa seleccionando uno de los tres boto-
nes de radio en la sección Recordset Type del cuadro de diálogo Database Options. Los
botones de radio gobiernan las tres opciones descritas a continuación:
iSnapshot. Una imagen instantánea recordset mantiene una visualización de datos
como los datos que existían en el momento en que se creó la imagen instantánea.
En el paso 3 de AppWizard (Fig. 2.8), ajuste el tipo deseado de OLE y de soporte ActiveX
para su programa. Los cinco botones de radio en la parte superior del cuadro de diálogo
controlan el tipo de soporte de documento compuesto que AppWizard añade a su progra-
ma. Aquí tiene las descripciones de las opciones de soporte de documento compuesto:
None. AppWizard no genera ningún código para el soporte de documento com-
puesto.
Container. AppWizard crea un programa que pueda contener objetos enlazados e
incrustados.
Dociunento activo 2;
Este es un ejemplo de un documento Activo, en el que el servidor Microsoft Word aparece dentro de
la vei~tanadel contenedor de Intemet Explorer 4 O El contenedor ha incorporado ordenes de menu en
ambas aplicaciones en un sistema de menu sencillo y proporciona acceso normal a todas las barras de 1
herramientas
Soporte de impresión
Por defecto, AppWizard activa el cuadro de diálogo Printing And Print Preview. Esta
opción añade código de iniciación a una visualización de aplicación que anula las funcio-
nes virtuales de CView de MFC.
........................................................................
/ / Impresión de CDemoView
Ayuda en Iínea
BEGIN-MESSAGE-MAP(CMainFrame, CFrameWnd)
ON-COMMAND(1D-HELP-FINDER, CFrameWnd::OnHelpFinder)
ON-COMMAND(1D-HELP, CFrameWnd::OnHelp)
ON-COMMANDíID-CONTEXT-HELP, CFrameWnd: :OnContextHelp)
ON-COMMAND(1D-DEFAULT-HELP, CFrameWnd: :OnHelpFinder)
END-MESSAGE-MAP ( )
APPWIZARD 39
Cada entrada en el mapa apunta a una de las tres funciones proporcionadas por el
marco de trabajo de MFC. Las funciones se invocan en respuesta a diferentes sucesos,
cada función invoca WinHlp32 y visualiza el texto apropiado desde el archivo de ayuda de
proyecto. La tabla siguiente describe cuándo se invocan las funciones:
Al invocar la herramienta Ayuda hace que el programa ejecute WinHlp32, que visuali-
za una ventana de ayuda describiendo el elemento sobre el que ha hecho clic. Por ejemplo,
al solicitar ayuda haciendo clic en el botón Guardar como se ilustraba anteriormente visua-
liza la ventana de ayuda que se muestra en la Figura 2.12.
Para mejorar el sistema de ayuda con descripciones de otros rasgos que puede pro-
gramar usted mismo, cargue AfxCore.rtf en un procesador de texto que reconozca el
formato nch-text. No utilice la utilidad Bloc de notas que viene con Windows para esta
tarea. Aunque el Bloc de notas lee documentos en formato rich-text, no guarda informa-
ción esperada por el compilador de ayuda. Los documentos rich-text están en formato
ASCII normal, así que si fuera necesario puede realizar cambios en un momento con el
editor de texto.
El primer paso en la creación de su propio texto de ayuda es buscar la cadena
««YourApp»» y reemplazar cada coincidencia con su nombre de aplicación. Los pá-
réntesis angulados (« ») en el documento encierran el texto de resguardo que sugiere
el tipo de texto de ayuda que usted debería añadir. Reemplace tanto las sugerencias
como los paréntesis con texto nuevo. Quite cualquier parte de los temas que no concier-
nan a su aplicación, llevando su pista para formateado necesario del texto colocado en el
archivo por AppWizard. Los temas en el archivo se deben separar con un corte de página
manual.
La autoría de ayuda es un tema largo y no es posible hacer aquí una descripción
completa. La ayuda en línea describe cómo utilizar la utilidad Help Workshop propor-
cionada con Visual C++ para construir sobre el sistema de ayuda que crea AppWizard.
El botón Advanced
En el ángulo inferior derecho del cuadro de diálogo del paso 4 de AppWizard hay un
botón que cuando se le hace clic visualiza un cuadro de diálogo con dos pestañas titulado
Use este comando para guardar el documento activo con su nombre y directorio
actuales. Cuando guarda un documento por primerave2 <<YourApp>>muestra el
cuadro d~ diálocin Gciardnr ci-?mr~
para que pueda asignar un nombre al documento.
Si desea cambiar el nombre y el directorio de un documento que y a existe antes de
guardarlo, elija el camando Guardar cnrrnü.
Método abreviado
Barra de herramientas: 1Ea%
Estilo de proyecto
AppWizard ofrece dos variaciones del estilo de programa determinado por botones de
radio en la parte superior del diálogo del paso 5. El botón de radio predeterminado, etique-
tado MFC Standard, es la opción correcta para la creación de una aplicación de Windows
normal con una clase de visualización derivada de CView. En los capítulos siguientes de
este libro, utilice el ajuste MFC Standard cuando use AppWizard para crear programas de
ejemplo, de modo que merezca la pena emplear tiempo aquí examinando los efectos del
segundo botón de radio. El botón se etiqueta Windows Explorer porque la aplicación que
crea AppWizard tiene una interfaz similar en aspecto y usuario a la conocida utilidad
Explorador que viene con Windows.
La ventana principal de la aplicación de tipo Explorador está dividida en dos paneles
contiguos, cada panel muestra una visualización diferente y cada uno está gobernado por
su clase propia. La clase de visualización para el panel izquierdo deriva del CTreeView de
MFC, haciendo el panel apropiado para visualizar una lista de elementos relacionados a
través de una jerarquía tipo árbol, como por ejemplo una lista de personal de una compa-
ñía, un árbol genealógico o la composición de archivos y carpetas en el disco duro. La
clase de visualización que se corresponde con el panel derecho se deriva de CListView,
diseñado para visualizar una lista de elementos que pertenecen de alguna manera a la
selección actual en el panel izquierdo. Al igual que el Explorador, la barra de herramientas
del programa contiene cuatro botones adicionales que modifican el aspecto del panel,
permitiendo al usuario elegir diferentes administraciones de visualización de iconos gran-
des o pequeños. Aquí tiene una idea de cómo puede aparecer una aplicación de tipo Explo-
rador típica:
Rool leiel A
- Fir-t lei.el 1 2 3 4
Subleve1 1 Firsl item Cecond ilem Thnd item Fourlh item
Cublevel2
S"blwd 3
Todavía no hemos hablado de los editores de Visual C++ con los que examinará y
cambiará un programa, pero si está interesado en revisar el código fuente para la aplica-
ción simple de la imagen superior, encontrará archivos de proyecto en el CD que se acom-
paña en la carpeta Codigo\Capitulo.O2\Demo. El proyecto Demo se creó utilizando App-
Wizard, eligiendo la opción Single Document en el paso 1 y la opción Windows Explorer
en el paso 5, y aceptando los valores predeterminados de AppWizard para las selecciones
que quedan. Demo es un proyecto rudimentario, creado sólo para sugerir el tipo de código
que debería añadir a una aplicación de tipo Explorador generada por AppWizard. El Capí-
tulo 3, «El editor de texto», y el Capítulo 4, «Recursos», describen con más profundidad
cómo utilizar los editores de Visual C++ para acceder a los proyectos de visualización
como los encontrados en el CD que se acompaña.
44 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Al solicitar comentarios de archivo fuente, hace que AppWizard añada notas útiles «para
hacer» al código fuente generado. Las notas aparecen como comentarios similares a los
que se muestran aquí, sugiriendo instrucciones fuente que deberían añadir para hacer ope-
rable una función o característica:
idiomas diferentes. El archivo de biblioteca MFC contiene datos en cadena tales como
texto de diálogo y mensajes de ayuda a los que puede acceder un programa. Debe asegu-
rarse de que su aplicación no accede y visualiza cadenas de biblioteca escritas en un
idioma que no sea el lenguaje nativo del usuario. Hay dos modos de solucionar este pro-
blema. La solución más simple es escribir su aplicación de modo que utilice su propia
cadena de datos exclusivamente sin acceder a texto proporcionado por la biblioteca (el
Capítulo 4, «Recursos», trata este tema con más detalle). Puede distribuir Mfcnn.dl1 inde-
pendientemente de los ajustes regionales de usuario.
La segunda solución implica la escritura de su programa de instalación de manera
que consulte el sistema de huésped para su idioma local, copie el archivo redistribuible
Mfcnnxxx.dl1 a la carpeta Sistema, y renombre el archivo Mfcnnloc.dl1 (el xxx en el nom-
bre del archivo representa el código de tres letras para el idioma del host, como deu para el
alemán y esp para el español estándar). Para obtener más información sobre este tema,
consulte las Notas Técnicas 56 y 57 en la ayuda interactiva, localizadas a través de la
entrada «MFC components» en el índice MSDN.
Si prefiere enlazar su aplicación de forma estática a MFC, seleccione el botón de radio
etiquetado As A Statically Linked Library. La vinculación estática significa que su aplica-
ción no depende de la presencia del archivo de biblioteca MFC, aunque todavía necesite el
archivo Msvcrt.dl1. El coste del vínculo dinámico es un tamaño de ejecutable más grande y
potencialmente el uso ineficaz de la memoria. La vinculación de forma estática a MFC no
es posible con la Edición de aprendizaje de Visual C++.
La opción de vinculación MFC que elija en el paso 5 es sólo el ajuste inicial para el
proyecto, y puede seleccionar una opción diferente en cualquier momento durante el de-
sarrollo. Antes de construir el desarrollo, elija el comando Settings del menú Project, y en
la pestaña General del cuadro de diálogo, elija vinculación dinámica o vinculación estática.
diálogo New (consulte la Figura 2.3). Este AppWizard particular visualiza sólo el simple
paso que se muestra en la Figura 2.16, que solicita información del tipo de cómo debería
enlazar su biblioteca de enlace dinámico con MFC.
El asistente ofrece tres opciones de vinculación diferentes, cada una con ventajas e in-
convenientes. Las dos primeras opciones resultan en una biblioteca de enlace dinámico que
cualquier programa Win32 puede acceder. La tercera opción limita más porque crea una
biblioteca de enlace dinámico que se puede utilizar sólo por aplicaciones u otras bibliote-
cas que también utilicen MFC. Las opciones de vinculación se describen a continuación.
BOOL CDemoApp::InitInstanceO
I
48 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
if (!AfxSocketInitO)
{
AfxMecsageBox(IDP~SOCKETS~INIT~FA1LED);
return FALSE;
1
return TRUE;
1
Dentro de cada almacenaje local de hilo, MFC mantiene un puntero a una estructura
llamada el estado del módulo, que contiene información específica sobre módulo en pro-
ceso que actualmente está servido por la biblioteca MFC. Cuando una aplicación introduce
una función exportada en su biblioteca de enlace dinámico, el estado del módulo pertenece
a la aplicación que invoca, no a su biblioteca. Antes de pasar la ejecución al MFC, la
función exportada debería alterar primero el puntero para referenciar el propio estado de
módulo de DLL. Este es el propósito de la macro AFX-MANAGE-STATE, que tempo-
ralmente cambia el puntero del estado de módulo para referenciar el módulo actual, es
decir, su biblioteca de enlace dinámico, a continuación restablece el puntero original cuan-
do la función exportada sale del ámbito y vuelve a la aplicación de invocación. AFX-
MANAGE-STATE no es necesaria para exportar funciones invocadas por la propia bi-
blioteca de MFC, como por ejemplo Initlnstance y las funciones de manejadores enumera-
das en un mapa de mensajes, porque MFC se encarga de instalar cuidadosamente el módu-
lo de estado correcto antes de la invocación.
La macro AFXMANAGE-STATE debería aparecer cerca del principio de una fun-
ción, incluso antes de las definiciones de variables de objetos, porque sus constructores
pueden incluir ellos mismos invocaciones a la biblioteca MFC. El bloque de condición
#ifdef que se muestra en el fragmento asegura que el compilador incluye el código de
macro sólo para una biblioteca que enlaza dinámicamente a MFC. Si el enlace es estático,
el compilador de Visual C++ no predefine la constante AFXDLL. Para información más
detallada sobre la macro AFX-MANAGE-STATE, consulte la Nota Técnica 58, «MFC
Module State Implementation», en la ayuda en línea de MSDN.
El editor de texto
Visual C++ proporciona un verdadero editor de texto de programación que está diseñado
especialmente para la tarea de «cortar código». El editor se integra muy bien con otras
herramientas del entorno, tal como el depurador, y ofrece una amplia gama de sofisticadas
características, incluyendo DeshacerIRehacer, comandos de pulsación personalizables y
acceso instantáneo a las referencias de MFC y Win32.
Un editor de texto requiere muy poco preámbulo; como programador, ya ha utilizado
al menos un editor y probablemente más de uno, así que vamos a empezar. Este capítulo
abarca los aspectos más importantes del editor de texto de Visual C++, describiendo carac-
terísticas útiles y ocultas y mostrándole cómo utilizar el editor de forma eficaz. Incluso
aunque decida quedarse con su editor actual para la mayoría de sus tareas de codificación,
debería por lo menos leer por encima este capítulo para tener una idea de las posibilidades
del editor de Visual C++. Más tarde o más temprano encontrará conveniente quedarse en
el entorno de Developer Studio cuando edite texto, aunque sólo sea para hacer modifica-
ciones rápidas para corregir los errores de compilación. Hay algunos consejos al final del
capítulo que puede encontrar útiles.
Como es un producto de Windows, el editor de texto Visual C++ guarda sus archivos
de texto en el formato de archivo ANSI. Para una discusión del estándar ANSI y las tablas
de los conjuntos de caracteres ANSI y ASCII, consulte el Apéndice A.
Cuando accede por primera vez a Visual C++, no ve el editor de texto. Tampoco ve un
botón que dice «Start the text editor», ni siquiera la palabra «editor» mencionada en ningu-
no de los menús. En el entorno orientado a objetos de Visual C++ sólo se preocupa sobre
54 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
el tipo de documento que desea, no sobre qué herramienta necesita para crearlo. El entorno
supervisa varios editores, además del editor de texto, así que sólo necesita indicar lo que
desea o revisar un documento de texto en lugar de, por ejemplo, un documento gráfico.
Visual C++ deduce del tipo de documento qué editor debe iniciar.
Para iniciar un documento nuevo partiendo de cero, haga aparecer el menú File y elija
el comando New. En la pestaña Files del diálogo New que se muestra en la Figura 3.1,
Visual C++ visualiza una lista de tipos de documento que puede crear, administrando la
lista en orden alfabético.
Introduzca un nombre de documento si lo prefiere, y a continuación seleccione de la
lista Active Server Page, C/C++ Header File, C++ Source File, HTML Page, Macro File,
SQL Script File o Text File. Haga clic en el botón OK para iniciar el editor de texto, que
aparece en forma de una ventana de documento en blanco. Hay un poco de bombo y
platillo cuando ocurre esto y los menús y las barras de herramientas apenas cambian. La
continuidad asegura un aspecto y comportamiento común entre los editores de Visual
C++, haciendo que el producto entero sea más fácil de aprender y utilizar. Si el documento
nuevo aparece como una ventana de tamaño completo, sólo unas cuantas pistas visuales
(aparte del propio documento) indican que está ahora en el editor de texto en lugar de en la
ventana principal de Visual C++. Una pista es un icono pequeño que aparece en el borde
izquierdo de la barra de menú. Otra indicación visual es el aspecto del nombre del docu-
mento encerrado entre paréntesis en la barra de título en la parte superior de la ventana
principal. Si no introduce un nombre de documento en el diálogo New, el editor inventa
uno, dándole al documento nuevo un nombre temporal, como Textl o Cppl. El nombre
sirve como resguardo hasta que guarde el documento y proporcione un nombre más des-
criptivo para el archivo.
Por debajo de la superficie, otros cambios ocurren dentro de los menús. Como vere-
mos en próximos capítulos, los menús son comunes a todos los editores de Visual C++,
incluyendo el editor de texto. Cuando el editor de texto se inicia, muchos de los coman-
dos de menús que estaban desactivados están ahora activos. Visual C t t habilita automáti-
camente los comandos de menú apropiados para cualquier editor que tenga centro de
entrada. Por ejemplo, como la búsqueda de texto tiene significado sólo en el editor de
texto, el comando Find del menú Edit aparece en texto normal cuando el editor de texto
está activo pero en gris cuando el editor de gráficos tiene la atención. La Figura 3.2 descri-
be brevemente los menús disponibles cuando el editor de texto está activo.
Hay también otros comandos disponibles. Por ejemplo, si borra el texto en el editor y
a continuación cambia de idea, el menú Edit ofrece los comandos Undo y Redo. Estos
comandos recuerdan un historial de supresiones, empezando con la supresión más re-
ciente. Para restablecer el texto de supresiones anteriores, continúe haciendo clic en
Undo o pulse CTRL+Z repetidamente hasta que recorra todo el camino hacia atrás por
el historial del texto que quiere restablecer. Esto tiene el efecto secundario de restable-
cer más supresiones recientes en el orden inverso, que puede ser lo contrario de lo que
desea.
El comando Redo en el menú Edit (que también se activa presionando la combinación
de teclas CTRL+Y) invierte el comando Undo más reciente, permitiéndole «deshacer un
deshecho».
DOCUMENTOS
Esta sección es la más larga del capítulo, describiendo cómo crear, abrir, visualizar, guar-
dar e imprimir documentos de texto. Como he mencionado anteriormente en la Introduc-
ción al principio del libro, parte del material explicado aquí parecerá probablemente una
revisión si ha utilizado antes un editor de texto basado en Windows o un procesador de
texto. Pero incluso los usuarios de Windows experimentados puede que se beneficien de
los temas de visualización e impresión de un documento, dado que abarcan el material
específico del editor de texto de Visual C++.
Debe tener en cuenta que las palabras «documento» y «archivo» se utilizan común-
mente de manera intercambiable cuando se refieren a la edición de texto. Abrir un archivo
y abrir un documento tiene el mismo significado.
Abrir un documento
El editor de texto de Visual C++ cumple con los estándares MDI (interfaz de documento
múltiple), de modo que puede tener un número cualquiera de documentos abiertos al mis-
mo tiempo. Repitiendo los pasos de abrir un documento con el comando New crea un
segundo documento vacío, esta vez con un nombre predeterminado, como Text2 o Cpp2.
Si la ventana tiene el tamaño completo, el nombre del documento actual, es decir, el
documento que tiene el centro de entrada, aparece en la barra de título en la parte superior
de la pantalla. Puede cambiar de documentos abiertos presionando CTRL+F6 o seleccionan-
do el nombre del documento deseado del menú Window.
Un documento se crea sólo una vez en su vida. Cuando guarda un documento nuevo en
el disco duro, existe desde ese momento en adelante como un archivo, y se debe abrir en
vez de crearlo cuando desee trabajar con él de nuevo. Utilice uno de los siguientes méto-
dos para abrir un documento existente:
Haga clic en el botón Open en la barra de herramientas Standard.
Por defecto, la lista MRU contiene los últimos cuatro archivos a los que se ha accedido a
través de cualquiera de los editores de Visual C++, no sólo del editor de texto. Para visuali-
zar la lista MRU, despliegue el menú File y deje el cursor momentáneamente en el coman-
do Recent Files. Seleccionando un nombre de archivo en la lista abre el documento en el
editor apropidado. La lista MRU es una comodidad bienvenida cuando trabaja siempre
con unos cuantos archivos. Incluso para un proyecto de programación pequeño, sin embar-
go, puede que se encuentre continuamente editando más de cuatro archivo fuente, así que
incluso archivos accedidos recientemente pueden desaparecer rápidamente de la lista
MRU. Afortunadamente, Visual C++ le permite expandir la lista para tener más nombres
de archivos. Haga clic en Options del menú Tools, y a continuación desplácese hacia la
derecha si es necesario para seleccionar la pestaña Workspace. Introduzca un valor nuevo
EL EDITOR DE TEXTO 57
en el cuadro de texto etiquetado Recent File List Contains, como se muestra a conti-
nuación.
La pestaña Workspace proporciona otra opción que afecta al aspecto de la lista MRU.
Si prefiere ver la lista directamente en el menú File en lugar de un submenú separado,
deseleccione el cuadro de comprobación etiquetado Show Recently Used Items On Sub-
menus. El documento adicional y los nombres de proyectos pueden hacer que el menú File
parezca saturado.
El diálogo Open
Visualización de un documento
A través del editor de texto, utilice la pantalla de forma inteligente, el espacio puede ser
escaso en Visual C++ cuando haya varias ventanas visibles. Para la visualiación más gran-
de posible de su código fuente, elija Full Screen del menú View, como se muestra en la
Figura 3.3, o pulse ALT+Vy a continuación U. La barra de títulos, los menús y las barras de
herramientas desaparecen para proporcionar el máximo espacio. Para volver a la visualiza-
ción normal, pulse la tecla ESC o haga clic en el botón en la barra de herramientas Full
Screen. Puede acceder a los menús en visualización de pantalla completa pulsando la tecla
ALT seguida de la primera letra del menú que desee, ALT+F para el menú File, por ejemplo
(la tecla ALT activa la barra de menú, así que las dos teclas no necesitan pulsarse de forma
simultánea). Pulse las teclas de FLECHA DERECHA e IZQUIERDA para desplazar los menús
adyacentes. Sin embargo, no puede utilizar el ratón para deslizar los menús adyacentes del
mismo modo que puede hacerlo cuando la barra de menú está visible.
Si encuentra que la barra Full Screen le distrae, elimínela haciendo clic en el botón
cerrar de la barra de herramientas. Con la barra de herramientas Full Screen desactivada, el
único medio de volver a la visualización normal desde el modo de pantalla completa es
pulsando la tecla ESC.Para volver a habilitar la barra de herramientas en visualización de
pantalla completa, pulse ALT+Tpara visualizar el menú Tools, y a continuación haga clic
en el comando Customize. En la pestaña Toolbars del diálogo Customize, active el cuadro
de diálogo Full Screen en la lista de barras de herramientas. Del mismo modo, puede
fabricar otras barras de herramientas o incluso la barra de menú visible en modo de panta-
lla completa.
EL EDITOR DE TEXTO 59
El menú Window proporciona una lista de todos los documentos que están abiertos
actualmente, incluyendo aquellos abiertos en otros editores de Visual C++. La lista de la
Figura 3.3, por ejemplo, contiene un documento de texto llamado Test.cpp y un mapa de
bits abierto en el editor de gráficos (que se describe en el Capítulo 4). Puede cambiar de
documentos abiertos desplegando el menú Window y haciendo clic en el nombre del
documento con el que desea trabajar. Para ver todas las ventanas de documento al mismo
tiempo, elija los comandos Cascade, Tile Horizontally o Tile Vertically.
Las ventanas del documento del editor de texto tienen múltiples paneles «divisorios»,
permitiéndole visualizar una, dos o cuatro partes diferentes del mismo documento a la vez.
La Figura 3.4 muestra una visualización de cuatro paneles de un documento simple.
una vista diferente del mismo documento. a vista diferente del mismo documento.
Visual C++ crea cada ventana de texto utilizando la clase CSplitterWnd de MFC, de
modo que divide los paneles que están habilitados automáticamente cuando crea o abre un
documento. Las barras separadoras que separan los paneles aparecen inicialmente como
dos botones pequeños, un botón colocado en la parte superior de la barra de desplazamien-
to vertical y el otro botón colocado en el ángulo izquierdo más lejano de la barra de
desplazamiento.
Para colocar una barra separadora, arrastre su botón al área de cliente de ventana y
suéltela. Puede mostrar ambas barras en un paso seleccionando el comando Split del menú
Wjndow. El comando Split centra un boceto de las barras separadoras en la ventana.
Mueva el ratón para colocar las barras como desee, y a continuación haga clic para cerrar-
las en el lugar.
Como los paneles separadores no tienen sus propias barras de desplazamiento indepen-
dientes, la visualización de separación más útil emplea sólo dos paneles, uno encima del
otro. Para hacer una visualización de dos paneles, arrastre la barra separadora vertical
totalmente a la izquierda o a la derecha hasta que la barra desaparezca. Vaya de un panel a
otro haciendo clic dentro del panel o pulsando la tecla F6.
Una visualización dividida en dos paneles es muy conveniente para dos visualizacio-
nes horizontales de un documento, pero esto es mucho menos eficaz para las visualizacio-
nes verticales contiguas, porque cada panel no se puede desplazar independientemente del
otro. Afortunadamente, otro control en el menú Window proporciona ordenadamente dos
o más visualizaciones de un documento. Con un solo documento abierto en el editor de
texto, haga clic en el comando New Window para abrir otra ventana que abra el mismo
documento. Esto no es lo mismo que abrir el archivo otra vez; la ventana nueva simple-
mente proporciona una segunda visualización del documento original en el espacio de
trabajo del editor. Cada ventana tiene su propio sistema de desplazamiento y cursor inter-
mitente, de modo que puede visualizar simultáneamente distintas partes del documento.
Para administrar las ventanas para la visualización contigua, haga clic en el comando Tile
Vertically en el menú Window. Haga clic dentro de una ventana o pulse C T R L + F ~para ir de
una visualización a otra.
Puede crear una visualización adicional haciendo clic en New Window otra vez. Aun-
que las ventanas de visualización operan independientemente una de otra, todas reflejan
los contenidos de un solo documento. Cualquier cambio que haga en una ventana aparece
inmediatamente en todas las ventanas.
Guardar un documento
Cuando está tecleando en una ventana de documento, aparece un asterisco al lado del
nombre del documento en la barra de título en la lista de documentos abiertos en el menú
Window. El asterisco le permite saber qué documento ha cambiado en algo y que los
contenidos del espacio de trabajo del documento en memoria difieren ahora del archivo en
disco. Al contrario que su procesador de textos, el editor de texto de Visual C++ no guarda
automáticamente su trabajo en desarrollo a intervalos regulares. Conforme teclea código
fuente nuevo, coja el hábito de guardar frecuentemente su trabajo en el disco utilizando
uno de estos métodos:
Hacer clic en el botón Save de la barra de herramientas Standard,
BAK. Una vez que guarda un documento, su primera versión en el disco desaparece para
siempre. Si necesita algunas variaciones de su fuente, elija Save As del menú File y déle a
cada versión fuente un nombre de archivo diferente.
Impresión de un documento
Para imprimir el documento que tiene la atención de entrada, haga clic en Print en el
menú File o pulse CTRL+P para abrir el diálogo Print. Si desea imprimir sólo una parte de
una lista fuente, por ejemplo una sola subrutina, seleccione primero el texto deseado. Al
hacer esto activa el botón de radio Selection en el diálogo Print, que se muestra en la
Figura 3.5.
El botón de radio Selection indica que sólo se imprimirá el texto seleccionado en lugar
del documento entero. Puede anular el ajuste haciendo clic en el botón de radio All.
El diálogo muestra la impresora a la que Windows enviará la tarea de impresión. Para
designar cualquier otra impresora adjunta a su sistema, haga clic en el cuadro de combina-
ción Printer y elija entre la lista de impresoras disponibles. Haga clic en OK para iniciar el
trabajo de impresión. Con la cola de impresión activada (lo que es probable), el control
vuelve casi inmediatamente al editor de texto, permitiéndole continuar su trabajo. Puede
imprimir varios trabajos en una sucesión rápida, a pesar de la monitorización el progreso
de sus tareas de impresión necesita una excursión a la carpeta Printers. Haga clic en el
botón Start en la barra de tareas, elija Settings, y a continuación haga clic en Printers.
Seleccione la impresora deseada y haga clic en Open en el menú File para ver la cola
actual de sus trabajos de impresión.
Cuando está activa la cola de impresión, su oportunidad de cancelar una tarea de im-
presión desde el editor dura unos instantes. Una vez que la cola de impresión tiene control
de la tarea de impresión, el botón Cancelar desaparece de la pantalla. Después de esto,
puede cancelar una tarea de impresión sólo desde la carpeta Printers. Si por alguna razón
está inhabilitada la cola de impresión, el editor de texto debe esperar hasta que la impreso-
ra finalice antes de que le devuelva el control.
El editor de texto de Visual C++ ofrece una cantidad limitada de formateado para la
página impresa, dejándole ajustar márgenes y especificar una cabecera y pie de página
para que aparezcan en cada página. Elija Page Setup del menú File, a continuación el texto
deseado en el cuadro de texto Footer o Header en el diálogo Page Setup. Utilice los códi-
gos de la Tabla 3.1 para incluir información en tiempo real en el texto de cabecera o de pie
de página.
le a navegar por el editor de texto de forma más precisa y eficaz. Primero, deberíamos
estar de acuerdo en parte de la terminología. El cursor familiar de los editores de texto
basados en DOS tiene un nombre diferente en Windows. Windows llama al indicador
parpadeante «signo de intercalación», porque su función es similar a la del símbolo de
signo de intercalación de revisor de libros ( A ) utilizado para indicar dónde debería inser-
tarse el texto nuevo.
La palabra «cursar» está reservada en Windows para la flecha (u otra imagen) que
muestra la posición del ratón actual. La ayuda en línea de Visual C++ llama al signo de in-
tercalación «punto de inserción», pero como programador de Windows debe saber la di-
ferencia técnica entre cursor y signo de intercalación. Entonces, cuando encuentre funciones
API, como por ejemplo ShowCaret y SetCaretPos, no tendrán ningún misterio para usted.
Las pulsaciones de tecla para mover el signo de intercalación en el editor de texto
deberían ser familiares para cualquiera que haya utilizado un procesador de texto de Win-
dows. La Tabla 3.2 describe las principales teclas de movimiento del signo de intercala-
ción del editor de texto.
Tabla 3.2. Teclas de movimiento del signo de intercalación del editor de texto
Por cierto, aquellos otros editores son correctos: un documento es una secuencia de
texto continua. No puede tener agujeros. Así que el editor de Visual C++ inteligentemente
«tabifica» el hueco entre el texto existente y cualquier otro texto nuevo añadido a la línea
de espacio virtual. El editor rellena el hueco con teclas el máximo posible, a continuación
añade espacios para las últimas columnas sólo si es necesario.
Ambas escuelas tienen sus seguidores. El editor de texto de Visual C++, siempre per-
sonalizable, le deja elegir, permitiéndole cambiar el ajuste de espacio virtual según sus
preferencias siguiendo estos pasos:
1. Del menú To-ols, elija Options.
2. Haga clic en la pestaña Compatibility.
3. Ajuste o borre el cuadro de comprobación Enable Virtual Space.
Delimitadores concordantes
El editor de texto reconoce pares de delimitadores que encierran bloques de código fuente
de C/C++, dejándole que mueva el signo de intercalación con una sola pulsación de tecla
desde un delimitador a su homólogo correspondiente. El editor puede distinguir tres deli-
minitadores: paréntesis ( ), llaves { ] y corchetes [ 1.
Los delimitadores aparecen en pares concordantes que sirven como sujetalibros para
bloques de código fuente. Cada par establece un nivel de delimitador y puede encerrar un
número cualquiera de subniveles anidados. El fragmento siguiente muestra un ejemplo
típico en el que los niveles están delimitados por llaves:
if (msg = WM-USER)
/ / Inicio del nivel A
for (i=O; i < 5; i++)
/ / Inicio del nivel B
Los dos grupos más internos tienen el mismo nivel (C), y ambos están contenidos en
niveles A y B. Cuando el signo de intercalación está al lado de los primeros paréntesis (que
empieza el nivel A), pulsando CTRL+] desplaza el signo de intercalación al último parénte-
sis al final del nivel A, saltando los paréntesis intermedios.
El editor determina a qué nivel pertenece un delimitador utilizando un viejo truco de
programador para comprobar código fuente: cuenta los paréntesis. Debe haber un número
igual de paréntesis abiertos que cerrados dentro de cualquier nivel, o el código es erróneo.
Para moverse hasta el final del nivel A desde los primeros paréntesis, el editor busca hacia
delante un paréntesis cerrado que concuerde mientras que lleva la cuenta. Para cada parén-
tesis abierto (hacia la derecha) que encuentre, aumenta la cuenta en uno. Cada paréntesis
(hacia la izquierda) disminuye la cuenta. La cuenta pasa a cero cuando el editor encuentra
el delimitador al final del nivel en el que comenzó.
El editor también reconoce las directivas de compilador condicional #if, #ifdef, #else,
#elif y #endif como delimitadores, aunque utilice pulsaciones de teclas diferentes para
navegar entre ellos. Cuando el signo de intercalación está en cualquier lugar dentro de un
bloque de directivas condicionales, puede desplazarse a la siguiente directiva pulsando
CTRL+J para desplazarse hacia atrás o CTRL+K para desplazarse hacia delante. Al añadir
la tecla MAYÚS a la combinación, selecciona el texto conforme el signo de intercalación se
desplaza a la siguiente directiva condicional.
Marcadores
Un marcador de editor de texto le guarda su puesto en un documento, permitiéndole volver
a la línea marcada sin importar dónde esté en el texto. Si en el pasado ha confiado en su
comando Go To del editor para regresar hacia una área interesante de su documento, verá
las ventajas de los marcadores. Go To apunta a un número de línea, pero a medida que
añade o suprime en algún otro sitio del documento, una fila de texto se puede meter o sacar
de esta posición original. Go To le deja en cualquier línea nueva que se haya movido
dentro de la abertura. Con un marcador, no tiene que recordar un número de línea para
volver a ella, y el marcador queda anclado en su línea a medida que el documento aumenta
o disminuye de tamaño. El editor de texto de Visual C++ ofrece dos tipos de marcadores,
llamados con nombre y sin nombre.
Un marcador puede ser tan persistente que a menudo parece exagerado. Un marcador con
nombre no es conveniente cuando sólo desea marcar un pasaje en su código fuente, volver
a consultarlo una o dos veces cuando esté editando otras partes del documento, y a conti-
nuación olvidarlo.
Para una marcación rápida, utilice en su lugar un marcador sin nombre. Un marcador
sin nombre es temporal, sólo dura hasta que elimine o cierre el documento. Marca una
línea, no una posición de signo de intercalación determinado. Cuando salta a un marcador
sin nombre, el signo de intercalación aparece al principio de la línea marcada. Si suprime
la línea, también suprime el marcador sin nombre.
La ventaja de un marcador sin nombre es que es fácil de ajustar e incluso más fácil de
suprimir. Para marcar una línea con un marcador sin nombre, pulse CTRL+F2 con el signo
de intercalación en cualquier parte en la línea o haga clic en el botón de la barra de
herramientas con la etiqueta sencilla, que se muestra a continuación.
1 Toggle Bookmark [ ~ t r l + ~ 2 ] I
Si está activado el margen de selección (como se describe más adelante en este capítu-
lo), aparece un icono de cuadro en el margen a la izquierda de la línea marcada. De lo
contrario, el editor marca la línea entera con un color diferente.
Puede saltar a un marcador sin nombre haciendo clic en los botones de la barra de
herramientas o pulsando las teclas F2 o MAYÚS+F~.Cada pulsación de tecla desplaza el
signo de intercalación secuencialmente hacia delante o hacia atrás a través de cada marca-
dor del documento, tanto con nombre como sin nombre.
Tiene varias opciones para eliminar un marcador sin nombre:
Coloque el signo de intercalación en la línea y pulse CTRL+F2 otra vez para desacti-
var el marcador.
i Pulse MAY~S+CTRL+F~.Esto elimina todos los marcadores sin nombre del documento.
Simplemente ignórelo. Los marcadores sin nombre en un documento desaparecen
cuando cierra el documento.
BÚSQUEDA DE TEXTO
El editor ofrece tres variaciones en el tema familiar de la búsqueda de texto:
Busca el texto en un documento abierto.
Reemplaza el texto en un documento abierto.
Busca el texto en archivos de disco.
Las dos primeras operaciones son prácticamente universales entre los editores de tex-
to. La búsqueda de texto en archivos de disco puede ser un rasgo menos usual, pero es
extremadamente útil, visualizando una lista de archivos que contienen una palabra o frase
en particular. Aquí tiene una explicación exhaustiva de las tres operaciones de búsqueda.
ejemplo, si una aparición de la cadena que desea buscar resulta estar en la pantalla, puede
tomar prestada la cadena sin tener que volver a teclearla. Para una sola palabra, simple-
mente haga clic en la palabra para ajustar el signo de intercalación en ella; si no, seleccio-
ne el texto que desee buscar arrastrando el cursor del ratón sobre él. A continuación abra el
diálogo Find pulsando CTRL+Fo eligiendo el comando Find del menú Edit. Cuando apare-
ce el diálogo, ya está inicializado con el texto seleccionado.
Puede refinar la búsqueda con parámetros que especifiquen la sensibilidad a las ma-
yúsculas y si la cadena debería concordar con una palabra entera o no. Haga clic en el
cuadro de comprobación Match Case para definir una búsqueda sensible a las mayúsculas
en la que el editor encuentre sólo texto que concuerde con la cadena de búsqueda de forma
exacta. Por ejemplo, una búsqueda sensible a las mayúsculas para «abc» encuentra sólo
esa cadena, mientras que una búsqueda no sensible a las mayúsculas para la misma cadena
puede encontrar abc, ABC o Abc. Haga clic en el cuadro de comprobación Match Whole
Word Only para ignorar las apariciones de la cadena de búsqueda contenidas en otra
palabra. Una búsqueda de palabra completa para «any» encuentra sólo ejemplos que apa-
recen como una palabra entera, ignorando palabras como company, many y ariywhere.
Haga clic en el botón Mark Al1 en el diálogo Find para etiquetar cada acierto de
búsqueda con un marcador sin nombre. Esta opción le permite volver a las apariciones de
una cadena a través de una sesión de edición mientras que continúa utilizando el comando
Find para buscar otras cadenas.
Una variación interesante de las capacidades de búsqueda del editor es un comando
llamado Incremental Search que comienza buscando a medida que teclea la cadena de
búsqueda. Pulse CTRL+I en un documento abierto y aparece la solicitud «Incremental
Search:» en la barra de estado en el ángulo inferior izquierdo de la ventana. A medida que
teclea la cadena de búsqueda, el editor comienza inmediatamente a buscar a través del
documento, normalmente localizando la cadena antes de que termine de teclearla. Cuando
el editor encuentra la palabra que está buscando, pulse INTRO o una tecla de FLECHA para
volver al modo edición. Para volver a buscar la misma cadena, haga clic en el botón
de barra de herramientas apropiada o pulse la tecla F3. La combinación de teclas
MAY~S+CTRL+I invierte la Incremental Search, de modo que el editor busque hacia atrás
desde la posición de signo de intercalación en lugar de hacia delante.
Reemplazar texto
Para buscar texto con el objetivo de reemplazarlo por otro texto, elija Replace del menú
Edit. Esto le presenta un diálogo parecido al diálogo Find, excepto que consulta dos cade-
nas en lugar de una. El primer cuadro toma una cadena normal. En el segundo cuadro,
teclee la cadena con la que desea reemplazar cualquier acontecimiento del texto encontra-
do. Si deja el segundo cuadro vacío, el editor reemplaza todas las visitas de búsqueda por
nada; es decir, suprime todos los acontecimientos de la cadena de búsqueda del docu-
mento.
Para buscar y reemplazar selectivamente, haga clic en el botón Replace cuando el
editor encuentre la cadena de búsqueda. A continuación salta automáticamente a la si-
guiente aparición de la cadena. Haciendo clic en el botón Find Next, salta por encima del
texto sin alterarlo. El botón Replace Al1 reemplaza todos los acontecimientos de la cadena
de búsqueda en un único paso. Puede buscar y reemplazar sólo hacia delante y sólo en el
documento actual, pero no en múltiples archivos.
Si selecciona más de una línea de texto antes de invocar el diálogo Replace, el botón de
radio Selection se activa automáticamente, indicando al editor que limite la operación de
búsqueda-y-selección a la sección seleccionada. Haciendo clic en el botón de radio Whole
File, anula el ajuste. Aunque puede seleccionar una columna de texto en el editor arras-
trando el cursor del ratón hacia abajo y a la derecha mientras que pulsa la tecla ALT,
normalmente no puede restringir los reemplazos a una columna seleccionada. El botón de
radio Selection está desactivado si la selección es columnar. Sin embargo, una macro
puede eliminar esta limitación, y el Capítulo 13 presenta una macro de ejemplo que le
permite buscar y reemplazar dentro de una columna marcada.
Figura 3.6. El cuadro de diálogo Find In Files, que se utiliza para buscar archivos
en disco.
72 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ra 1.7, pág. 12). Para dirigir la salida del comando, en lugar de la pestaña Find In Files 2,
active el cuadro de comprobación Output To Pane 2, que se muestra en la Figura 3.6. Al
activar o desactivar el cuadro de comprobación, permite mantener dos listas de archivo
separadas de modo que los resultados de búsqueda no superpongan los resultados de una
búsqueda anterior.
Una vez que ha ajustado los parámetros de búsqueda, haga clic en el botón Find.
Cuando Visual C++ encuentra un archivo que contiene la cadena de búsqueda dada, enu-
mera el nombre de archivo y la ruta en la ventana Output. Cada entrada en la lista también
incluye una copia de la línea en la que la cadena apareció por primera vez en el archivo, de
modo que pueda ver cómo se utiliza la cadena en el contexto. Haciendo dos veces clic en
un archivo en la lista lo abre en el editor de texto.
Antes de llevar a cabo una búsqueda de archivos, Visual C++ guarda primero cualquier
documento no guardado y abierto en el editor de texto, asegurando que se ha buscado la
versión más actualizada de cada archivo. Puede ajustar este comportamiento en la pestaña
Editor del diálogo Options a través de dos cuadros de comprobación etiquetados Save
Before Running Tools y Prompt Before Savings Files. Despejando el primer cuadro de
comprobación, Visual C++ instruye para que no guarde documentos abiertos antes de
buscar, restringiendo de este modo su búsqueda a documentos tal y como estaban la última
vez que los guardó. Si prefiere que Visual C++ le deje elegir si guardar o no un documento
cuando invoca el comando Find In Files, ajuste ambos cuadros de comprobación. Esto
hace que el editor consulte primero los permisos antes de guardar cada documento abierto.
Añada un signo más a la expresión y cambia el significado. El signo más significa «uno o
más de estos caracteres». El editor interpreta a continuación [a-zA-Z]+ como cualquier
cadena de letras, es decir, cualquier palabra. De forma similar, la expresión normal [O-91
significa un dígito, pero [O-9]+ se expande para significar cualquier número entero positi-
vo, independientemente de su tamaño.
Las búsquedas de expresiones normales son siempre sensibles a las mayúsculas. Inclu-
so si desactiva el cuadro de diálogo Match Case en el diálogo Find, una búsqueda para la
expresión normal [O-9a-fl+ encuentra sólo números hexadecimales como 0 x 3 7 pero ~ no
Ox7A4B. Para encontrar el segundo número, debe incluir letras en mayúsculas en la expre-
sión regular como esta: [O-9a-fA-F]+.
La codificación para Windows y MFC obliga incluso a los desarrolladores más expertos a
no alejarse mucho de los enormes libros de referencias y de la documentación en línea
cuando están codificando. Muy pocos de nosotros ejecutamos alguna vez más de una parte
ínfima de información necesaria para escribir programas de Windows, y pasamos mucho
tiempo buscando listas de parámetros y confirmando la ortografía de funciones y nombres
de variables. Pero el editor de texto de Visual C++ tiene posibilidades diseñadas para
ayudar a liberar al desarrollador de estas interrupciones interminables. Esta sección descri-
74 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
be la mejor y más nueva característica, una ayuda para teclear llamada Statement Com-
pletion.
Statement Completion es un término global para un trío de herramientas de programa-
ción, llamadas List Members, Parameter Info y Type Info. En un sentido casi literal, estas
herramientas ponen en sus manos una versión condensada de Win32 y material de referen-
cia de MFC.
List Members
Diseñados para acelerar la entrada de código y minimizar los errores tipográficos, el rasgo
List Members del editor permanece continuamente cerrado a mano a medida que teclea.
A través de una ventana emergente, List Members proporciona una lista enorme de miem-
bros de clase MFC, funciones de tiempo de ejecución C, constantes manifiestas, nombres
de estructura, funciones Win32 API y miembros de clase del proyecto actual, permitiéndo-
le seleccionar de la lista para completar la palabra que está tecleando actualmente. La
ventana List Member aparece automáticamente cuando teclea el operador de resolución de
ámbito (::), miembro de operador (.), u operador miembro puntero (->). Conforme con-
tinúa tecleando un nombre de miembro, la barra de selección de ventana se desplaza a la
entrada de la lista que mejor completa el nombre. La Figura 3.7 muestra cómo se pone a
cero la ventana List Members en la función CDC::SetMapMode incluso antes de que
termine de teclearla.
El editor de texto inserta la entrada de la lista realzada en el documento cuando teclea
un carácter que no sea una letra, como por ejemplo un espacio o un punto y coma. El
proceso es más fluido de lo que parece, especialmente después de un poco de práctica,
porque los nombres de miembros en código fuente van casi siempre seguidos de puntua-
ción; un paréntesis izquierdo después de un nombre de función, o un punto y coma o signo
SetEoundsRect
AddMetaFileCornment
igual después de una variable. El escenario que se muestra en la Figura 3.7, por ejemplo, se
completa lógicamente tecleando un paréntesis izquierdo, produciendo este resultado:
El menú Edit también contiene un comando llamado Complete Word, que implica la
existencia de otra herramienta más, Statement Completion. Pero Complete Word no es
una herramienta nueva en absoluto, sino solamente una forma abreviada de List Members.
En lugar de pulsar CTRL+ALT+T para invocar el comando List Members, probablemente
terminará por preferir la combinación de teclas más sencilla de CTRL+ESPACIO para ejecu-
tar Complete Word. Normalmente, ambas combinaciones de teclas tienen exactamente el
mismo efecto, visualizando la ventana List Members con la barra de selección colocada en
la primera entrada que completa correctamente la palabra que está tecleando. Pero si la
lista contiene sólo una posibilidad que completa su palabra, interactuar con la ventana List
Members puede parecer una distracción innecesaria. En este caso, la combinación
CTRL+ESPACIO estiliza la operación completando su palabra sin visualizar la ventana List
Members. Pulsando CTRL+ESPACIO después de teclear CreateMul, por ejemplo, completa
su tecleado en un solo paso, porque el editor de texto determina sin ambigüedad lo que
pretende teclear CreateMultiProfileTransform y no CreateMutex.
Parameter lnfo
La característica Parameter Info funciona en conjunción con List Members, emergiendo
como una ventana de herramienta de consejo discreta cuando teclea el primer paréntesis
después de un nombre de función. La ventana de herramienta de consejo sirve como una
tarjeta de pista en pantalla, visualizando el prototipo de función y los parámetros necesarios:
m-wndStatusBar.SetIndicators(I
~BOOL
Setindicators (constUINT *IpIDArray, int n 1 ~ ~ o u n t ) l
m-wndStatusBar GetPaneText(1
I%31 of 2 )$ CString GetPaneText (intn~nden)]
m-wndStatusBar GetPaneText(1
1 % o~F 2 1 vold GetPaneText (intnInden,CString &r~tring)l
La herramienta de consejo de Parameter Info aparece automáticamente cuando lo ne-
cesita, pero se puede invocar de forma explícita cuando se coloca el signo de intercalación
en cualquier sitio encima o a la derecha de un nombre de función reconocida. Elija el
comando desde el menú Edit o pulsando la combinación de teclas CTRL+MAY~S+ESPACIO.
EL EDITOR DE TEXTO 77
Haciendo dos veces clic sobre un nombre de función en un documento también propor&o-
na acceso al comando a través del menú contextual del editor. Como con cualquier otro
comando en Visual C++, puede asignar una combinación de teclas de su propia elección
para invocar Parameter Info. La sección titulada «Comandos no vinculados» (pág. 79)
explica cómo hacerlo.
Type lnfo
Type Info es parecida a Parameter Info, apareciendo como una ventana de herramienta de
consejo que visualiza información sobre una variable de función. Si Type Info reconoce el
nombre del símbolo bajo el cursor del ratón, la ventana de herramienta de consejo aparece
automáticamente, desapareciendo al mover el cursor. También puede elegir Type Info del
menú Edit o del menú de contexto, o pulsando la combinación de teclas CTRL+T. El segun-
do método es conveniente cuando desee información sobre un símbolo que acaba de te-
clear o pegar en un documento de la ventana de List Members. Cuando el signo de interca-
lación del editor está dentro o adyacente a un nombre de función, Type Info visualiza la
misma información que Parameter Info, enumerando el prototipo de función. Cuando se
invoca para un tipo definido, Type Info visualiza la sentencia typedef que crea el alias:
OLECHAR
(typedef unsigned short OLECHAR .]
Type Info es quizá más útil cuando se invoca para visualizar información sobre una
variable. Muestra la declaración de variable, así que ya no tiene que peinar código fuente o
recurrir a la fuerza al archivo de cabecera de clase para confirmar una variable de tipo. Por
ejemplo, el nombre de la variable indicators que se muestra aquí no da indicación de su
tipo, pero la ventana Type Info identifica inmediatamente la variable como una matriz de
números enteros no señalados:
EL COMANDO ADVANCED
El comando Advanced cerca de la parte inferior del menú Edit representa una colección de
opciones que pueden ser muy útiles cuando trabaje en un documento de texto. Deje el
cursor un momento en el comando Advanced para visualizar el menú secundario que se
muestra en la página siguiente.
78 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Como ve, el menú proporciona acceso al comando Incremental Search descrito ante-
riormente, aunque pulsar CTRL+I es un modo más conveniente de invocar el comando. El
comando Format Selection inserta tabuladores para ajustar los niveles de sangría en blo-
ques de código C/C++ delimitado por llaves. El comando puede convertir código como este:
if (msg = K U S E R )
{
for (i=O; i < 5; i++)
{
/ / Código adicional
1
en este:
if (msg = WM-USER)
{
for (i=O; i < 5 ; i++)
{
/ / Código adicional
}
1
El comando Format Selection funciona escaneando texto seleccionado por llaves para
determinar los niveles anidados. Las Iíneas de texto en el primer nivel están sangradas una
posición de tabulador, las líneas en el segundo nivel están sangradas dos posiciones, y así
sucesivamente.
El comando Tabify Selection cambia una serie seleccionada de caracteres de espacio a
una cadena equivalente de tabuladores. El comando Untabify Selection invierte el proce-
so, expandiendo tabuladores en espacios. Los efectos de cualquier comando se ven mejor
activando el conmutador View Whitespace, que hace visibles los espacios y tabuladores
en un documento. Cuando el interruptor está activado, cada carácter de espacio en el texto
aparece como un punto pequeño (.) y cada tabulador como dobles comillas bajas hacia la
derecha (»).
Los dos comandos que quedan en el menú secundario actúan como sus nombres sugie-
ren. El comando Make Selection Uppercase cambia todas las letras dentro de una selec-
ción a mayúsculas, mientras que el comando Make Selection Lowercase hace lo contrario.
Los caracteres que no sean letras en la selección, como por ejemplo los números y los
signos de puntuación, no se ven afectados.
EL EDITOR DE TEXTO 79
COMANDOS NO VINCULADOS
Cada comando de Visual C++ tiene un nombre interno descriptivo. Por ejemplo, los co-
mandos Incremental Search y Tabify que acabamos de describir tienen nombres internos
de SearchIncremental, SelectionTabify y SelectionUntabify. La ayuda en línea se refiere a
muchos otros comandos que no encontrará en los menús; comandos con nombres como
GoToNextErrorTag, LineTranspose y LineDeleteToStart. Hay dos razones por las que la
ayuda prefiere identificar comandos por el nombre interno en vez de por la combinación
de teclas como puede ser F4 o MAY~S+ALT+T. Primero, puede cambiar una combinación
de teclas para que un comando haga lo que usted quiera. Segundo, muchos comandos no
tienen pulsaciones de teclas asignadas para ello. Se dice que tales comandos están «no
vinculados». Para utilizar un comando no vinculado, primero debe asignarle una combina-
ción clave de su elección.
Bajo la superficie del entorno de Developer Studio yace un conjunto extensivo de
comandos; hay muchos más comandos disponibles de los que aparecen en menú y en
botones de barra de herramientas. Haga clic en Keyboard Map en el menú Help para ver
una lista de nombres de comandos, que se muestra en la Figura 3.8.
La lista predeterminada en la ventana Help Keyboard se llama «Bound Commands»,
lo que significa que éstos son los comandos que ya tienen combinaciones de tecla asigna-
das. Para ver una lista tanto de los comandos vinculados como de los no vinculados que
pertenezcan sólo al editor de texto, seleccione Edit del cuadro de combinación y haga clic
en el botón Command sobre la segunda columna para clasificar la lista alfabéticamente por
el comando. A medida que avanza en la lista, verá en la columna Keys que la mayor parte de
los comandos ya tienen asignadas combinaciones de tecla, pero otros muchos no. El conjun-
to de comandos no vinculados hace disponible una gran selección de características, a las
que de otra manera no se puede acceder a través de menús, barras de herramientas o teclado.
La forma de acceder a un comando la decide usted. Visual C++ le permite añadir
cualquier comando vinculado o no vinculado a un menú o barra de herramientas, como se
describe en el Capítulo 13, «Personalización de Visual C++». Pero como los menús y las
barras de herramientas abarrotados tienden a ser contraproducentes, a menudo es mejor
habilitar un comando no vinculado asignándolo a una combinación de teclas. La única
desventaja es que debe memorizar a continuación la pulsación de tecla que invoca el
comando.
Figura 3.8. Seleccione Keyboard M a p del m e n ú Help para visualizar una lista
d e comandos d e Visual C++.
80 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Nadie quiere habilitar todos los comandos no vinculados al mismo tiempo. Elija única-
mente aquellos que piensa que más le beneficiarán, dándoles la combinación de teclas que
mejor vaya con su estilo, y eso probablemente le refrescará la memoria. Como ejemplo,
vamos a añadir al editor de texto dos comandos útiles, llamados WordUpperCase y Word-
LowerCase, que cambian las mayúsculas de la palabra en el documento actual. Por defec-
to, WordUpperCase y WordLowerCase no tienen combinación de teclas asignadas, ni
tampoco hay botones de barra de herramientas u opciones de menú para invocar los co-
mandos. No hay un modo de utilizar los comandos hasta que especifique la combinación
de teclas para ellos.
Aquí tiene cómo habilitar los comandos. Desde el menú Tools, elija Customize para
abrir el diálogo Customize, y a continuación haga clic en la pestaña Keyboard. Seleccione
Edit del cuadro de combinación Category y asegúrese de que Text aparece en el cuadro
Editor. Estos ajustes significan que estamos ajustando una pulsación de tecla para un
comando que afecta sólo al editor de texto. Los comandos enumerados en el cuadro Com-
mands se clasifican por orden alfabético. Desplácese hasta la parte inferior de la lista para
encontrar la entrada WordUpperCase, y a continuación haga clic en la entrada para selec-
cionarla. Una descripción breve del comando aparece en el ángulo inferior izquierdo del
diálogo, pero el cuadro Current Keys permanece en blanco, indicando que no hay ninguna
tecla de comando asignada a WordUpperCase. Para asignar una tecla, haga clic en el
cuadro de texto Press New Shorcut Key y pulse la combinación de teclas que desee para
invocar el comando. Si pulsa CTRL+U, el diálogo le informa de que la combinación de te-
clas está actualmente asignada al comando SelectionLowercase. Esto no significa que no
pueda adjuntar CTRL+u a WordUpperCase si lo desea; es sólo para recordarle que si lo
hace, al pulsar CTRL+U ya no invocará SectionLowercase, que entonces se convertiría en
un comando no vinculado. ALT+U es una mejor elección para WordUpperCase, porque
CTRL+U ya está en uso. Cuando pulsa ALT+U, el diálogo le indica que la pulsación de tecla
no está actualmente asignada (véase la Figura 3.9). Haga clic en el botón Assign y la
pulsación de tecla está lista para su uso.
Figura 3.9. Asignar una combinación de teclas a un comando del editor de texto.
Haga lo mismo para el comando WordLowerCase, asignándole una pulsación de teclas
de ALT+L. Cuando pulse ALT+L en el cuadro de texto Press New Shortcut Key, un mensaje
le informa de que la combinación de teclas es utilizada para acceder al menú. El mensaje
se refiere al menú Layout, que está disponible sólo cuando el editor de diálogo está activo.
Como el menú Layout no tiene nada que hacer con el editor de texto, eligiendo ALT+L
no le supone un conflicto de pulsación de teclas. Cuando el editor de texto está acti-
vo, ALT+L invoca el comando WordLowerCase; cuando el editor de diálogo está activo,
ALT+L hace emerger el menú Layout como antes.
Para utilizar los nuevos comandos WordUpperCase y WordLowerCase, abra un docu-
mento de texto y coloque un signo de intercalación en cualquier sitio en una palabra. Al
pulsar ALT+U o ALT+L invoca los comandos, cambiando las mayúsculas de todas las letras
desde la posición de signo de intercalación al final de la palabra. Por casualidad, los co-
mandos nuevos también duplican los comandos SelectionUppercase y SelectionLower-
case, porque actúan en cualquier bloque de texto seleccionado, no sólo en una sola palabra.
Los comandos Selection son ahora superficiales, lo que no es ninguna tragedia. Las nuevas
pulsaciones de teclas ALT+U y ALT+L son más fáciles de utilizar y recordar que las combi-
naciones de teclas equivalentes MAYÚS+CTRL+U (Selectionuppercase) y CTRL+U (Selec-
tionlowercase). La duplicación se aplica sin embargo sólo a un texto seleccionado, porque
SelectionUppercase y SelectionLowercase afectan los dos el carácter adyacente al signo
de intercalación cuando no hay texto seleccionado.
Figura 3.10. Visual C++ ofrece una elección de iconos para un nuevo botón
de barra de herramientas.
de texto en la parte inferior del diálogo. Aquí tiene lo que puede parecer una barra de
herramientas nueva con botones para los comandos WordUpperCase y WordLowerCase:
Los comandos no vinculados no son sólo para el editor de texto. El capítulo siguiente
describe cómo utilizar estos métodos para implementar comandos útiles para el editor de
gráficos, como pulsaciones de teclas o botones de barra de herramientas. El Capítulo 13
explica con mayor detalle el tema de la creación de barras de herramientas en el entorno de
Visual C++, explicando cómo renombrar y suprimir barras de herramientas, cómo copiar
botones de una barra de herramientas a otra y cómo personalizar imágenes de botones.
Ahora tenemos una nueva macro. Visual C++ almacena la macro en la carpeta Com-
monWsDev98Wacros en un archivo llamado GlobalTemporary.dsm (la extensión de ar-
chivo significa macro de Developer Studio). El archivo contiene una sola subrutina de
Visual Basic que contiene instrucciones que invocan tres comandos que acabamos de
grabar:
Sub GlobalTemporary
ActiveDocument.Selection.Se1ectAll
ActiveDocument.Se1ection.Untabify
ActiveDocument.Selection.Start0fDocument
End Sub
(El Apéndice C, «Introducción a VBScript», examina con mucho más detalle el len-
guaje fuente de macros, Visual Basic Scripting Edition). Para experimentar con la macro,
abra un documento representativo y active el comando View Whitespace en el submenú
Advanced para hacer visibles los efectos de la macro. Ahora ejecute la macro pulsando
84 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
El editor de texto de Visual C++ quiere cambiar muchas de sus características para acomo-
dar mejor su estilo de trabajo. Ya hemos examinado el comando Customize del menú
Tools, que le permite personalizar barras de herramientas y asignar pulsaciones de teclas a
los comandos. Para cambiar otras características de la interfaz de editor, elija Options del
menú Tools.
El comando Options visualiza el diálogo que se muestra en la Figura 3.11, permitién-
dole especificar características de editor de texto tales como:
Aspecto, guardar documentos y opciones de Statement Completion.
iTabuladores y sangrías.
Emulaciones.
Fuentes.
En la pestaña del diálogo Editor, haga clic en sus preferencias de cómo y cuándo
debería el editor guardar un documento (el cuadro de comprobación etiquetado Auto-
en la pestaña ResourceView y expanda la lista haciendo clic en los signos más adyacentes
a los iconos de la carpeta. Para abrir un recurso en el editor apropiado (lo que haremos en
breve), haga dos veces clic en el recurso de la lista. La Figura 4.1 muestra los recursos
visualizados en el panel ResourceView de un proyecto AppWizard típico llamado Demo.
El archivo RC predeterminado creado por AppWizard es extensivo y contiene largas
tablas de cadena, guiones de menú y código que pertenece al desarrollo multiplataforma.
Si acepta todos los valores predeterminados de AppWizard cuando cree una aplicación
nueva, puede que termine con un archivo RC de casi 400 líneas. Puede que esté tentado de
modificar el archivo RC en un editor de texto, suprimiendo las líneas superfluas de código
generado por AppWizard y reduciendo el tamaño del archivo a proporciones manejables.
Pero hacer esto significa tener problemas más tarde cuando modifique un recurso con uno
de los editores de recurso de Visual C++. Aunque puede que termine con un archivo RC
válido, Visual C++, desafortunadamente, ya no lo reconoce como un producto de App-
Wizard. Todavía puede revisar un recurso con un editor, pero cuando guarde las revisio-
nes, Visual C++ superpone su archivo RC minimalista con uno nuevo que contenga mu-
chas de las adiciones superfluas de AppWizard, que ha eliminado previamente. La única
alternativa es guardar los recursos modificados bajo un nombre de archivo diferente, a
continuación utilice el editor de texto para copiar las líneas que desee desde el archivo
nuevo y péguelas en el guión de recurso original. Le recomiendo que aprenda a vivir con
archivos RC grandes que genera AppWizard, y excepto los pequeños cambios, revise los
recursos sólo a través de editores de recursos.
Cada recurso en un proyecto está identificado en el archivo RC, ya sea por un identificador
constante o, con menos frecuencia, por un nombre en forma de una cadena de carácter. Los
B 5Accelerator
IDR-MAINFRAME
@ i?
e DDiog
I-?
IDDABOUTBOX
8 a lcon
124 IDR-DEMOTYPE
IDR-MAINFRAME
El 9Menu
IDR-MAINFRAME
1'1 5String Table
& String Table
El Toolbar
;ao! IDR-MAINFRAME
El Version
El VS-VERCION-INFO
recursos en el programa ficticio Demo de la Figura 4.1, por ejemplo, están todos identifi-
cados por valores constantes: IDR-MAINFRAME para el menú y la barra de herramien-
tas, IDR-DEMOTYPE para uno de los iconos de programa, e IDD-ABOUTBOX para el
diálogo About. Las constantes que identifican los recursos de un proyecto están definidas
normalmente en un archivo llamado Resource.h, que sirve como el archivo de cabecera
principal para el archivo RC de proyecto. AppWizard crea Res0urce.h automáticamente
como parte de un proyecto, asignando los prefijos MFC estándar a los identificadores de
recursos. La Tabla 4.1 enumera algunos de los prefijos de identificador que utiliza MFC.
Los identificadores constantes pueden formarse por letras (mayúsculas o minúsculas),
numerales y subrayados, pero no pueden empezar con un numeral.
Los programadores de C saben cómo identificar los números como constantes mani-
fiestas o «defines», pero Visual C++ algunas veces se refiere a ellas como símbolos. Téc-
nicamente, un símbolo es un nombre en el código fuente, como por ejemplo una variable o
un nombre de función, que etiqueta una dirección de memoria. Veremos en el Capítulo 11,
«El depurador», cómo el compilador puede generar una lista de símbolos de un programa
que el depurador lee para aprender los nombres de las variables y funciones en el progra-
ma. No confunda los símbolos de recursos con los símbolos en su código fuente. Sin duda,
los diseñadores de Visual C++ eligen la palabra símbolo para promocionar la idea de que
el guión de recurso es también un tipo de código fuente y que un identificador de recurso
es análogo a un nombre de variable en la fuente del programa.
Puede cambiar el nombre o valor numérico de un identificador de recurso enumerado
en el panel ResourceView de la ventana Workspace. Primero muestre el nombre del iden-
tificador expandiendo el icono de la carpeta adecuada, como se muestra en la Figura 4.1.
A continuación haga clic en el identificador dentro de la lista para seleccionarlo y escoja
Properties en el menú de contexto desplegable View. Puede también hacer clic con el
botón derecho en un identificador y elija Properties del menú contextual emergente. De
cualquier modo, cambie el nombre del identificador volviendo a teclearlo en el control ID.
Al mismo tiempo, puede asignar un nuevo valor de número entero numérico añadiéndolo
al nombre del modo siguiente:
IDDABOUTBOX-NEW = 3001
Symbols en el diálogo del explorador. Por ejemplo, AppWizard añade esta línea al archivo
RC, que nos encontraremos de nuevo más tarde en este capítulo:
Antes de sumergirnos más profundamente en descripciones, vamos a ver una parte de1
archivo de guiones de recurso que genera AppWizard para el programa ficticio Demo.
Como hemos visto, AppWizard crea automáticamente un guión de recurso para un cuadro
de diálogo About, junto con un icono de MFC. El guión de diálogo generado en el archivo
Demo.rc es asi:
No está mal conseguir no escribir ni una línea de código. Sin embargo, AppWizard no
es útil en todas las ocasiones. Para mostrarle cómo se vive sin AppWizard, el resto de este
capítulo desarrolla un programa cargado de recursos a partir de cero sin AppWizard y
discute pros y contras de este enfoque.
Aquí empezamos una serie de secciones que desarrollan paso a paso un programa de
ejemplo llamado DiskPiel. Cada sección se concentra en un solo tipo de recurso, empe-
zando con menús y aceleradores y siguiendo con barras de estado, mapas de bits y barras
de herramientas. Una sección comienza con una discusión general de un tipo de recurso y
termina haciendo una contribución a DiskPiel, demostrando cómo crear o revisar un re-
curso con el editor de Visual C++ apropiado. Cuando hayamos terminado, DiskPiel será
una utilidad eficaz que muestra de un vistazo el uso de memoria actual y el espacio de
disco disponible.
Conservando las buenas costumbres de desarrollo, comprobaremos el «rendimiento»
del programa desde el principio antes de escribir nada de código. Las especificaciones le
darán una idea de los recursos que añadiremos al programa y le harán más fácil ver cómo
funcionan juntas para formar una interfaz consistente. Aquí tiene las especificaciones de
DiskPiel abreviadas:
i Descripción. DiskPiel es un programa de pequeña utilidad escrito con MFC que
visualiza un diagrama de pastel de dos piezas. Dependiendo de las selecciones del
menú o de la barra de herramientas, el diagrama muestra ubicaciones de espacio
actual para memoria en un disco designado. Una porción del pastel representa
espacio ocupado, mientras que la segunda porción, desplazada ligeramente de la
primera, muestra el espacio libre. Las etiquetas identifican claramente ambas por-
ciones.
w Ventana principal. El programa tiene cuatro menús, llamados File, Chart, View y
Help. El menú File contiene sólo un comando Exit, y el menú Help tiene un co-
mando About que visualiza información de programa. El menú View permite al
usuario mostrar u ocultar la barra de herramientas y la barra de estado. El menú
Chart en principio contiene sólo un comando llamado Memory, que visualiza el
uso de memoria. En tiempo de ejecución, DiskPiel busca los discos adjuntos al
sistema, incluyendo discos RAM y discos de red remotos, y los añade al menú
Chart. El programa ignora las disqueteras, discos CD y otro tipo de información
del que se pueda prescindir.
RECURSOS 95
rrecta para DiskPiel. Después de seleccionar Win32 Application para crear un programa
MFC como DiskPiel, debe configurar el proyecto para que reconozca la biblioteca MFC.
Como se describe en el Capítulo 2, «AppWizard», esto se hace a través de un cambio en la
pestaña General del diálogo Project Settings. Invoque el diálogo eligiendo Settings del
menú Project, seleccione Al1 Configurations del cuadro de combinación en el ángulo supe-
rior izquierdo, y elija o bien vinculación estática o bien dinámica para el proyecto. La
Figura 4.3 muestra la segunda elección.
Una vez que el proyecto DiskPiel ya hecho está abierto y debidamente configurado,
podemos empezar la creación de recursos para el mismo y añadir DiskPiel .rc y archivos
Res0urce.h. DiskPiel está cargado de recursos para un programa tan pequeño, así que la
mayor parte del trabajo implica la creación de datos de recurso. Escribiremos el código
actual para el último programa, después de que se completen los recursos.
subrayada en los menús para identificarla al usuario. Una tecla mnemónica debería ser
única para un menú o barra de menú; un menú Format, por ejemplo, debería tener una
tecla rnnemónica que no fuese «F» para evitar conflictos con el menú File. Pero el uso de
mnemónicas únicas es sólo una recomendación, no una regla; si una barra de menú o un
menú desplegable contiene las mismas mnemónicas en dos o más sitios, Windows realza
cada comando a la vez conforme el usuario pulsa la tecla mnemónica, y sólo activa el
comando elegido cuando se pulsa la tecla INTRO. El menú Editor, descrito en la sección
siguiente, puede comprobar los duplicados mnemónicos a través de un comando en su
menú contextual emergente. Haga clic en el botón derecho del ratón en el área de trabajo
del editor para invocar e1 menú:
Las teclas mnemónicas y aceleradoras no son las mismas. Una tecla aceleradora activa
un comando sin pasar a través del sistema de menú, mientras que una tecla mnemónica
subrayada está disponible sólo cuando un menú está visible.
Cada comando de menú tiene un identificador asociado que empieza con un prefijo
ID- seguido de un nombre que describe el comando. El nombre de identificador, incluyen-
do el prefijo ID-, es decisión completamente suya; el guión de menú de la página 97
muestra sólo lo que realiza AppWizard (como veremos, sin embargo, hay ventajas al
utilizar ciertos nombres de símbolo que MFC ya ha definido). Es a través de los identifica-
dores de comando como un programa se refiere a los sucesos del menú. Cuando el usuario
hace clic en el comando de menú o pulsa una tecla aceleradora, Windows envía un mensa-
je WM-COMMAND al procedimiento de la ventana principal con el identificador de
comando en la palabra inferior wParam. Si el comando está en respuesta al usuario pulsan-
do una tecla aceleradora, la palabra superior wParam tiene un valor de TRUE; si está en
respuesta a una selección de menú, la palabra superior es FALSE.
Un programa C maneja tradicionalmente comandos de menú comprobando el paráme-
tro wParam de un mensaje WM-COMMAND en una serie de sentencias que pueden
cambiar entre mayúsculas y minúsculas:
switch (msg)
I
case WM_COMMAND:
switch (LOWORD (wparam))
I
case ID-FILENEW:
OnFileNew O ;
break;
100 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
case ID-FILE-OPEN:
OnFileOpen ( ) ;
break;
BEGIN-MESSAGE-MAP(CMyFrame, CFrameWnd)
ON-COMMAND(1D-FILE-NEW, OnFileNew)
ON-COMMAND(1D-FILE-OPEN, OnFileOpen)
Cuando cree un recurso de menú partiendo desde cero tal como lo haremos para DiskPie 1 ,
elija Resource del menú Insert para visualizar la lista de tipos de recurso que se muestra
aquí, y a continuación haga dos veces clic en la entrada Menu de la lista para invocar el
editor de menú.
El proyecto debe estar abierto y puede que tenga que ocultar la ventana Workspace o
Output para mostrar el área de trabajo del editor. Cuando diseña y guarda su menú, Visual
C++ escribe el guión de menú al archivo RC del proyecto y escribe las sentencias #define
RECURSOS 101
MFC proporciona la cabecera Afxres-h para ahorrarle el problema de tener que definir
para cada proyecto los mismos identificadores comunes que aparecen en programas típi-
Figura 4.5. Creación de menús DiskPiel utilizando el editor de menús de Visual C++.
102 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
cos de Windows. Siguiendo la teoría de que la mayor parte de los programas de Windows
tienen menús File, Edit, View y Help, Afxres.h define muchos identificadores, como por
ejemplo IDFILE-OPEN-, ID-EDIT-COPY e ID-APP-ABOUT. Esto deja Res0urce.h
para los nuevos identificadores de recurso que define usted mismo. Por casualidad, Afx-
res.h no tiene definición para IDFILEEXIT, pero ¿qué pasaría si la tuviese? En ese caso,
obtendría un error cuando compilase el archivo RC, porque I D F I L E E X I T se definiría
dos veces, una vez en Afxres.h y otra en Res0urce.h.
Los guiones de recurso que genera AppWizard no tienen este problema potencial de
colisión de nombre. Todos los elementos de menú que genera AppWizard están definidos
en Afxres.h, de modo que AppWizard no les añade definiciones para Res0urce.h. Para un
proyecto que no sea de AppWizard como DiskPiel, tiene tres opciones para evitar defini-
ciones duplicadas cuando utilice los editores de recurso:
Abra el archivo RC en el editor de texto y elimine la sentencia #include para
Afxres.h.
Dé a los identificadores de recurso sus propios nombres, sin aceptar los nombres
predeterminados del editor que pueden estar en Afxres.h.
Edite el archivo Res0urce.h y suprima cualquier identificador que ya esté definido en
Afxres.h.
El problema con la primera opción es que también le obliga a eliminar del archivo toda
la parafernalia restante de Visual C++ que requiere definiciones en Afxres.h. La segunda
solución es más segura. Cuando usted mismo nombre los identificadores de recurso, la
MFC Technical Note 20 recomienda la adición del prefijo IDM- a los identificadores de
menú, dado que IDM- nunca se utiliza como un prefijo de identificador en Afxres.h.
Especifique el nombre del identificador en el diálogo Menu Item Properties, y ajuste op-
cionalmente un valor para el identificador al mismo tiempo del siguiente modo:
Asegúrese de que cada identificador de menú tiene un valor único, por supuesto.
Hay buenas razones para adoptar la tercera solución en la lista anterior de opciones, a
pesar de su falta de elegancia. Considere lo que pasaría si identificara el comando Exit en
su programa con un nombre como IDM-FILEEXIT. Para una aplicación MFC como
DiskPiel, debe a continuación proporcionar una función de manipulador para el mensaje
WM-COMMAND que transporta el identificador, y también añadir una línea al mapa de
mensaje que apunta al manejador. Los resultados pueden ser así:
Afxres.h contiene varios nombres de identificador especiales para los que MFC pro-
porciona sus funciones de manejador, ahorrándole a la aplicación el problema de tener que
hacerlo. Uno de estos identificadores especiales es I D A P P E X I T , que es atrapado de
forma automática por una función MFC que cierra la aplicación. Asignando el valor
ID-APP-EXIT al comando de menú Exit, DiskPiel no tiene que proporcionar su propio
código para manejar la selección de menú Exit. Por razones similares, los dos elementos
de menú en el menú View de DiskPiel tienen asignados los valores ID-VIEW-TOOL-
BAR y ID-VIEW-STATUS-BAR. MFC reconoce estos valores especiales e invoca sus
propias funciones de manejador para visualizar u ocultar la barra de herramientas y la
barra de estado. DiskPiel simplemente utiliza los identificadores en su guión de menú
para los comandos Toolbar y Status Bar, y el marco de trabajo se encarga de todo lo
demás.
La desventaja de darle a los comandos nombres de identificadores especiales tales
como ID-APP-EXIT o ID-VIEW-TOOLBAR es que el editor de menú escribe definicio-
nes para los nombres en el archivo Resource.h, duplicando de este modo definiciones en
Afxres.h. Tenemos que utilizar el editor de texto para suprimir las definiciones superfluas
en Res0urce.h después de crear los recursos.
Los nombres de los identificadores para los elementos del menú DiskPiel se especi-
fican tecleándolos en el diálogo Menu Item Properties. Aquí tiene un resumen de los
resultados:
-- --
Un nombre como IDR-MENU1 está bien para el menú, pero puede que no sea una
buena elección para un programa MFC como DiskPiel. Como programa de interfaz de
documento simple (SDI), DiskPiel puede registrar plantillas para sus recursos con una
sola invocación al constructor CSingleDocTemplate, siempre que los recursos tengan to-
dos el mismo valor de identificador. No importa cuál sea el valor del identificador, o
incluso si se dan nombres de identificador diferentes a los recursos, siempre que los recur-
sos de menú, barra de herramientas, tabla aceleradora y barra de estado estén todos repre-
sentados por el mismo número constante. Si su programa no invoca CSingleD~cTe~mplate
o su MDI equivalente CMultiDocTemplate, no se preocupe sobre la identificación de re-
cursos como menús y aceleradores con el mismo valor de símbolo.
Por defecto, los editores de recurso dan nombres y valores diferentes a todos los identi-
ficadores para los recursos de ventana principales, así que el archivo Res0urce.h puede
terminar apareciendo así:
La Figura 4.7 muestra cómo son los menús terminados para DiskPiel. Los comandos
Disk C y Disk D en el menú Chart no aparecen en el guión de menú anterior, porque estos
comandos se añaden al menú en tiempo de ejecución. El icono en la barra de título se crea
más tarde en el capítulo.
E VIRTKEY
IDM-DISK-F F VIRTKEY
IDM-DISK-G G VIRTKEY
IDMJlSKH H VIRTKEY
IDM DlSK I I VIRTKFY
VIRTKEY
VIRTKEY
VIRTKEY
VIRTKEY
VIRTKEY
Para añadir una tecla aceleradora a la tabla, haga dos veces clic en el cuadro de ele-
mento nuevo (que aparece como un rectángulo punteado) para invocar el diálogo Accel
Properties, a continuación teclee la tecla aceleradora y su identificador. Por ejemplo, aña-
da la tecla aceleradora CTRL+M para el comando Memory de DiskPiel tecleando M en el
control Key e IDM-MEMORY en el control ID. Asignando al acelerador CTRL+M el mismo
identificador dado al comando Memory en el editor de menú, nos aseguramos que pulsan-
do CTRL+M en DiskPiel y eligiendo Memory desde el menú Chart tenemos el mismo
efecto. En cualquier caso, el mismo procedimiento se invoca para visualizar el diagrama
de pastel para visualizar el uso de memoria.
Los aceleradores para los comandos Disk de DiskPiel tienen todos identificadores como
IDM-DISK-C, IDM-DISK-D, y así sucesivamente. Tenga en cuenta que en la Figura 4.8
ninguno de estos aceleradores están combinados con otras teclas tales como CTRL o MAYÚS,
permitiendo de este modo al usuario pulsar simplemente una tecla de letra como por ejemplo
C o D, para visualizar un diagrama de uso para el disco C o D. Para instalar o eliminar una
tecla de combinación para un acelerador, seleccione o deseleccione el cuadro de comproba-
ción CTRL, ALT o MAYÚS en el área Modifiers del diálogo Accel Properties.
La eliminación de teclas de la tabla es fácil en el editor de acelerador: simplemente
seleccione la entrada de tabla y pulse la tecla DELETE. Para seleccionar un bloque de entra-
das, haga clic en la primera entrada del bloque, a continuación mantenga pulsada la tecla
MAYOS y haga clic en la última entrada del bloque. La adición de nombres a la tabla lleva
más trabajo, especialmente si tiene muchas teclas. Para identificadores de símbolos que
tengan nombres secuenciales, como por ejemplo los de la tabla DiskPiel, un editor de texto
con capacidades de macros es algunas veces más conveniente. Si crea la tabla aceleradora
en el archivo RC utilizando un editor de texto, recuerde añadir definiciones apropiadas al
archivo Res0urce.h. Si una tecla aceleradora se corresponde con un comando de menú,
recuerde también darle al acelerador el mismo identificador que al elemento de menú.
El Portapapeles puede ser de ayuda en el editor acelerador cuando añada un grupo de
aceleradores que tengan nombres similares, como por ejemplo IDM-DISK-C hasta
IDM-DISK-Z. Teclee la primera entrada completamente, selecciónela en la lista y pulse
CTRL+C para copiarla en el portapapeles de Windows, a continuación pulse repetidamente
CTRL+V para copiar una serie de duplicados en el editor acelerador. Seguidamente, haga
dos veces clic en la entrada para invocar el diálogo Accel Properties y haga clic en el botón
con forma de interruptor para que el diálogo permanezca en la pantalla. A continuación
puede desplazar la lista hacia abajo para seleccionar entradas y modificar nombres y teclas
de identificador según sea necesario.
Haga clic en Save en el menú File para guardar la tabla de acelerador nueva. El editor
da al recurso un identificador como IDRACCELERATORl, que puede ver en el panel
ResourceView de la ventana Workspace. Para DiskPiel, este nombre no es deseable por la
misma razón que IDR-MENUl no es un nombre deseable para el recurso de menú. Pulse
con el botón derecho del ratón el identificador sobre el panel ResourceView y haga clic en
Properties en el menú contextual emergente, a continuación cambie el nombre de símbolo
de recurso a IDR-MAINFRAME, como se ilustra en la Figura 4.9. Este es el mismo
nombre dado anteriormente al recurso de menú.
Elija el comando Save otra vez para instalar el identificador IDR-MAINFRAME nue-
vo para la tabla de acelerador. Un fragmento muestra que la tabla de acelerador aparece
ahora como en el archivo DiskPiel actualizado:
RECURSOS 109
= 3 Accalerator
1 :1
%I IDR ACCELERATOR~]
-'( Menu
#define IDM-MEMORY
#define IDM-DISK-C
#define IDM-DISK-D
#define IDM-DISK-E
#define IDM-DISK-X
#define IDM-DISK-Y
#define IDM-DISK-Z
Los valores actuales que obtiene de los identificadores IDM-DISK no tienen impor-
tancia, pero existen dos buenas razones para guardar la secuencia de valores. Primera, los
valores secuenciales desde IDM-DISK-C hasta IDM-DISK-Z le permiten un único pro-
cedimiento en DiskPiel para manejar todos los comandos de menú o teclas aceleradoras
desde C hasta Z utilizando la macro ON-COMMAND-RANGE de MFC. Resolveremos
los detalles cuando empecemos a añadir código a DiskPiel. La segunda razón, para utili-
zar valores de identificador secuenciales tiene que ver con cómo Windows carga las cade-
nas contenidas en unos datos de recurso de programa. Esto es lo que viene a continuación.
110 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Recursos de cadena
Un recurso de cadena no es diferente de cualquier otra cadena en los datos del programa,
excepto en que debe leerse desde el archivo ejecutable en otro buffer. Un programa de C
lee un recurso de cadena invocando la función API LoadString; un programa MFC puede
invocar la función de miembro de la clase Cstring. Y al menos en el caso de las descripcio-
nes de la barra de estado, un programa MFC no tiene ni siquiera eso. MFC proporcion
código predeterminado que puede cargar cadenas de descripción de forma automática,
como veremos más tarde.
Un recurso de cadena está definido en el archivo RC del programa en una tabla de
cadena, que es una lista de cadenas identificadas por la palabra clave STRINGTABLE y
encerrada entre corchetes o entre llaves por sentencias BEGIN-END:
STRINGTABLE
{
ID-STRING1 "Text for string resource #1"
ID-STRING2 "Text for string resource # 2 "
1
cadenas en un lugar en vez de dispersas por varios módulos fuente. Entre otros beneficios,
esto permite que un traductor cree una versión de lenguaje extranjero del programa revi-
sando sólo los guiones en el archivo RC, después del cual el desarrollador puede recompi-
lar el archivo y volver a enlazarlo. El código fuente no se toca nunca.
Puede añadir o modificar recursos de cadena en el archivo RC utilizando el editor de
texto o el editor de cadena de Visual C++. Si cambia el nombre de un identificador de
cadena, el editor de cadena ofrece la ventaja de añadir automáticamente al archivo Resour-
ce.h una definición para el nuevo identificador. El editor de cadena no reemplaza, sin
embargo, los ejemplos del identificador anterior en su código fuente.
Aquí tiene un ejemplo simple que ilustra la relación entre menús, barras de herra-
mientas, cadenas de solicitud y herramientas de consejo. Aunque los fragmentos de
código que sigue describen un programa MFC que sólo abre y guarda un documento, los
pasos asociados mostrados en negrita se aplican también a los programas que no son de
MFC.
112 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
IDR-MAINFRAME MENU
BEGIN
POPUP "&FileU
BEGIN
MENUITEM "&openU, ID-FILE-OPEN
MENUITEM "&Caveu, ID-FILE-CAVE
END
END
STRINGTABLE
BEGIN
ID-FILE-OPEN "Abre un documento existente\nOpenM
ID-FILE-CAVE "Guarda el documento activo\nSaveU
END
O utilice este código para cargar el menú por separado después de crear la ventana
principal:
CMenu menu;
menu.LoadMenu( IDR-MAINFRAME );
SetMenu( &menu ) ;
menu.Detach ( ) ;
AppWizard genera código para hacer todo esto por usted. Sólo necesita invocar el
editor de cadena y eliminar recursos de cadena no necesarios, reemplazando con cadenas
que describan los nuevos comandos en sus menús de programa. Llegaremos al editor de
cadena en un momento, pero primero hay un recurso de cadena más a conocer.
La cadena de documento
Cuando hayamos terminado con DiskPiel, tendrá seis recursos de programa, cada uno
etiquetado con el identificador IDR-MAINFRAME:
El icono de aplicación.
iEl menú para la ventana principal.
(Como todos los recursos de cadena, la cadena de documento debe aparecer como una
sola línea en el archivo RC, pero por razones de espacio se ha mostrado anteriormente en
dos líneas). Las subcadenas contienen texto y nombres que MFC asigna al programa y los
documentos que crea. En el orden mostrado, las subcadenas especifican:
114 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Para un proyecto AppWizard que ya tiene una tabla de cadena en su archivo de guión RC,
inicie el editor de cadena haciendo dos veces clic en String Table en el panel Resource-
View de la ventana Workspace. Para crear una tabla de cadena para un proyecto como
DiskPiel, elija Resource del menú Insert y haga dos veces clic en String Table en la lista
Resource Type.
El editor de cadena es tan prosaico como las propias cadenas. Las únicas partes intere-
santes son las líneas horizontales en la ventana del editor, que puede ver en la Figura 4.10.
Estas líneas muestran las divisiones en la tabla de cadena entre grupos de cadena llamadas
segmentos, cada una de las cuales mantiene un máximo de 16 cadenas. El valor del identi-
ficador de cadena determina qué cadena pertenece a qué segmento. Las cadenas con valo-
res de identificador de O a 15 pertenecen al primer segmento, las cadenas con valores de 16
a 31 pertenecen al segundo segmento, y así sucesivamente.
Un segmento actúa como un buffer de lectura hacia delante que se encuentra en mu-
chas unidades de disco, en los que el controlador de disco lee no sólo un sector solicitado
del disco, sino también varios de los sectores siguientes, almacenándolos en un buffer de
memoria para usarlos posteriormente. Los buffers de lectura hacia delante aceleran el uso
de disco porque un acceso de disco va generalmente seguido por más, que el controlador
puede dar servicio leyendo desde el buffer en lugar del disco. Siguiendo la misma lógica,
el sistema lee recursos de cadena desde el archivo ejecutable un segmento cada vez. Cuan-
do un programa invoca la función API LoadString para leer una cadena individual desde sus
datos de recurso, Windows lee el segmento entero al que pertenece la cadena suponiendo
Usage for drive R\nDiive R
Usage foi diivs S\nDnve S
Uoage for diive T\nDrive T
IDM~DISK~U Urage for drive U\nDrive U
IDM-DISK-V
IDM-DISK-W
IDM DISK X
que si el programa desea una cadena ahora, pronto solicitará las otras. Por esta razón,
debería intentar agrupar las cadenas relacionadas dándoles valores de identificador se-
cuenciales. Probablemente haría esto de cualquier modo, pero ahora sabe el porqué es una
buena idea.
Como el resto de los editores de recurso de Visual C++, el editor de cadena indica si la
cadena siguiente está colocada en la tabla visualizando un cuadro de elemento nuevo
como un rectángulo punteado. Para introducir una cadena nueva, seleccione el cuadro de
elemento nuevo y comience a teclear el texto en cadena. El diálogo String Properties
aparece con un identificador de símbolo predeterminado. Vuelva a escribir el nombre de
identificador si lo desea, o seleccione un nombre desde la lista desplegable del cuadro de
combinación de diálogo. La lista contiene todos los identificadores definidos para el pro-
yecto, incluyendo aquéllos en el archivo Afxres.h, permitiéndole seleccionar un nombre,
como por ejemplo IDMMEMORY, en lugar de teclearlo. El editor de cadena clasifica
automáticamente las entradas de tabla por el valor del identificador.
Puede añadir caracteres especiales a una cadena tecleando las secuencias de escape
que se muestran en la Tabla 4.2. Para obtener una lista de los valores ASCII y ANSII
mencionados en la tabla, consulte el Apéndice A.
La tabla de cadena en el archivo DiskPiel .rc contiene todos los recursos de cadena de
programa:
STRINGTABLE DISCARDABLE
BEGIN
IDR-MAINFFGME "DiskPiel\nDisk Usage\n\n\n\n\n\nn
IDM-MEMORY "Memory usage\nMemoryn
IDM-DISK-C "Usage for drive C\nDrive C"
IDM-DISK-D "Usage for drive D\nDrive D"
IDM-DISK-E "Usage for drive E\nDrive E'
Figura 4.11. El editor de gráficos de Visual C++ con dos documentos abiertos.
118 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
tos, uno es un mapa de bits familiar de 16 por 15 y el otro es un cursor de ratón de color de
32 por 32.
La apariencia del editor difiere sólo ligeramente para cada tipo de recurso, que indica
el tipo de recurso en el que está trabajando por un icono en el ángulo superior izquierdo de
la ventana de documento. La Tabla 4.3 muestra el icono para cada documento de recurso y
enumera las extensiones para los tipos de archivo que el editor de gráficos lee y escribe.
Cuando utiliza el comando Open para abrir un documento de recurso existente con
cualquiera de las extensiones enumeradas en la tercera columna de la Tabla 4.3, Visual
C++ inicia automáticamente el editor de gráficos para el tipo de recurso apropiado.
DiskPiel tiene menús, teclas aceleradoras y datos de cadena, pero todavía no tiene
recursos gráficos. Para un proyecto en desarrollo como DiskPiel, hay dos medios ligera-
mente diferentes de crear un nuevo recurso de gráficos. La elección del método depende
de qué tiene en mente para el archivo de imagen. Si tiene un proyecto abierto y desea
añadir un nuevo recurso de gráficos al proyecto, elija Resource del menú Insert y haga dos
veces clic en Bitmap, Cursor, Icon o Toolbar en la lista. Visual C++ lanza el editor de
gráficos, visualizando en la barra de título un identificador asignado para el documento de
recurso. Dependiendo del tipo de recurso, el identificador es un nombre genérico tal como
IDB-BITMAPI , IDC-CURSOR1, IDI-ICON1 O IDR-TOOLBAR1 . Los subsiguientes
recursos abiertos en el editor reciben identificadores similares que incrementan el dígito,
como por ejemplo IDIICON2, IDI-ICON3, y así sucesivamente. Cuando guarda un re-
curso de gráficos en un archivo, Visual C++ le da al archivo el mismo nombre que el
identificador menos el prefijo y a continuación define al identificador en el archivo Re-
s0urce.h del proyecto. Por ejemplo, al guardar un recurso llamado IDB-BITMAP1, añade
esta línea al archivo RC:
Sin embargo, no tiene que aceptar estos nombres no descriptivos. Antes de guardar un
recurso, déle un identificador descriptivo haciendo clic en Properties en el menú View y
Transparencia activada
Transparencia desactivada
El color de fondo de la imagen de mapa de bits base, blanco, en este caso, permanece
opaco, independientemente del ajuste de transparencia, de modo que puede visualizar el
mapa de bits en la pantalla que superpone cualquier píxel cubierto por el área cuadrada de
mapa de bits. Sin embargo, un programa puede simular píxeles transparentes cuando vi-
sualice un mapa de bits, enmascarando primero el fondo de mapa de bits y reemplazándolo
con una copia del área de pantalla en la que aparecerá el mapa de bits. Si tiene interés en
esta técnica, puede encontrar una explicación completa con una clase derivada para los
mapas de bits transparentes en Programación de Windows 95 con MFC, de Jeff Prosise, en
el capítulo titulado «Mapas de bits, paletas y regiones». A menudo sólo desea asegurarse
que un mapa de bits tiene el mismo color de fondo que el de la ventana en que se visualiza,
dándole la ilusión de transaparencia. Hay un modo fácil de hacer eso, que se explica en la
sección siguiente.
Otro rasgo oculto del editor de gráficos es que los botones izquierdo y derecho del
ratón generalmente se corresponden con los colores de fondo y de primer plano, respecti-
vamente. Por ejemplo, al hacer clic en la paleta Colors con el botón izquierdo del ratón
selecciona el color de primer plano; haciendo clic con el botón derecho selecciona el color
de fondo. Puede dibujar en la imagen con cualquier color arrastrando o haciendo clic en el
botón del ratón adecuado.
RECURSOS 121
Botón Descripción
Botón Descripción
El Air Brush dibuja un modelo aleatorio de color, que simula el efecto de echar
3 spray sobre el dibujo. Elija la densidad y tamaño del spray en el cuadro selector de
la barra de herramientas.
Y?! Las herramientas de dibujo de líneas dibuja líneas rectas o torcidas «dibujando
bandas» desde la posición del clic inicial. Haga clic donde desee que empiece la
línea, arrastre el cursor hasta el punto final de la línea y luego suelte el botón del
ratón para ajustar la línea. La herramienta Curve necesita un paso más: después de
soltar el botón del ratón para ajustar el punto final, desplace el cursor para estable-
cer la curvatura de la línea. Cuando la línea tiene la forma que desea, haga doble
clic para ajustar la línea (debe hacer doble clic en el mismo botón del ratón que ha
utilizado para arrastrar el cursor). Si cambia de opinión mientras dibuja la línea,
presione la tecla ESC o haga clic en el otro botón del ratón para comenzar de
nuevo.
La herramienta Text visualiza una pequeña ventana para teclear texto que aparece
& en la imagen en el color frontal actual. Elija la transparencia de texto en el cuadro
selector de transparencia según se ha descrito antes, luego presione ESC cuando
termine de cerrar la herramienta Text.
Estas herramientas dibujan rectángulos, rectángulos redondeados y elipses, tanto
rellenas como vacías. Seleccione una herramienta, luego dibuje la forma con el
color de fondo o frontal arrastrando el cursor del ratón sobre la imagen desde la
parte superior izquierda hasta la parte inferior derecha del área que desea cubrir.
Si mantiene presionada la tecla MAYOS, restringe la forma, dibujando un rectángu-
lo dentro de un cuadrado o una elipse dentro de un círculo. Para cancelar mientras
arrastra el cursor, presione ESC o haga clic en el otro botón del ratón.
Mapas de bits
Cuando inicia el editor de gráficos para un nuevo mapa de bits, le presenta una área de
trabajo limpia de 48 píxeles cuadrados. Sin embargo, los mapas de bits no tienen por qué
ser cuadrados, pueden ser rectangulares de cualquier tamaño, hasta 2.048 píxeles en un
lateral. Para cambiar el tamaño del área de trabajo, arrastre uno de los manejadores que
cambian el tamaño al borde del área de trabajo, teniendo en cuenta el nuevo tamaño de la
barra de estado del editor conforme arrastra el manejador. También puede teclear el tama-
ño deseado en el diálogo Bitmap Properties, invocado haciendo clic en Properties del
menú View.
Aquí tiene un ejemplo de cómo un programa C puede visualizar un recurso de mapa
de bits. El fragmento supone que el mapa de bits se guarda originalmente en el archi-
vo Res\Bitmap.bmp y se identifica en el archivo RC del programa por el nombre
BitmapDemo, que es la misma cadena dada a la función LoadBitmap para cargar el
recurso:
//Declaración del recurso en el archivo RC
BitmapDemo BITMAP "res\\bitmap.bmpV
Los pasos son similiares para la visualización del mapa de bits en un programa MFC.
Primero inicialice un objeto CBitmap con el recurso:
BITMAP bm;
CDC dcMemory;
Antes de cerrar esta sección de mapas de bits, vamos a volver sobre el tema de la
transparencia de mapa de bits una última vez. Ya sabemos que un color de fondo de mapa
de bits es opaco, pero si el color de fondo de la imagen es blanco y el mapa de bits se
visualiza en una ventana que es también blanca, la forma cuadrada de mapa de bits queda
oculta. Sólo los colores de fondo que no sean blancos resaltan, creando la ilusión de un
fondo transparente. Pero ¿qué pasa si la ventana no fuese blanca? En ese caso, el mapa de
bits visualizado en los fragmentos de código anterior aparece con su color de fondo mos-
trado como un cuadrado, que puede que no sea lo que usted desea.
Las ventanas de aplicación normalmente toman el color de la ventana del sistema
identificado como COLOR-WINDOW, que por defecto es blanco. Sin embargo, un pro-
grama puede cambiar el color de la ventana del sistema invocando SetSysColors, o el
124 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
usuario puede cambiar el color en la sección Display del Panel de control de Windows (si
desea intentarlo, seleccione Window del cuadro de combinación Item en la pestaña Ap-
pearance del diálogo Display Properties, elija un color nuevo de la lista desplegable Color
y haga clic en OK). Un programa puede asegurar el color de fondo de un mapa de bits
siempre que concuerde con el color COLOR-WINDOW cargando el mapa de bits me-
diante el uso de la función API Loadlmage en lugar de LoadBitmap, especificando la
etiqueta LR-LOADTRANSPARENT como esta:
hbm = LoadImage( h I n s t a n c e , s z A p p N a m e , IMAGE-BITMAP,
o, o, LR-LOADTRANSPARENT );
Esta función mira el color del primer píxel de la imagen, que yace en el ángulo supe-
rior izquierdo del mapa de bits rectangular y es en principio parte del fondo. LoadImage
reemplaza a continuación la entrada correspondiente en la tabla de color de mapa de bits
con el color COLOR-WINDOW correspondiente. De este modo, todos los píxeles de la
imagen que hacen el fondo están visualizados en el color de la ventana predeterminada. La
única advertencia es que LR-LOADTRANSPARENT no funciona si el mapa de bits tiene
más de 256 colores.
No estamos con LoadImage todavía. La función también carga imágenes de icono,
como veremos en una sección más adelante. Pero ahora mismo vamos a echar un vistazo a
cómo un mapa de bits se puede convertir en una barra de herramientas.
Barras de herramientas
La creación de una barra de herramientas en Visual C++ es simplemente una cuestión de
diseñar un mapa de bits que contenga las imágenes para los botones de barras de herra-
mientas. El mapa de bits se almacena como un archivo BMP y se referencia en el archivo
RC del proyecto con un nombre de identificador:
Tanto el nombre del identificador como el nombre del archivo son decisión suya.
DiskPie 1 nombra su recurso de barra de herramientas IDR-MAINFRAME para concordar
con los recursos de otros programas, permitiéndole una invocación al constructor CSingle-
DocTemplate, como se explicó anteriormente.
El mapa de bits de la barra de herramientas es una serie de imágenes que recubren los
botones de la barra de herramientas, una imagen para cada botón. Por defecto, cada imagen
tiene 16 píxeles de ancho y 15 píxeles de alto, que es apropiado para un botón de barra de
herramientas que tiene como tamaño estándar 24 x 16 píxeles. Arrastrando los bordes del
cuadro de área de trabajo del editor al modo Windows, puede ajustar un tamaño de imagen
que sea mayor o más pequeño, más ancho o más estrecho. El nuevo tamaño se aplica a las
imágenes en la barra de herramientas, dado que no puede tener botones o tamaños diferentes
en una barra de herramientas. Cuando guarda su trabajo, Visual C++ especifica automáti-
camente el tamaño nuevo de los botones de la barra de herramientas en el archivo RC.
Si AppWizard genera el proyecto para usted, crea un archivo llamado Toolbar.bmp en
la carpeta Res de proyecto. El archivo contiene imágenes para los botones de la barra de
herramientas que se corresponden con los comandos New, Open, Save, Cut, Copy, Paste,
Print y Help. La Figura 4.13 muestra una ampliación del mapa de bits en Toolbar.bmp y la
barra de herramientas resultante.
El fragmento siguiente muestra el guión que escribe AppWizard al archivo RC del
programa para crear la barra de herramientas. Como podría esperar, cada botón en el guión
de la barra de herramientas tiene el mismo identificador que el comando de menú corres-
pondiente en el guión de menú listado en la página 97.
m-toolbar.Create( t h i s ) ; //Crealaventanadebarradeherramientas
m-toolbar.LoadToolBar( IDR-MAINFRAME ) ; / / Carga l a s imágenes de mapa d e b i t s
Hay dos enfoques para la creación de una barra de herramientas partiendo de cero en
Visual C++. El primero le parecerá familiar por ahora: en un proyecto abierto, haga clic en
Resource en el menú Insert y dos veces clic en Toolbar en la lista Resource Types. Esto
lanza la variación de la barra de herramientas del editor de gráficos, que visualiza tres
paneles divididos que se muestran en la Figura 4.14. Los dos paneles inferiores muestran
el tamaño real y las visualizaciones ampliadas del botón de la barra de herramientas actual
en la que está trabajando, y el panel superior muestra una visualización de la barra de
herramientas completa. A medida que comienza a trabajar en un botón, la imagen de botón
aparece automáticamente en la visualización de la barra de herramientas, cambiando en
tiempo real a medida que edita. Cuando ha terminado con un botón, haga clic en el botón
de elemento nuevo en la parte superior para obtener una área de trabajo nueva para el
botón siguiente. Puede cambiar la posición de un botón arrastrándolo dentro de la barra de
herramientas, o suprimir un botón arrastrándolo completamente fuera de la barra de herra-
mientas. Para añadir un hueco de separador entre botones como el de la Figura 4.14,
arrastre un botón derecho o izquierdo aproximadamente la mitad de la anchura del botón.
Puede cerrar un hueco del mismo modo.
No se preocupe sobre el botón de elemento nuevo en blanco cuando guarde la barra de
herramientas. No está incluido en el guión de la barra de herramientas que el editor escribe
al archivo RC. Cuando trabaje en la barra de herramientas principal del programa, déle a
cada botón el mismo identificador utilizado para los elementos de menú correspondientes,
como por ejemplo ID-FILE-NEW o ID-FILE-PRINT. Introduzca el identificador de bo-
Los botones que visualizan los diagramas de uso de los discos se añaden cuando se
inicia el programa. Por ejemplo, si DiskPiel encuentra cuatro discos con designaciones C,
D, P y R, especifica cinco botones cuando crea la barra de herramientas; un botón para el
comando Memory y los otros cuatro para los discos. Como no hay ningún modo de saber
previamente qué designaciones de control encontrará DiskPie 1 para cada sistema en el que
corre, el mapa de bits de la barra de herramientas para DiskPiel tiene imágenes de botón
para 24 discos diferentes, etiquetados de la C a la Z. Como cada una de las 25 imágenes
tiene 16 píxeles de ancho, el mapa de bits entero de la barra de herramientas DiskPiel
tiene 400 píxeles de ancho y 15 píxeles de alto. La Figura 4.15 proporciona una visualiza-
ción detallada de algunas imágenes de botones.
El mapa de bits no fue ni la mitad de difícil de construir de lo que puede pensar, se
tardó sólo unos 20 minutos. El secreto está en indicar al editor de gráficos que está creando
un mapa de bits en lugar de una barra de herramientas. El primer paso es el mismo en
cualquier caso: inicie el editor de gráficos haciendo clic en Resource en el menú Insert.
Para los mapas de bits de barra de herramientas anchas con imágenes repetidas como el
mapa de bits de la Figura 4.15, elija Bitmap en lugar de Toolbar desde la lista de tipos de
recurso. Esto le permite trabajar en las imágenes de botón en una tira continua en lugar de
Pulse la tecla INTRO para volver al área de trabajo, que ahora tiene las dimensio-
nes nuevas. El siguiente paso pinta el área de trabajo entera de color gris claro de
modo que cada imagen se combina con su botón. Haga clic en el cuadro de color
gris claro en la paleta Colors y seleccione la herramienta Fill útil en todo momento
en la barra de herramientas Graphics.
Haga clic en cualquier sitio en la cuadrícula del área de trabajo para
pintar toda el área gris claro. Ahora trace la imagen del primer botón en un bloque de 16 por
15 (o cualquiera que sea el tamaño del botón) dentro del área de trabajo. Una vez que ha
trazado la primera imagen, puede reproducirla haciendo clic en la herramienta Rectangle
Selection y arrastrando el cursor sobre el bloque de la imagen 16 por 15, como se muestra en
la Figura 4.16. Arrastre con el botón izquierdo o derecho del ratón, dependiendo de si tiene
intención de desplazar la imagen o copiarla. Utilice el botón izquierdo del ratón si desea
desplazar la imagen a algún otro sitio del área de trabajo del mapa de bits. Cuando suelta el
botón, aparece un marco de selección alrededor de la imagen, permitiéndole volver a colocar
END
Los identificadores de botón deben cambiarse con el tiempo a los mismos valores dados
a los comandos de menú, es decir, IDM-MEMORY, IDM-DISK-C, IDM-DISK-D, y así
sucesivamente.Esto le asegura que haciendo clic en un botón de barra de herramientas tiene
el mismo efecto que si selecciona el comando de menú equivalente. Podríamos especificar
los identificadores de botón correctos en el editor de gráficos utilizando el cuadro Toolbar
Button Properties, invocado seleccionando un botón y haciendo dos veces clic en cualquier
parte en el área de trabajo. Pero ese trabajo no es necesario. DiskPiel asigna los valores
130 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
correctos a los botones cuando determina en tiempo de ejecución cuántos botones deben
aparecer en la barra de herramientas. Editaremos el guión de barra de herramientas más
tarde, reemplazándolo con el guión matriz citado al principio de esta sección, y eliminare-
mos también del archivo Res0urce.h las sentencias #define superfluas para los identificado-
res de botones.
lconos
Un programa MFC no tiene que preocuparse sobre nada de esto. AppWizard proporcio-
na dos iconos para un proyecto, un icono que sirve como icono de aplicación, y el otro para
representar documentos que crea la aplicación. El archivo RC identifica los recursos de
icono como IDR-MAINFRAME y IDR-projectTYPE, donde project representa el nombre
de proyecto. AppWizard almacena los iconos, que se muestran aquí, en la carpeta Res del
proyecto como los archivos project.ico y projectDoc.ico:
132 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Si el archivo de icono contiene tanto una imagen pequeña como grande, MFC carga las
imágenes y las adjunta correctamente a la ventana de marco, de modo que la imagen peque-
ña aparece en la barra de título y la imagen grande aparece en el cuadro About. Si mira
a través del código fuente para aprender más sobre cómo funciona todo esto, verá que
CWinApp::LoadIcon no invoca a ::LoadImage como se ha descrito anteriormente. En su
lugar, invoca a ::FindResource con un valor de RT-GROUP-ICON para cargar todos los
mensajes en el recurso de icono, y a continuación busca el recurso para la imagen que más
concuerde con el tamaño necesario. CWinApp::LoadIcon recupera la imagen invocando la
función API ::LoadIcon con el valor de ejemplo devuelto por ::FindResource.
Todos estos métodos invocan el diálogo New Icon Image que se muestra en la Figu-
ra 4.17, que enumera los tamaños de imagen que están disponibles pero no están todavía
adjuntos al icono. Si no ve el tamaño de imagen que desea en la lista, haga clic en el botón
Custom y especifique un tamaño de imagen nuevo.
El diálogo New Icon Image proporciona los medios para incluir múltiples imágenes en
un solo recurso de icono. Después de haber trazado la imagen de 32 por 32, seleccione otro
tamaño del diálogo y empiece otra vez. Si desea volver a cambiar a la imagen original de 32
por 32, haga clic en la flecha desplegable hacia abajo en el cuadro de combinación Device y
elija Standard (32 por 32) de la lista expuesta. Cuando selecciona un tamaño de imagen
nuevo desde el diálogo New Icon Image, la entrada desaparece de la lista de diálogo y se
transfiere a la lista de cuadro de combinación. En otras palabras, el cuadro de combinación
Device enumera los tamaños de imagen actualmente en el icono, mientras que el diálogo
New Icon Image muestra los tamaños disponibles que puede añadir al icono. Para eliminar la
imagen actual del icono, haga clic en Delete Device Image en el menú Image.
El icono DiskPiel tiene tres tamaños de imágenes, que van desde 16 a 48 píxeles cua-
drados:
Normalmente, las imágenes en un icono de programa tienen el mismo dibujo pero dife-
rentes tamaños. Le di a las tres imágenes diferentes diseños para mostrar claramente
que Windows extrae la imagen correcta de los datos de recurso de DiskPiel en lugar de
únicamente ampliar la imagen de 32 por 32. Haga dos veces clic en cualquier parte sobre el
área de trabajo del editor para abrir el diálogo Icon Properties y asigne el identificador
IDRMAINFRAME al recurso, y a continuación guarde su trabajo.
Cursores de ratón
Las especificaciones para DiskPie1 no invocan para designar un cursor de ratón personaliza-
do, pero vamos a detenemos un momento aquí para ver cómo se hace en Visual C++. Un
cursor de ratón es un mapa de bits de 32 píxeles cuadrados monocromo con un fondo trans-
parente y una «zona activa». La zona activa es el único píxel del mapa de bits que Windows
reconoce como la coordenada de cursor. Cuando un programa recibe un mensaje
WMMOUSEMOVE o WM-LBUTTONDOWN, por ejemplo, las coordenadas del cursor x
e y mantenidas en el valor LParam del mensaje representan el píxel bajo la zona activa del
cursor:
case WM_LBUTTONDOWN:
x = LOWORD( lParam ); / / Coordenada X del clic del ratón
y = HIWORD( lParam ); / / Coordenada Y del clic del ratón
Para crear un cursor de ratón, elija New desde el menú File y haga dos veces clic en
Cursor File en la pestaña Files, o para un proyecto existente, elija Resource del menú Insert.
Haciendo dos veces clic en Cursor en la lista le proporciona una hoja en blanco sobre la que
puede diseñar su cursor nuevo. Si prefiere empezar con una imagen de cursor Windows
estándar, expanda la cabecera Cursor y elija IDC-NODROP, IDC-POINTER o IDC-
POINTER-COPY. El editor visualiza una área de trabajo de 32 píxeles cuadrados en los que
dibuja el cursor. Si ya tiene una imagen que desea utilizar pero está en otro formato, por
ejemplo un mapa de bits de 32 por 32, abra primero el mapa de bits en el editor de gráficos y
pulse CTRL+Cpara copiar su imagen al Portapapeles. A continuación abra el cursor nuevo y
pulse CTRL+Vpara pegar la imagen de mapa de bits en el área de trabajo del cursor. Los
colores del mapa de bits se han convertido en blanco o transparente, dependiendo de su
intensidad. Si no le gustan los resultados, pulse la tecla DEL para borrar la imagen del cursor.
Cuando el editor de gráficos carga una imagen de cursor, aparece un botón Set
Hotspot por encima de la ventana del área de trabajo. Puede ver el botón en la Figu-
ra 4.18 con las coordenadas (0, 13), colocando la zona activa en la nariz del roedor de
la imagen. Haga clic en el botón Set Hotspot, a continuación haga clic en el punto de
la cuadrícula de imagen donde desea ajustar la zona activa del cursor. Elija Save del menú
File cuando haya terminado, y Visual C++ guarda la imagen como archivo CUR, añadiendo
una definición de recurso al archivo RC de proyecto que aparece del modo siguiente:
Al ajustar el cursor nuevo como un cursor predeterminado de programa, implica una invo-
cación a la función API LoadCursor. En un programa C, esto se hace normalmente cuando
Figura 4.18. Creación de u n cursor d e ratón personalizado e n el editor d e gráficos
d e Visual C++.
Estos comandos hacen que Windows visualicen el cursor nuevo siempre que el punte-
ro del ratón esté colocado en el área de cliente de programa. La Figura 4.19 muestra el as-
136 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
pecto en un programa del cursor diseñado anteriormente (puede encontrar el código fuente
para este minúsculo programa C en la subcarpeta Codigo\Capitulo.04\Mouse en el CD que
se acompaña).
Igual para los cursores. Vamos a volver al programa DiskPiel.
En este punto DiskPie1 tiene un menú, teclas aceleradoras, barra de herramientas, barra de
estado, icono; todos los datos de recurso que necesita. Ahora es el momento de añadir
código para hacerlo funcionar. Si ha seguido la génesis de DiskPiel en este capítulo,
reconocerá los elementos de la interfaz de usuario en la Figura 4.20. El icono de aplica-
ción, el menú, la barra de herramientas y la barra de estado se diseñaron en secciones
anteriores utilizando los editores de recurso de Visual C++.
Durante la creación de su ventana principal, DiskPiel encuentra todos los discos fijos
adjuntos, incluyendo los discos remotos y los discos RAM, y para cada uno hace lo siguiente:
Añade un comando llamado Disk x al menú Chart, donde x es la letra de designación
de control.
(Continúa)
138 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
140 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
í Continúa)
142 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
( Continúa)
144 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
(Continúa)
146 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR 1
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
150 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Si quisiera seguir el fluido lógico del programa, los pasos importantes comienz n en el
1
módulo MainFrml.cpp. La función CMain::OnsetFocus se invoca siempre que ndows
envía un mensaje WM-SETFOCUS para informar a la ventana principal que se le resta la
atención de entrada. Esto ocurre cuando DiskPiel se inicia por primera vez siempr que el
usuario vuelve a cambiar a DiskPiel desde otra aplicación. La función sirve así /ara dos
propósitos. Ahorra a CMainFrame::OnCreate el problema de invocar DiskDoc::Ge Memo-
ryUsage para inicializar datos al inicio de un programa, y también le asegura que c p d o . el
usuario ejecuta otro programa o visualiza un archivo, el diagrama actual se vuelve a dibujar
automáticamente para reflejar las condiciones nuevas cuando DiskPiel vuelva a ener la
atención.
Dada una lista de unidades de disco adjuntas, la hnción CMainFram
en el menú Chart comandos tales como Disk C y Disk D para cada unida
añade botones de barra de herramientas para unidades, seleccionando la
mapa de bits de la barra de herramientas de acuerdo con la letra de desi
del disco. El botón para la unidad D, por ejemplo, está pintado con la s
mapa de bits que contiene la imagen de una unidad de disco y la letra D
de la barra de herramientas con sus secciones de 25 imágenes aparec
El usuario puede solicitar un diagrama para memoria o para una
medio de:
La elección de un comando desde el menú Chart.
Hacer clic en un botón de la barra de herramientas.
Pulsando CTRL+M para memoria, o pulsando cualquier letra de la C a la Z dara una
unidad de disco.
Estos eventos los manejan las funciones CMainFrarne::OnMemory y CMainFrapc::On-
Disk, que reciben control a través del mapa de mensaje de clase:
Puede que recuerde que cuando creamos las teclas aceleradoras de Dis
riormente en el capítulo, nos aseguramos de que los valores de los ide
IDM-DISK-C a IDM-DISK-Z estaban ordenados secuencialmente. El mapa
anterior muestra el porqué. Como los identificadores tienen valores secuenciale
ma puede utilizar la macro ON-COMMAND-RANGE de MFC para encamina
des de cualquier disco para la función OnDisk. Desde el identificador de disc
como parámetro, OnDisk determina el controlador para el que el usuario h
diagrama. OnMemory no requiere ON-COMMAND-RANGE porque sólo h
La forma de determinar memoria y uso de disco se restringe a dos
módulo DiskDoc.cpp. Las dos funciones, llamadas CDiskDoc::Ge
CDiskDoc::GetDiskUsage, emplean lógica similar. Recuperan la info
tan desde el sistema invocando las funciones API GlobalMemoryStatus y GetDiskFree-
Space, a continuación utilice la información para determinar valores para las variables de
miembro dwTota2 y dwFree, que contienen el número total de bytes libres para el diagra-
ma actual. No supone ninguna diferencia para la función trazada si la información en las
variables representa memoria o espacio de disco. Después de determinar los número de
uso actual, las funciones OnMemory y OnDisk invocan Invalidate para obligar a visualizar
un diagrama nuevo.
La escena cambia ahora a la función CDiskView::OnDraw en el archivo Disk-
View.cpp. Esta función utiliza los valores públicos dwTotal y dwFree para determinar los
ángulos de barrido para los dos fragmentos del diagrama de pastel:
OnDraw pinta el primer trozo «Used», barriendo el arco en sentido contrario a las
agujas del reloj desde la posición de las 12 por el ángulo dUseSweep. El trozo «Free» está
ligeramente desplazado desde el trozo «Used», trazado con un arco en el sentido de las
agujas del reloj de los radianes dFreeSweep. OnDraw adjunta las etiquetas a las dos sec-
ciones y visualiza el diagrama. La solicitud de otro diagrama inicia todo el proceso com-
pleto otra vez.
Un diagrama de pastel representa una imagen instantánea de una condición actual. El
único modo conveniente de actualizar un diagrama mientras que DiskPie 1 tiene la tención
es pulsar la tecla aceleradora para el diagrama. El uso de memoria en un sistema multitarea
preventivo como Windows es especialmente dinámico, cambiando cada microsegundo. Si
pulsa continuamente CTRL+M mientras que Visual C++ compila un proyecto en segundo
plano, puede ver los efectos de la asignación y desasignación de la memoria física. La
adición de una función OnTimer actualizaría la visualización de forma más regular.
Más seria es la incapaciadad de DiskPiel para encargarse de la dinámica de adjuntos a
la unidad de disco. Hemos visto cómo la función CMainFrame::OnCreate añade coman-
dos de menú y botones de barra de herramientas para unidades de disco adjuntas al siste-
ma, incluyendo cualquier unidad remota proporcionada a través de una red. El menú y la
barra de herramientas permanecen a continuación intactas a lo largo de la duración del
programa, incluso aunque el usuario pueda añadir o separar una unidad de red, o un adjun-
to pueda desaparecer debido a los problemas en el servidor final. Sin embargo, una situa-
ción semejante no es fatal; DiskPiel todavía funciona correctamente, porque CDisk-
D0c::GetDiskUsage siempre lista unidades antes de visualizar un diagrama. Una mejora
del programa podría añadir lógica o volver a comprobar los adjuntos a la unidad de disco
cuando actualice la visualización y añada o elimine comandos de menú y botones de barra
de herramienta apropiados.
lmage ImageBrurhLarger + Increasei lhe brush sire by one pixel in each diiectio
llmage ImageBruih0utlineTooI Shift+O Oullines h e bruth or relection with the current
.e to one pixel
mage IrnageBiushSmaller
mage ImageBiushTool D
mane ImaoeColorNext 1
END
La versión final del archivo DiskPiel.exe tiene un tamaño de 24.576 bytes. Echando por la
borda muchos de sus datos de recurso, el nuevo programa DiskPie2 se reduce hasta 17.920
bytes, disminuyendo el tamaño a casi un tercio sin cambiar apreciablemente el programa.
DiskPie2 adopta cuatro técnicas para reducir el tamaño de sus datos de recurso:
Con la excepción de un mensaje «Ready», el programa no contiene ninguna cadena
de solicitud en sus datos de recurso. Basándose en su lugar en cadenas de solicitud
proporcionadas por el archivo de biblioteca MFC, DiskPie2 visualiza descripciones
generales en su barra de estado para todos los comandos, excepto aquellos seleccio-
nados en el menú Chart.
i Sin cadenas de solicitud, DiskPie2 debe generar texto de herramientas de consejo
bajo petición. El código para generar las herramientas de consejo ocupa menos espa-
cio que las propias cadenas de herramientas de consejo, lo que implica una disminu-
ción general del tamaño de programa.
La imagen de icono de 48 x 48 tiene una tabla de color con 16, en lugar de 256
colores.
m El mapa de bits de la barra de herramientas de DiskPie2 tiene sólo dos imágenes, en
lugar de 25 que necesitaba DiskPiel.
Dos visualizaciones del mapa de bits de la barra de herramientas muestran las imáge-
nes utilizadas para los botones de barra de herramientas de DiskPie2:
Las imágenes duplican las dos primeras imágenes que DiskPiel utiliza para su barra de
herramientas, excepto que la segunda imagen no está etiquetada, haciéndola adecuada
para representar cualquier disco. DiskPie2 añade designaciones de disco tales como «C:»
y «D:» a los botones en tiempo de ejecución. Puede ver los resultados en la Figura 4.22.
El nuevo programa DiskPie2 necesita cambios para tres de los archivos fuente de
DiskPiel para crear versiones actualizadas, llamadas DiskPie2.rc, MainFrm2.h y Main
Frm2.cpp. El Listado 4.2 muestra el código revisado, en el que DiskPie2.r~se desprende
de las cadenas de solicitud innecesarias y MainFrm2.cpp recibe intrucciones añadidas para
etiquetar los botones de barras de herramientas y construir texto de herramientas de
consejo.
156 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
typedef struct
I
NMHDR hdr ;
LPTSTR IpszText ;
char szText [ 8 0 ] ;
HINSTANCE hinst ;
UINT uFlags;
} TOOLTIPTEXT, FAR *LPTOOLTIPTEXT;
STRINGTABLE
BEGIN
IDM-MEMORY "Memory usage"
IDM-DISK-C " C"
IDM-DISK-D "D"
IDM-DISK-X "X"
IDM-DISK-Y "Y"
IDM-DISK-Z "Z"
END
Este capítulo forma la segunda parte de nuestra explicación de datos de recursos, ini-
ciada en el capítulo anterior. Empezaremos echando un vistazo al guión de recurso que
define un diálogo en un archivo RC de proyecto, y a continuación nos introduciremos en el
editor de diálogo de Visual C++, que hace el diseño de diálogo tan fácil como señalar y
hacer clic. El capítulo ilustrará las habilidades del editor con distintos programas de ejem-
plo, uno de los cuales muestra cómo crear una hoja de propiedades, también conocido
como diálogo de pestañas.
EL EDITOR DE DIÁLOGO
En los albores de la programación de Windows, los desarrolladores tenían que diseñar una
visión de diálogo oculto escribiendo un guión en el archivo RC, a continuación compilan-
do y ejecutando el programa para ver cómo es verdaderamente el diálogo. Este proceso de
prueba y error normalmente necesitaba varias iteraciones para obtener controles de posi-
ción y funcionamiento correctos. Pero esto se ha terminado. Más que nada, es el editor de
recursos quien hace el «visual» de Visual C++, y una vez que comience a diseñar con el
editor de diálogo, nunca volverá al método antiguo. No sólo puede componer un diálogo
de calidad profesional con unos cuantos clics de ratón, viendo cómo toma forma en la
pantalla a medida que lo crea, sino que también puede probar un modelo de trabajo del
diálogo en el mismo editor. ¿Un control no aparece como debería? ¿Ha cambiado de
opinión sobre la tecla mnemónica? El editor hace de las revisiones un placer, y cuando
haya terminado sabrá cómo se comportará el diálogo exactamente y cómo será en el pro-
grama que ejecuta. Y como veremos en el capítulo siguiente, el editor de diálogo también
se integra bien con ClassWizard, que puede generar código automáticamente para iniciali-
zar y recuperar datos de los controles de diálogo.
Al igual que con otros recursos, puede encontrar a menudo más fácil hacer cambios
pequeños a un guión de diálogo existente editando el archivo RC con un editor de texto.
No hay nada malo en ello. Pero para la mayor parte de las revisiones y especialmente
cuando cree un diálogo nuevo, el editor de diálogo es su apuesta más segura. Como los
otros editores de recurso de Visual C++, el editor de diálogo se lanza de uno de dos modos,
dependiendo de si desea crear un diálogo nuevo o continuar trabajando en uno que ya
existe en el archivo RC del proyecto. Para crear un diálogo nuevo partiendo de cero, haga
cljc en Resource en el menú Insert para hacer aparecer el cuadro Insert Resource, a conti-
nuación seleccione Dialog de la lista:
La barra de herramientas Controls proporciona acceso de un clic a los controles que puede
colocar en la ventana de diálogo. Haga clic en el botón de la barra de herramientas para el
1 +$ -1 Acceleiator 1
+1 J lcon
E J Menu
a
C J String Table
+i 3
Toolbar
+l Veision
Selección Imagen
Cuadro de edición Cuadro de grupo
Cuadro de comprobación Botón circular
Cuadro de lista Barra desplaz. horizontal Barra desplaz. vertical
Rotación Indicador de proceso
Tecla activa Control de lista
Control de pestañas Animación
Selector fechalhora Calendario mensual
Control personalizado Cuadro de comprobación extend.
Cuando la cuadrícula esté visible en la ventana del diálogo, un control sólo se desplaza
de una línea de la cuadrícula a otra, un rasgo llamado a veces «ajustar a la cuadrícula». Por
defecto, el espaciado de cuadrícula vertical y horizontal es de cinco unidades de diálogo,
pero puede cambiar el espaciado en el diálogo Guide Settings que se muestra en la Figu-
ra 5.4. Haga clic en Guide Settings en el menú Layout para invocar el diálogo.
Si la característica ajustar a la cuadrícula le impide administrar los controles del modo
que desea, simplemente desactive la cuadrícula. Si más tarde la vuelve a activar, el editor
ya no le impedirá colocar las ventanas de control en el diálogo. Para suprimir temporal-
mente la característica ajustar a la cuadrícula, pulse la tecla ALT a medida que arrastra un
control.
Haga clic en la
herramienta Selección.
una ventana nueva que es un clon de la original, excepto que tiene su propio valor de
identificador.
L ~ a m b i aa regla o retícula
Hace el mismo tamaño, altura o ambos
Espacia horizontal o verticalmente
Centra horizontal o verticalmente
-Alinea a la izquierda, derecha, arriba o abajo
Como puede ver en la Figura 5.8, la barra de herramientas administra los botones en
cinco grupos lógicos para alinear, centrar, espaciar y ajustar el tamaño de los controles y
para activar y desactivar la cuadrícula y las guías de regla. El alineado, el espaciado y las
herramientas de ajuste de tamaño, que aparecen en gris en la Figura 5.8, están habilitadas
sólo cuando se seleccionan dos o más controles en el diálogo.
Las próximas secciones mostrarán algunos efectos de las herramientas Dialog. Nor-
malmente son necesarias varias herramientas para actualizar un grupo de controles en la
posición deseada, de modo que tenga que pensar también en el orden en que aplican las
herramientas. Sin embargo, no hay nada escrito en piedra sobre colocación, y si el efecto
de una herramienta no es lo que esperaba, simplemente haga clic en Undo del menú Edit.
Herramientas de alineación
Las herramientas de alineación alinean los controles de un grupo seleccionado con el
control dominante. Por ejemplo, haciendo clic en el botón Align Left, cambia las coorde-
nadas x de los controles seleccionados para que concuerden con la coordenada x del con-
trol dominante sin afectar a las coordenadas y:
Herramientas de espaciado
Las herramientas de espaciado sólo funcionan en un grupo seleccionado de tres o más
controles. Son únicas entre las herramientas Dialog en el sentido en que no diferencian
cuál de los controles seleccionados es el dominante. El espaciado horizontal cambia las
coordenadas x de todos los controles en el grupo, excepto los controles más a la izquierda
y más a la derecha, espaciando los otros controles del grupo uniformemente en dirección
horizontal. La herramienta de espaciado vertical hace lo mismo para las coordenadas y,
espaciando los controles uniformemente en la dirección vertical. En el ejemplo que se
muestra a continuación, la herramienta de espaciado vertical, a la que el editor le da el
confuso nombre de Space Down, vuelve a colocar sólo el botón Apply; los botones OK y
Cancel permanecen en su lugar.
Propiedades de control
Cada control colocado en el área de trabajo del diálogo tiene un cuadro de diálogo Proper-
ties en el que puede especificar un identificador y valor para el control, teclear en un rótulo
y ajustar las etiquetas de estilo apropiadas para la ventana de control. Para mostrar un
cuadro de control Properties, haga clic en el botón derecho del ratón en la ventana de
control en el área de trabajo y elija Properties del menú de contexto emergente. Puede
también seleccionar un control y hacer clic en el comando Properties en el menú View. El
diálogo Properties difiere ligeramente en aspecto y contenido para cada tipo de control; la
Figura 5.9 muestra cómo es para un control de cuadro de comprobación.
En el Capítulo 4 puede que se haya acostumbrado a hacer dos veces clic sobre un
elemento en el editor de gráficos de Visual C++ para invocar un cuadro Properties adecua-
Orden de tabulación
Las mnemónicas no son el único modo de centrar la atención de entrada sobre un control
particular. El usuario puede hacer clic en el control con el ratón o desplazarse mediante
tabuladores hasta el control pulsando repetidamente la tecla TAB. Cada vez que el sistema
178 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
detecta la tecla TAB, desplaza la atención de entrada al control siguiente en una jerarquía
llamada orden de tabulación.
El orden de tabulación es implícito por las sentencias de control en el guión de diálogo
del archivo RC. Por ejemplo, cuando el diálogo definido en el guión siguiente aparece prime-
ro, el control del cuadro de combinación IDC-COMBO1 tiene la atención de entrada. mil-
sando la tecla TAB desplaza la atención de entrada desde el control IDC-COMBO1 a otros
controles en el orden mostrado. Cuando el botón ID-CANCEL tiene la atención, pulsando la
tecla TAB otra vez inicia de nuevo el ciclo desde el principio de la lista IDC-COMBOI:
IDD-DEMO-DLG DIALOG O , 0, 247, 65
STYLE DSMODALFRAME 1 DS-3DLOOK 1 WS-POPUP 1 WS-VISIBLE 1 WS-CAPTION
CAPTION "Demo Dialog"
FONT 8, "MS Sans Serif"
BEGIN
COMBOBOX IDC-COMB01,15,15,159,80,WS~VSCROLL 1
WS-TABSTOP 1 CBS-DROPDOWN 1 CBS-AUTOHSCROLL
CONTROL "and",IDC~RADIO1,"Button",BS~AUTORADIOBUTTON 1
WS-GROUP 1 WS-TABSTOP,14,39,25,10
CONTROL "or",IDC~RAD102,"Button",BSSAUTORADIOBUTTON 1
WS_TABSTOP,44,39,20,10
COMBOBOX IDC-COMB02,69,39,106,80,WS-VSCROLL1 WSGROUP 1
WS-TABSTOP 1 CBS-DROPDOWN 1 CBS-AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,196,12,44,17,WS-GROUP
PUSHBUTTON "Cancel",ID-CANCEL,196,34,44,17,WS-GROUP
END
El editor de diálogo le aísla de los detalles de lo que pasa en el archivo RC, pero
todavía tiene que preocuparse sobre el orden de tabulación en un diálogo que consulta su
entrada de usuario. Comprobar el orden es normalmente lo último que haga en el editor
antes de pulsar sobre el interruptor de prueba en la barra de herramientas Dialog. Elija Tab
Order del menú Layout para visualizar su orden de tabulación actual de diálogo, que
aparece como números secuenciales adyacentes a los controles de diálogo. Aquí tiene
cómo es el área de trabajo para el diálogo definido en el guión anterior:
Para revisar el orden de tabulación, haga clic en cada control en una secuencia empe-
zando con el primer control, es decir, el control que desea que tenga la atención de entrada
cuando aparece el diálogo por primera vez. Si el orden existente es correcto sólo hasta un
cierto punto, puede que encuentre más fácil simplemente cambiar la parte que no estaba
bien. Pulse la tecla CTRL mientras que hace clic en la ventana de control que tiene el
número de orden de tabulación correcto más elevado, suelte a continuación la tecla CTRL y
continúe haciendo clic en los controles en la secuencia deseada hasta que el orden sea
correcto. Por ejemplo, para cambiar el orden de controles del 4 al 6, haga clic en el con-
trol 3 con la tecla CTRL pulsada, a continuación en orden secuencia1 haga clic en los
controles que desea que tengan números de tabulación de 4, 5 y 6. Pulse la tecla INTRO
para ajustar el orden y vuelva al modo de edición.
El orden de tabulación correcto es especialmente importante para los controles que
aparecen en grupos, como por ejemplo botones de radio y cuadros de comprobación. El
Capítulo 6, «ClassWizard», vuelve brevemente sobre el tema del orden de tabulación y al
papel que juega al hacer un grupo de botones de radio mutuamente exclusivos, de modo
que al seleccionar un botón en el grupo automáticamente deseleccione los otros.
Aquí tiene un ejemplo que demuestra algunas de las capacidades del editor de diálogo.
Supongamos que desea revisar el cuadro About introducido anteriormente en el capítulo
para el proyecto Demo. La Figura 5.10 muestra una posibilidad, un cuadro About algo más
elaborado para un programa igualmente ficticio llamado SpiffyDemo.
El logotipo de mapa de bits de acceso se creó anteriormente en el editor de gráficos de
Visual C++ y se guardó en un archivo llamado XYZCorp.bmp. Además, los cambios se
hicieron por completo en el editor de diálogo en sólo unos cuantos pasos y en unos cinco
minutos de trabajo. Si quisiera intentar revisar el cuadro About usted mismo, empiece con
un proyecto AppWizard desechable llamado Demo. A continuación siga los pasos resumi-
dos aquí:
1. Cree el logotipo. Haga clic en Resource en el menú Insert y elija Bitmap para
lanzar el editor de gráficos de Visual C++. Diseñe el mapa de bits del logotipo y
elija Properties del menú View para dar al recurso de mapa de bits nuevo un
identificador y especificar opcionalmente el nombre de archivo. Haga clic en Save
en el menú File cuando haya terminado.
2. Lance el editor de diálogo y cargue el cuadro About. En el panel Resource-
View de la ventana Workspace, haga dos veces clic en IDDABOUTBOX para
iniciar el editor de diálogo.
h D l g = C r e a t e D i a l o g ( h I n s t , MAKEINTRESOURCE ( I D D - D I A L O G l ) ,
hwnd, DlgProc ) ;
d e l e t e pDlg;
El programa Color presentado en esta sección visualiza diálogos no modales con tres
controles deslizadores (también llamados barras de seguimiento) que ajustan los compo-
nentes rojo, verde y azul del color de fondo de la ventana principal. A medida que desplaza
un deslizador «miniatura» con el ratón o las teclas de flecha, la ventana principal cambia
de color tomando el componente el color nuevo, que puede variar en valor desde O hasta
una elevada intensidad de 255. Con hardware de vídeo adecuado, puede visualizar en
teoría cualquiera de los 16.777.216 (2563) diferentes colores desplazando las barras desli-
zadoras. La Figura 5.11 muestra la ventana de programa con el diálogo no modal Color
visualizado.
Color es un programa MFC, pero no se creó con AppWizard (la sección siguiente
muestra cómo crear un diálogo nuevo en una aplicación AppWizard). El proyecto Color
empieza con la selección del icono Win32 Application en la pestaña Projects del diálogo
New. Teclee Color como nombre de proyecto y haga clic en OK. Inicie el editor de
diálogo haciendo clic en Resource en el menú Insert y haciendo dos veces clic en Dialog
en la lista de tipos de recuros. El diálogo nuevo se inicia con los botones OK y Cancel
predeterminados; seleccione los botones en el área de trabajo y pulse la tecla DEL para
suprimirlos.
-- El diálogo de Color contiene sólo tres tipos de control diferentes, creados con
2 e
las herramientas Static Text, Button y Slider de la barra de herramientas Controls
del editor. Las barras deslizadores forman tres líneas distintas en el diálogo, eti-
quetadas Red, Green y Blue en la Figura 5.11. El diálogo diseña invocaciones
para la creación de la línea Red (superior) colocando un control de deslizador
entre dos controles de texto estático, los cuales a continuación son alineados y
espaciados. Inicialice las capturas para los controles de texto a Red y x en el
diálogo Text Properties. La x sirve como un lugar en el que Color sobrescribe en
tiempo de ejecución con el valor actual de la posición miniatura deslizadora. Déle
al control deslizador un borde de límite estático y un botón miniatura punteado.
Estos estilos se ajustan en el diálogo Slider Properties, y se invocan haciendo clic
en el botón derecho sobre la ventana de control de deslizador en el diálogo y
eligiendo Properties:
1. En la pestaña Styles, seleccione BottorntRight en el cuadro Point para el estilo
miniatura.
2. En la pestaña Extended Styles, haga clic en el cuadro de comprobación Static Edge.
Las líneas Green y Blue son sólo clones de la línea Red, copiada seleccionando los tres
ontroles de la línea Red como un grupo, y arrastrando a continuación el grupo con la tecla
E
TRL pulsada, como se explicaba anteriormente en el capítulo.
En este punto, el área de trabajo del editor de diálogo es como el que se muestra seguida-
Al terminar el diálogo sólo necesita cambiar la segunda y tercera captura a Green y
los controles y añadiendo un botón de pulsación OK en la parte inferior de la
184 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Las capturas para los controles estáticos Color (los controles más a la izquierda) debe-
rían estar en Red, Green y Blue. La captura del control estático más a la derecha es x para
las tres líneas.
El código fuente de color requiere sólo un archivo principal, dos archivos de cabecera
llamados Res0urce.h y Co1or.h y un archivo de guión de recurso llamado Co1or.r~que
define el diálogo. Una sipnosis breve del programa empieza en la página 190, siguiendo el
listado fuente (Listado 5.1).
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Todo el trabajo importante del programa lo llevan a cabo dos clases, CMainFrame
para la ventana principal y un Cdialog derivado llamado CColorDlg que guía el diálogo no
modal. El constructor CColorDlg inicializa una matriz de valores de color para el color de
fondo de la ventana principal, que empieza como verde de intensidad media. Como los
componentes rojo, verde y azul del color de fondo de la ventana pueden variar en valor de
O a 255, la función OnInitDialog ajusta cada rango de desplazamiento de deslizador de O a
255. Esto significa que un número entero en ese rango representa la posición de un botón
miniatura en cualquier momento, determinando convenientemente el valor actual del com-
ponente de color.
Cuando el usuario mueve el deslizador miniatura, la función CColorDlg::OnHScroll
toma el control y deduce qué deslizador se está desplazando invocando GetDlgCtrlID. La
función invoca a continuación GetPos para obtener la nueva posición de la miniatura,
escribe ese valor a la matriz nColor y al control de texto estático en el diálogo e invoca la
función Invalidate.
La invocación a Invalidate hace que el sistema operativo envíe a la ventana principal
un mensjae WM-ERASEBKGND para indicarle la ventana que se vuelva a pintar por sí
misma. Este mensaje da a una aplicación como Color una oportunidad de pintar su propio
fondo. La mayor parte de las aplicaciones ignoran el mensaje WM-ERASEBKGND, en
cuyo caso Windows pinta la ventana con cualquier color de fondo predeterminado que el
programa especificó cuando creó la ventana (normalmente blanco). Color.cpp atrapa el
mensaje WM-ERASEBKGND en su función CMainFrame::OnEraseBkGnd,que declara
un objeto COLORREF basado en los componentes de color rojo, verde y azul almacena-
dos en la matriz nColor:
COLORREF rgbBackGnd = RGB ((BYTE) pColorDlg->nColor[O],
(BYTE) pColorDig->nC~l~r[l],
(BYTE) pC010rDlg->nColor[2]);
una forma rigurosa que puede ser un poco difícil de hacer coincidir con el estilo compacto
de Color.cpp. Para mostrarle exactamente lo que implica añadir un diálogo nuevo a un
programa AppWizard, esta sección muestra los pasos necesarios utilizando un programa
AppWizard de ejemplo llamado MfcTree.
El diálogo MfcTree, que se muestra en la Figura 5.12, no es nada atractivo; simple-
mente una visualización en árbol de algunas clases de MFC y un botón OK. Pero sirve para
los propósitos de ilustración, y se mejorará más adelante en este capítulo y de nuevo en el
Capítulo 13.
El programa se crea en cinco pasos:
1. Ejecute AppWizard para crear un proyecto MfcTree.
2. Cree el recurso de diálogo en el editor de diálogo.
3. Añada archivos fuente al proyecto para la clase de diálogo nueva.
4. Revise el menú.
5. Añada el código fuente necesario a los archivos fuente del proyecto.
Daremos estos pasos poco a poco.
Haga clic en New en el menú File y seleccione la pestaña Projects en el diálogo New. Elija
el icono (exe) AppWizard de MFC, déle el nombre de MfcTree al proyecto nuevo, y a
continuación haga clic en el botón OK para ejecutar AppWizard. Acepte los valores prede-
terminados de AppWizard, con estas excepciones: seleccione la Single Document Interfa-
ce en el paso 1 y deseleccione los cuadros de comprobación para el soporte de barras de
herramientas acopladas e impresión en el paso 4. Para guardar nombres de archivos cortos
en el CD que acompaña, se cambiaron los nombres de archivo MfcTreeDoc y MfcTree-
View a MfcDoc y MfcView, pero este paso es totalmente opcional.
Estos cambios de estilo añaden iconos más y menos grandes a la visualización de lista,
que cuando se hace clic comprime o expande niveles en su jerarquía. Desplácese al botón
OK en la parte inferior del diálogo y centre los controles que utilizan la herramienta de
espaciado horizontal descrita anteriormente en el capítulo. El diálogo nuevo es así en el
área de trabajo del editor:
Collapsed Node
Elija Save del menú File para guardar el guión de diálogo nuevo para el archivo
MfcTree.rc. Visual C++ automáticamente añade sentencias #define a los identificadores
194 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
nuevos para el archivo Res0urce.h. Cierre el editor de diálogo haciendo clic en el comando
Close del menú File.
Ahora tenemos un recurso de diálogo nuevo pero necesita una clase derivada de CDialog
para ejecutarla. En el capítulo siguiente veremos cómo utilizar ClassWizard para generar
una clase de diálogo de esqueleto y añadir automáticamente sus archivos al proyecto, pero
por ahora podemos hacer lo mismo utilizando el editor de texto. Cree archivos fuente
llamados MfcD1g.h y MfcDlg.cpp para contener la clase de diálogo nueva CMfcDlg (Lis-
tado 5.2). Sólo necesitamos estos escuetos archivos en este punto para añadir la clase
CMfcDlg al proyecto. Añadiremos código fuente a los archivos en el paso 5.
Para crear archivos fuente nuevos desde cero para un proyecto abierto como MfcTree,
deje el cursor en el comando Add To Project en el menú Project, a continuación haga clic
en New en el menú secundario que aparece. Seleccione el tipo de archivo que desea crear,
ya sea un archivo de cabecera o un archivo de implementación fuente, e introduzca un
nombre de archivo. Haga clic en OK para lanzar el editor de texto, que presenta una
ventana de documento en blanco.
Y - . .
MFC Trae W
,
MfcTree necesita un comando de menú para invocar el diálogo, pero no necesita todos los
demás comandos que AppWizard pone en los menús. Utilizando el editor de menú de
Visual C++ descrito en el Capítulo 4, modifique los menús de MfcTree para que sean del
modo siguiente:
BPtion* HaQ
r----'----
--..-.,.-
l..... ...--..... i
,
!"--"""."-"
Hejpb
i"--rr..,.ii,
&m& MfcTr
"--""--"."-"
!..--."-...-,""...--"
?,
e...
---,
%
196 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
BOOL CMfcD1g::OnInitDialogO
I
198 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
return TRUE;
}
Aunque el código anterior sea suficiente por ahora, no es muy eficaz. El programa
MfcTree3 introducido en el Capítulo 13, «Personalización de Visual C++», muestra un
método más claro para implementar una serie de invocaciones a Insertltem.
A continuación, edite los archivos MfcTree.cpp y MfcTree.h en el editor de texto para
añadir una función llamada OnMfcTree. La función de manejador OnMfcTree toma el
control cuando el usuario hace clic en el comando MFC Tree en el menú Options del
programa. Para añadir OnMfcTree, abra el documento MfcTree.cpp haciendo dos veces
clic en su nombre de archivo en el panel FileView de la ventana Workspace. Inserte la
línea siguiente en algún sitio cerca del principio del código fuente:
También, edite el mapa de mensaje en MfcTree.cpp de modo que aparezca del modo
siguiente:
BEGIN-MESSAGE-MAP(CMfcTreeApp, CWinApp)
//{{AFXMSG-MAP(CMfcTreeApp)
ON-COMMAND(1D-APP-ABOUT, OnApgAbout)
ON-COMMAND(1DM-OPTIONS-MFC, OnMfcTree)
/ / IIAFX-MSG-MAP
ENDMESSAGE-MAP ( )
void CMfcTreeApp::OnMfcTree()
{
CMfcDlg mfcDlg;
mfcDlg.DoModa1 ( ) ;
1
MfcTree ya está listo. Haga clic en el comando Set Active Configuration en el menú
Build y seleccione el destino Win32 Release, entonces construya y pruebe la aplicación.
CUADROS DE DIÁLOGO Y CONTROLES 199
APL~CACIONESBASADAS EN DIÁLOGO
Toda la acción en MfcTree está en el diálogo, no en la ventana principal. Sería más conve-
niente utilizar el programa si prescindiese de la ventana principal completamente y sólo
visualizase el diálogo, ahorrándole al usuario el trabajo de hacer clic en un comando de
menú para invocar el diálogo. Ha visto aplicaciones basadas en diálogo antes, las utilida-
des Mapa de caracteres, Calculadora y Marcador de teléfono que vienen con Windows son
todos ejemplos de cómo un programa puede interactuar de forma eficaz con el usuario a
través de un solo diálogo que sustituye a la ventana principal. En esta sección veremos
cómo escribir una aplicación basada en un diálogo en Visual C++ y construir un par de
programas de ejemplo para demostrar la técnica.
Una aplicación basada en un diálogo escrita en C no inicializa una estructura
WNDCLASS o WNDCLASSEX, no invoca RegisterClass para registrar la clase de venta-
na y no invoca CreateWindow para crear una ventana principal. No invoca ShowWindow y
UpdateWindow y no tiene ni siquiera un bucle de mensaje con llamadas a GetMessage y
DispatchMessage. No necesita todo esto; toda la interacción con el usuario tiene lugar en
el diálogo, y Windows es eso lo que maneja. El programa sólo necesita crear este diálogo:
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hInstPrev,
LPSTR szCmdLine, int nCmdShow )
I
DialogBox( hInst, MAKEINTRESOURCE (IDD-DIALOG),
NULL, DlgProc ) ;
return ( 0 ) ;
}
1. Ejecutar AppWizard
para crear el proyecto J r/
2. Crear o modificar
el diálogo J
4. Modificar el menú J
Los pasos 3 y 4, que crean una clase nueva para el diálogo y modifican el menú
principal, no son necesarias para la versión basada en diálogo de MfcTree. AppWizard
genera automáticamente archivos fuente esqueleto para la clase de diálogo, y si elimina la
ventana principal, también elimina la necesidad de un menú. Incluso el código fuente que
AppWizard produce es más fácil de modificar porque sólo necesita editar la función
CMfcDlg::OnlnitDialog, añadiendo las invocaciones Insertltem después de la línea «to
do» de la función. Como antes, el sombreado gris del fragmento inferior indica las nuevas
líneas fuente, que son las mismas que las añadidas a la versión original de MfcTree intro-
ducido anteriormente.
CUADROS DE DIÁLOGO Y CONTROLES 201
BOOL CMfcD1g::OnInitDialogO
La Figura 5.14 muestra cómo es la versión basada en diálogo del programa MfcTree.
En esencia, es el mismo programa que la versión original mostrada en la Figura 5.12,
excepto que se ha eliminado la ventana principal, la barra de título de diálogo contiene
ahora el icono de aplicación y el menú de sistema, y el programa es más fácil de crear y
utilizar. Si desea probar la versión nueva usted mismo, la encontrará en la carpeta Capitu-
lo.O5\MfcTree2 en el CD que se acompaña.
Control bars
Controls
Dialog boxes
Fra
m
Other windows
CPropertyS heet
CSplitterWnd
Views
para hacer las cosas un poco más interesantes, el diálogo que DirListl presenta al usuario
es una hoja de propiedades, llamada a menudo diálogo de pestañas.
Las hojas de propiedades pueden presentar mucha información sin abrumar al usuario,
resolviendo de forma ordenada el problema de saturación en un cuadro de diálogo. Pero en
esencia, una hoja de propiedad es sólo un diálogo, o más bien una serie de diálogos llama-
dos páginas, una superpuesta sobre la otra. Usted diseña cada página de una hoja de pro-
piedades con el editor de diálogo como lo haría con cualquier otro diálogo. El guión de
diálogo para una página en el archivo RC es igual que un diálogo normal, excepto que
todas las páginas de una hoja de propiedades tiene el mismo tamaño. Windows visualiza
los diálogos como páginas de hoja de propiedades cuando un programa invoca la función
API PropertySheet o crea un objeto CPropertySheet de MFC.
Puede que ya haya ejecutado una herramienta llamada Tab Control en la
barra de herramientas Controls del editor de diálogo. Este control visualiza un
conjunto de páginas de pestañas que pueden actuar como una hoja de propieda-
des dentro de un diálogo, pero no convierte su diálogo en una hoja de propiedades.
La Figura 5.15 muestra las páginas primera y segunda, etiquetadas Loca-
tion y Date, del diálogo de hoja de propiedades DirListl.
La pestaña Date hace uso del control nuevo Date-Time Picker, permitiendo al usuario
seleccionar fechas a través de un calendario emergente. Aunque los controles en las pági-
El parámetro pDir apunta a una cadena que termina en nulo y que contiene la ruta de
directorios. La constante IDC-LIST identifica el control de cuadro de lista que visualiza el
listado de directorios, y IDC-DIRPATH identifica un control estático para el que DlgDir-
List escribe la cadena de camino. DDL-ALL es una constante definida en el código fuente
que combina las etiquetas DLL-DRIVES, DLL-DIRECTORY y DLL-HIDDEN. Estas
etiquetas indican a DlgDirList que incluya discos, subdirectorios y archivos ocultos en la
lista de directorios. Al hacer clic en un disco o subdirectorio de la lista, cambia el camino y
actualiza la lista. El botón grande etiquetado Up 1 Directory Leve1 en la pestaña Location
permite al usuario volver a subir por la ruta.
El diálogo de hoja de propiedades en DirListl utiliza sólo cinco tipos de control:
botones de pulsar, botones de radio, un cuadro de lista, controles de edición y cuadros de
giro. Si está interesado en cómo se montan los controles, abra el archivo DirListl.rc y
lance el editor de diálogo haciendo dos veces clic en uno de los identificadores de diálogo
que se muestran a continuación:
E5 J lcon
El i-] String Table
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ficador de diálogo al inicializador base CPropertyPage. Por ejemplo, aquí tiene cómo
DirListl .h declara el objeto CPropertyPage para la primera página de la hoja de propie-
dades:
public :
CPagel ( ) : CPropertyPageí IDEPAGEl ) O
ACCEDER AL CLASSWIZARD
No puede acceder al ClassWizard desde un proyecto vacío. El proyecto debe tener al
menos un archivo RC adjunto, incluso si el archivo RC está vacío. Una vez que el archivo
RC está agregado a un proyecto (AppWizard hace esto automáticamente), puede invocar
el diálogo ClassWizard eligiendo el comando ClassWizard desde el menú View, como se
muestra en la Figura 6.1.
Puede que no sean obvios dos puntos acerca del ClassWizard: primero, sus servicios
son completamente opcionales. Puede desarrollar su proyecto desde el principio hasta el
final sin tratar en ningún momento con ClassWizard, si así lo prefiere; segundo, puede
utilizar ClassWizard para añadir clases nuevas a un proyecto MFC incluso si el proyecto
no se originó con AppWizard. ClassWizard compila una base de datos de las clases de
proyecto y la almacena en un archivo que tiene el mismo nombre que el proyecto con una
extensión CLW. Si siempre utiliza ClassWizard para crear clases nuevas para el proyecto,
el archivo CLW permanece actualizado. Sin embargo, Visual C++ no le restringe a una
relación de todo o nada con ClassWizard, sino que es libre de escribir una clase nueva suya
propia o copiar código de otros archivos fuente fuera del proyecto actual. En estos casos,
donde una clase se origina desde una fuente que no sea ClassWizard, hay un modo fácil de
actualizar la base de datos CLW. Después de que añada los archivos fuente de clase nueva
al proyecto utilizando el comando Add To Project en el menú Project, suprima el archivo
CLW e invoque ClassWizard otra vez. Visual C++ detecta que la base de datos no existe y
se ofrece para crearla otra vez:
Cuando haga clic en Sí para aceptar la oferta de construir una base de datos nueva,
aparece un diálogo titulado Select Source Files con una lista de la implementación y los
archivos de cabecera que Visual C++ leerá para construir una base de datos de clase. Si ha
introducido todos los archivos nuevos en el proyecto, la lista ya debería estar completa, así
que no necesita hacer nada más que clic en OK. Un indicador de progreso indica breve-
EL QIÁLOGO CLASSWIZARD
La Figura 6.2 muestra el diálogo principal ClassWizard. Invoca al diálogo «principal» por-
que ClassWizard puede manifestarse en alrededor de 20 diálogos diferentes, dependiendo de
las circunstancias. El diálogo de la Figura 6.2 actúa como una especie de entrada principal
para ClassWizard, y se llama ClassWizard de MFC para recordarle que sólo trata con clases
MFC. ClassWizard no le ayudará a crear una clase derivada de cualquier otra de las clases
MFC soportadas; para una clase con cualquier otra base, debe escribir el código usted mismo
partiendo desde cero utilizando el editor de texto o el comando New Class del menú Insert.
El comando New Class se describe con más detalle más adelante en el capítulo.
Las cinco pestañas del diálogo ClassWizard de MFC tienen propósitos muy diferentes
y no son en absoluto relevantes para ninguna clase particular. La Tabla 6.1 le ayuda a
determinar qué pestaña (o pestañas) necesita, dependiendo de lo que desee hacer para su
clase.
Las dos primeras pestañas del diálogo ClassWizard, etiquetadas Message Maps y Mem-
ber Variables, se describen en las dos secciones siguientes. Las explicaciones de las pestañas
Automation y ActiveX .se aplazan a los Capítulos 8 y 9, que muestran cómo ClassWizard
puede ayudarle en el desarrollo de proyectos que implican controles ActiveX.
Tabla 6.1. Las cinco pestañas del cuadro de diálogo MFC ClassWizard
Pestaña Propósito
están administrados de forma que cada control visualice un nivel superior en detalle pro-
gresivamente; en otras palabras, los contenidos de un control dependen de la selección
actual del control anterior. El cuadro de combinación Class Name enumera las clases del
proyecto seleccionado en el cuadro de combinación Project; el cuadro Object IDs. muestra
los identificadores asociados con la clase seleccionada en el cuadro Class Name, y el
cuadro Messages visualiza mensajes y otra información para la selección actual en el
cuadro Object IDs. La Tabla 6.2 muestra la relación entre el elemento seleccionado en
el cuadro Object Ids y los contenidos del cuadro Messages.
Cuando seleccione un mensaje o función virtual en el cuadro Messages, aparece una
descripción escueta del elemento seleccionado en la parte inferior del diálogo MFC Class-
Wizard. Para obtener información más detallada sobre el elemento seleccionado, cambie a la
ayuda interactiva MSDN como se describía en el Capítulo 1, «El entomo», y busque el índice.
Al contrario que otras versiones más antiguas de Visual C++, pulsando la tecla FI visualiza
la información general sobre el propio cuadro Messages, no el elemento seleccionado.
Para añadir una función manejador de un mensaje para la clase seleccionada, haga dos
veces clic en el mensaje o en la función virtual en el cuadro Messages. El cuadro Member
Functions contiene una lista de las funciones de clase actuales, que en la Figura 6.2 son
Initlnstance y OnAppAbout. La «W» identifica OnAppAbout como una función que mane-
Figura 6.3. El cuadro de diálogo Add Member Variable, invocado desde la pestaña
Member Variables.
222 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ca que la variable contiene los datos de control, como por ejemplo el texto o valor numéri-
co que el usuario teclea en un cuadro de edición. El ajuste Control significa que la variable
representa el propio control. Como ejemplo, considere una variable de miembro para com-
probar el control. Al seleccionar la categoría Value, hace la variable de miembro Boolean,
capaz de contener un valor TRUE o FALSE que indica el estado del cuadro de comproba-
ción. La selección de la categoría Control, sin embargo, hace de la variable un objeto
CButton, que representa el control del cuadro de comprobación. Si desea utilizar la varia-
ble para determinar si el usuario ha activado o desactivado el cuadro de comprobación,
seleccione la categoría Value. Seleccione la categoría Control si desea utilizar la variable
para alterar el cuadro de comprobación de algún modo en tiempo de ejecución, como por
ejemplo cambiando su captura invocando CButt0n::SetWindowText o ajustando el estado
de cuadro de comprobación invocando CButton::SetCheck.
La Tabla 6.3 enumera los tipos de variables disponibles para controles estándar. Aquí
tiene algunos puntos a tener en mente conforme lea la tabla:
Los cuadros de edición son especialmente adeptos a pasar datos a variables de mu-
chos tipos diferentes. El tipo de datos «numérico» mencionado en la tabla es un tér-
mino genérico que incluye BYTE, short, int, UINT, long, DWORD, float y double.
ClassWizard no enumera controles estáticos identificados por el valor genérico
IDC-STATIC. Para asociar un control estático con una variable, primero asigne al
control un identificador que no sea IDC-STATIC.
iDe un grupo de botones de radio, sólo el identificador para el primer botón de grupo
aparece en el cuadro Control IDs. La razón por la que otros botones del grupo no
están incluidos se hará más clara en el capítulo cuando expliquemos cómo una única
variable asociada con el primer botón de grupo representa al grupo entero.
MFC proporciona una variedad de funciones de intercambio de datos que desplazan los
datos entre controles y variables de miembro en una clase de diálogo. Junto con las funcio-
nes enumeradas en la Tabla 6.4, hay funciones de intercambio especializadas para datos
Obtienelajusta ...
para un control
Función de intercambio datos de este tipo
- - -
... de este tipo
-
recordset y datos devueltos por los controles ActiveX. La función DDX-Control transfiere
datos para varios tipos diferentes de controles, como por ejemplo Animate e IP Address.
Para obtener información detallada sobre las funciones de intercambio de datos, consulte
la ayuda en línea de Visual C++.
La función DDX-Radio que se enumera en la Tabla 6.4 le garantiza un poco más de
discusión. Esta función es única entre las funciones de intercambio de datos, en el sentido
de que se aplica a un grupo de controles en lugar de a un solo control. DDX-Radio devuel-
ve un valor int que indica qué botón de radio de entre un grupo ha activado el usuario;
O para el primer botón del grupo, 1 para el segundo botón y así sucesivamente. Un valor
- 1 significa que todos los botones en el grupo están eliminados. Puede invocar
DDX-Radio para determinar el estado de un solo botón de radio siempre que sea el único
botón en el grupo. En este caso, un valor devuelto de O significa que el botón está activado,
y un valor de - 1 significa que el botón está desactivado. El ajuste de un grupo de botones
de radio se hace normalmente en el editor de diálogo, como veremos en un momento.
Todas menos una de las funciones de validación de datos de diálogo monitorizan datos
numéricos, asegurando que un valor introducido por el usuario está entre los límites máxi-
mo y mínimo. La excepción es la función DDV-MaxChars, que verifica que el número de
caracteres tecleados en un control de edición o cuadro de combinación no exceda un
máximo dado. Al contrario que las funciones de intercambio enumeradas en la Tabla 6.4,
las funciones de validación entran en acción sólo cuando se cierra el diálogo, no cuando
aparecen en un principio. Si un valor introducido en un control está fuera de la categoría de
los límites especificados, la función de validación para el control visualiza un cuadro de
mensaje que informa al usuario del problema. Cuando se elimina el cuadro de mensaje, el
control ofensivo centra la atención, indicando al usuario que vuelva a introducir los datos.
El usuario no puede cerrar el diálogo haciendo clic en OK, a no ser que todas las funciones
de validación de datos hayan sido completadas.
Tabla 6.5. Diálogo de funciones de validación de datos
La función DoDataExchange
DoDataExchange es una función completa, no código matriz al que deba añadirle más
cosas. Como el marco de trabajo invoca DoDataExchange cuando el diálogo empieza y
termina, es a veces conveniente añadir inicialización y eliminar código para ello, de lo
contrario normalmente puede olvidar que la función existe. Si elimina una variable en la
pestaña Member Variables, ClassWizard elimina cualquier invocación de intercambio y
validación de datos para la variable en la función DoDataExchange.
ClassWizard y AppWizard escriben una función DoDataExchange para cada diálogo
en un proyecto, incluso diálogos que no tengan controles que acepten entrada de usuario.
Por ejemplo, la clase de cuadro About generada por AppWizard tiene la función Do-
DataExchange incluso aunque ninguno de los controles de diálogo -un icono, dos líneas
de texto estático y un botón de pulsación- puedan recibir datos del usuario. Si desea que
un control simplemente visualice datos sin permitir al usuario cambiar un valor, debería
borrar cualquiera de las invocaciones de función DDX/DDV de control que ClassWizard
añade a DoDataExchange. Otra opción es eliminar la función DoDataExchange completa-
mente, como veremos en un ejemplo posterior de este capítulo.
226 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Cuando se crea un diálogo, debería pensar en los primeros estadios del proceso de
diseño sobre cómo la clase de diálogo incorporará el intercambio y validación de datos de
diálogo. Como puede que recuerde del Capítulo 5, el editor de diálogo le permite ajustar
propiedades de cada control seleccionando y deseleccionando cuadros de comprobación
apropiados en el cuadro de control Properties. Cuando ajuste propiedades para un control
que utilice intercambio y validación de datos de diálogo, tenga presente estos puntos:
Para un control de edición que acepte sólo números enteros simples de base decimal
(en lugar de base hexadecimal), seleccione el cuadro de comprobación Number en la
pestaña Styles del diálogo Edit Properties. Esto le da al control una etiqueta de estilo
de ESNUMBER, haciendo que ignore cualquier carácter que no sea un dígito del O
al 9, incluyendo comas y puntos.
Si un cuadro de combinación o cuadro de lista acepta sólo datos numéricos formados
libremente, acceda al diálogo de control Properties y desactive la opción de clasifi-
cación. Para un cuadro de combinación y cuadro de lista normal, deseleccione el
cuadro de comprobación Sort en la pestaña Styles; para un control de lista, asegúrese
de que la selección Sort es None en la pestaña Styles. Como estos controles clasifi-
can números por los valores ASCII de los dígitos, no valores numéricos, clasifican
una lista de números correctamente sólo cuando las entradas tengan un número fija-
do de dígitos e incluyan ceros a la izquierda, como por ejemplo 001 y 099.
Cuando cree un grupo de botones de radio, ajuste la propiedad Auto para cada botón
en el grupo. Esto hace que los botones de radio se excluyan mutuamente de modo
que al hacer clic en un botón borre automáticamente todos los demás del grupo.
Ajuste la propiedad Group sólo para el primer botón de radio de un grupo y asegúre-
se de que todos los demás botones del grupo sigan el orden de tabulación secuencial.
El fracaso al hacer esto desactiva la posibilidad del diálogo para desplazar la aten-
ción desde un botón de radio al siguiente conforme el usuario pulse las teclas de
flecha. También instale la propiedad Group para el control que sigue inmediatamen-
te al último botón de radio de un grupo. Esto señala el final del grupo anterior y el
principio de otro. Ejecutar el programa en el depurador le indicará cuándo un grupo
de botones de radio no está propiamente delimitado por propiedades Group, porque
uando aparece el diálogo el depurador visualiza este mensaje en la ventana Output:
Warning: skipping non-radio button in group.
Para nombrar los archivos fuente de clase, ClassWizard añade extensiones de archivo
CPP y H al nombre de clase que especifica, menos cualquier prefijo «C». Si prefiere otro
nombre para cualquier archivo, haga clic en el botón Change.
Si el control Dialog ID está activo en el diálogo New Class depende de la clase de base
seleccionada. Cuando seleccione como base una de las cinco clases de diálogo MFC
(CDialog, CPropertyPage, CFormView, CRecordView o CDaoRecordView), el control
Dialog ID se vuelve activo, solicitando el identificador de recurso del diálogo asociado
con la clase nueva. El mejor medio de crear una clase basada en diálogo es diseñar y
guardar el recurso de diálogo en el editor de diálogo en primer lugar y a continuación,
mientras que el editor esté todavía activo y tenga la atención de entrada, acceder al Class-
Wizard para crear la clase nueva para el diálogo. Haremos exactamente eso para un ejem-
plo más adelante en el capítulo.
El aspecto de los botones de radio en el cuadro de grupo Automation depende de si la
clase de base seleccionada soporta Automation. Cuando seleccione una clase de base,
como por ejemplo CHttpFilter, un mensaje discreto le informa de que Automation no es
una opción. Sin embargo, para las clases de base que soportan Automation, el botón de
radio Automation está activado. Activando este botón, le indica a ClassWizard para que
escriba código para los archivos fuente generados que hacen de la clase nueva un objeto
programable, visible a las aplicaciones de cliente Automation, como por ejemplo Micro-
soft Excel. Si quisiera más información sobre Automation, Programación avanzada con
Visual C++, de David Kruglinski, dedica un lúcido capítulo al tema, completo de referen-
cias a ClassWizard.
Haciendo clic en el botón de radio Createable By Type ID, genera código que permite
a otras aplicaciones ActiveX crear un objeto Automation de su clase nueva. ClassWizard
combina automáticamente los nombres del proyecto y la clase para formar el identificador
de tipo que se muestra en el control de edición, un esquema que le ayuda a asegurar que el
identificador es único. El identificador tipo, también conocido como identificador progra-
mático, se puede utilizar por una aplicación de cliente de ActiveX para especificar el
objeto. Una macro Excel, por ejemplo, puede crear un objeto de CNewClass como este:
Class, que hace que Visual C++ genere un código matriz para una clase que no sea deriva-
da de MFC. Visual C++ está sólo tomando prestada tecnología del ClassWizard para esta
hazaña, y si prefiere ver el comando New Class como otra parte de ClassWizard, no estoy
en desacuerdo. Pero tenga presente que la variación del diálogo New Class trazada en la
Figura 6.4 es sólo para clases derivadas de MFC, para las que siempre puede aplicar un
potencial entero de ClassWizard, una herramienta diseñada desde el principio para MFC.
La creación de una clase genérica que no sea MFC con el comando New Class genera
código matriz, pero deja a la clase huérfana de otros rasgos de ClassWizard.
El ajuste de Generic Class en el diálogo activa un cuadro de lista desde el que puede
seleccionar una base para la clase nueva. El código fuente generado consiste en sólo fun-
ciones matriz para la clase constructora y destructora, contenida en un archivo CPP y el
archivo H nombrado para la clase. Por ejemplo, aquí tiene la declaración de que el coman-
do New Class escribe al archivo de cabecera para una clase derivada desde la CBaseClass
ficticia:
Visual C++ ofrece otros derivados de ClassWizard además del comando New Class.
Como veremos a continuación, la WizardBar actúa como un tipo de puerta lateral para
ClassWizard que es a menudo más conveniente que la entrada principal.
230 MlCROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
LA WIZARDBAR
La WizardBar es una barra de herramientas acoplable que hace un seguimiento de la
posición del signo de intercalación o de la selección actual a medida que se desplaza en los
editores de diálogo y de texto. La barra ajusta continuamente su aspecto y opciones para
reflejar cualquier clase con la que esté tratando actualmente. Al abrir el archivo de imple-
mentación para la clase CMainFrame, por ejemplo, inicializa automáticamente la Wizard-
Bar para esa clase, ofreciendo un medio conveniente de navegar rápidamente por las de-
claraciones y definiciones de funciones de miembro. Active y desactive la WizardBar
como lo haría con cualquiera de las otras barras de herramientas de Visual C++, haciendo
clic en el comando Customize del menú Tools y seleccionando el cuadro de comprobación
apropiado en la pestaña Toolbars. La Figura 6.6 muestra una visualización típica de la
WizardBar.
Los tres cuadros de combinación de la WizardBar encapsulan información visualizada
en la pestaña Message Maps del diálogo ClassWizard de MFC, y cualquier cambio hecho
en ClassWizard se refleja instantáneamente en la WizardBar. La Tabla 6.6 describe los
cuadros y botones de la WizardBar. El botón de flecha más a la derecha de la WizardBar
visualiza el menú desplegable de opciones que se muestran en la Figura 6.7.
Los contenidos del menú reflejan el documento actualmente activo, de modo que los
comandos tales como Go To Next Function están disponibles sólo cuando un documento
fuente está abierto en el editor de texto. En las descripciones siguientes de los comandos
de menú, la palabra «actual» se refiere a los ajustes en la WizardBar. La clase actual, por
ejemplo, es la clase que se visualiza en el cuadro Classes de WizardBar en la Figura 6.6.
Go To Function Definition. Abre el archivo CPP fuente si es necesario y coloca el
signo de intercalación en la primera línea de la función actual, identificada en el
cuadro Functions de la WizardBar.
Go To Function Declaration. Coloca el signo de intercalación en el prototipo de
función actual.
m Add Windows Message Handler. Invoca el diálogo New Windows Message
Handler. Este diálogo le permite añadir rápidamente una función matriz de maneja-
dor de mensaje a una clase de ventana que desciende de Cwnd.
Add Virtual Function. Anula una función virtual de la clase de base. Este comando
visualiza dos listas, una que contiene funciones virtuales que están disponibles para
anular, y la otra que muestra esas funciones que ya están anuladas por la clase actual.
Las listas proporcionan la misma información que el cuadro Messages en el
Control de la
WizardBar Descripción
Figura 6.7. El menú WizardBar, que se visualiza haciendo clic en el botón de flecha
de la WizardBar.
232 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
diálogo ClassWizard de MFC, pero son más fáciles de utilizar y más convenientes
para navegar.
Add Member Function. Añade una función de miembro matriz a la clase actual.
Introduzca el tipo devuelto de función, declaración y etiqueta de acceso como se
muestra a continuación.
Cuando hace clic en el botón OK, visual C++ añade tanto el código de declaración
como el de definición a la función nueva para la clase de archivos fuente:
CÓM~
CLASSWIZARD RECONOCE LAS CLASES
El archivo de base de datos CLW está en formato de texto ASCII, así que puede que esté
interesado en leerlo, utilizando el editor de texto. La base de datos desglosa cada clase en
el proyecto, manteniendo un registro de la base de clase y los archivos fuente. Los datos de
recurso, como por ejemplo los diálogos, menús y aceleradores, también se desglosan en el
archivo junto con sus identificadores.
Para construir el archivo de base de datos CLW, Visual C++ escanea cada archivo
fuente adjunto al proyecto y busca líneas especiales comentadas. Ha visto estos comenta-
rios antes en los programas de AppWizard; empiezan con //{{ o //} }, actuando como
paréntesis que marcan declaraciones, entradas de mapa de mensaje y otro código que
pertenezca a los miembros de clase. Los comentarios no tienen otro propósito que identifi-
car información de clase para su inclusión en la base de datos CLW. Si escribe una clase
sin ClassWizard pero más tarde desea utilizar ClassWizard para añadir otras funciones o
datos a la clase, debe incluir las líneas comentadas como se describe a continuación. De lo
contrario, los comentarios no son necesarios.
Cada delimitador de comentario contiene una de las trece palabras clave enumeradas
en la Tabla 6.7. La mayor parte de las palabras clave se utilizan por pares, una palabra
clave marca una declaración en el archivo de cabecera de clase, mientras que su homóloga,
que tiene un sufijo -MAP, marca una entrada correspondiente en un mapa de mensaje en
el archivo CPP. Por ejemplo, aquí tiene cómo Visual C++ reconoce una función de mane-
jador de mensaje en una clase llamada CDemoApp, derivada de CWinApp de MFC. En el
archivo de implementación CPP, los comentarios especiales de AFX-MSG-MAP ponen
entre paréntesis una entrada de mapa de mensaje para la función de manejador CDe-
woApp::OnAppAbout, haciendo que la entrada sea reconocible por ClassWizard:
234 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
public :
//{{AFXMSG(CDemoApp)
a f x m s g v o i d OnAppAboutO;
//l}AFXMSG
DECLAREMESSAGE-MAPO
Con esta información registrada en la base de datos CLW, ClassWizard sabe que la
clase CDemoApp contiene una función miembro llamada OnAppAbout que se invoca
cuando el programa recibe un identificador ID-APP-ABOUT contenido en el mensaje
WM-COMMAND. El prefijo afx-msg en el prototipo de función está incluido para bene-
ficio de ClassWizard; por lo demás no es necesario.
ClassWizard y AppWizard añaden automáticamente los comentarios de delimitador
correctos cuando generan código fuente, aislándoles de estos detalles. Pero si desea utili-
zar ClassWizard en un proyecto que no originó con AppWizard, la Tabla 6.7 mues-
tra cómo convertir los archivos fuente de clase existente para hacerlos reconocibles para
ClassWizard. La tabla describe los tipos de funciones y declaraciones señaladas por
las palabras clave de comentario e indica en qué archivo fuente se utiliza una palabra
clave.
Es posible añadir una clase para la cual el código fuente ya existe. Por ejemplo, puede
que ya haya escrito la clase de diálogo antes de crear y guardar el recurso en el editor de
diálogo. En ese caso, haría clic en el botón de radio Select An Existing Class para adjuntar
236 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
el diálogo a la clase e impedir que ClassWizard genere archivos fuente CPP y H. Para esta
demostración, sin embargo, el botón de radio Create A New Class es la elección segura, ya
que la clase CMfcDlg no existe todavía.
Al hacer clic en el botón OK, abre el familiar diálogo New Class. Introduzca CMfcDlg
en el cuadro Name para dar un nombre a la clase nueva. Sáltese el botón Change para
aceptar los nombres predeterminados de MfcDlg.cpp y MfcD1g.h que ClassWizard propone
para los archivos fuente. Como ya sabe que estamos creando una clase para el diálogo nuevo,
ClassWizard ya ha seleccionado CDialog como la clase base. También ha rellenado el cuadro
Dialog ID con el identificador de diálogo IDD-MFC-DIALOG, que el editor de diálogo
escribió al archivo MfcTree.rc. La Figura 6.8 muestra cómo debería ser el diálogo New Class.
Haga clic en el botón OK para cerrar el diálogo New Class y descubra el diálogo
ClassWizard de MFC que se muestra en la Figura 6.9. Aquí es donde genera el código
esqueleto para la clase CMfcDlg nueva. CMfcDlg necesita sólo una función OnInitDialog,
que obtiene el control justo antes de que aparezca el diálogo IDD-MFC-DIALOG. En
el cuadro Object IDs, seleccione CMfcDlg, y a continuación haga dos veces clic en
WM-INITDIALOG en el cuadro Messages para añadir la función OnInitDialog.
Los nombres de dos funciones miembro que cree ClassWizard aparecen en el cuadro
' Member Functions en la parte inferior del diálogo. El elemento seleccionado en el cuadro
Si hace clic en el botón Sí, está de acuerdo con ClassWizard en que borrará el código
fuente de DoDataExchange en el editor de texto. De lo contrario, corre el riesgo de definir
dos veces DoDataExchange si decide más tarde volver a añadir la función al archivo
MfcDlg.cpp. Cuando ClassWizard añade una función de miembro nuevo, no escanea pri-
mero el archivo para ver si la función existe ya; sólo escribe la nueva función shell:
preempaquetados tienden a ser más rápidos que los que crea usted mismo, porque pueden
automatizar su proceso entero añadiendo un componente a un proyecto. Al operar como
bibliotecas ejecutables que carga y ejecuta el entorno de Visual C++, estos paquetes profe-
sionales pueden insertar recursos de gráficos y volver a escribir los archivos existentes del
proyecto añadiendo funciones y sentencias #include conforme sea necesario; normalmen-
te hasta el punto donde quede poca o ninguna programación que realizar. Y los componen-
tes preempaquetados a menudo proporcionan su propia ayuda en línea.
La Gallery visualiza sus elementos en el diálogo Components And Controls Gallery
que se muestra en la Figura 7.1. Cuando un proyecto de Visual C++ está abierto en el
entorno, puede acceder al diálogo Gallery dejando el cursor momentáneamente en el co-
mando Add To Project del menú Project. Esto visualiza un menú desplegable secundario
desde el que puede elegir el comando Components And Controls. Si el botón More Info de
diálogo está habilitado cuando selecciona un componente de la lista, el componente puede
describirse a sí mismo a través de su propia ayuda interactiva. Para insertar un componente
Gallery en su proyecto, seleccione su icono y haga clic en el botón Insert.
En la versión 4 de Visual C++, la Gallery (entonces conocida como Component
Gallery) era capaz de volverse muy poblada si creaba proyectos regularmente con App-
Wizard. Cada vez que AppWizard se ejecutaba, automáticamente añadía una categoría
nueva a Component Gallery e instalaba todas las clases de proyecto, como por ejemplo
CMainFrame y CAboutDlg, como componentes de código fuente. Por defecto, Class-
Wizard también añadía a la categoría de proyecto cualquier clase nueva que creara. La
responsabilidad de eliminar ocasionalmente las adiciones no deseadas en la Component
Gallery corría a cargo del usuario, aunque muchos programadores simplemente ignoraban
la lista que se expande de componentes o no eran conscientes de lo que AppWizard y
ClassWizard estaban haciendo entre bastidores. La base de datos Component Gallery esta-
ba almacenada en un solo archivo llamado Gallery.dat, así que no había inconveniente de
compartir componentes con otros desarrolladores.
Clipboard
assistant
Una pantalla de presentación es una imagen de mapa de bits que aparece brevemente al
inicio del programa. Los programas grandes de Windows (como por ejemplo Visual C++ e
incluso el propio Windows) a menudo soportan una pantalla de presentación antes de
Figura 7.2. El programa Gadgets con su página de propiedad visualizada.
visualizar una pantalla principal mientras que se cargan archivos y se llevan a cabo otros
procedimientos de inicialización. Esto no sólo le da al usuario algún atractivo a lo que
mirar mientras que el programa esté ocupado, también aporta una impresión de receptivi-
dad. El programa Gadgets es tan pequeño que realmente no necesita una pantalla de pre-
sentación, pero esto no debería detenernos. Vamos a añadir una barra de estado con un
reloj mientras que estamos en ello. No creerá lo fácil que es todo esto.
Los componentes que necesitamos, llamados Splash Screen y Status Bar, están identi-
ficados con estos iconos grandes en la carpeta Components de Visual C++ del diálogo
Gallery:
Añada cada componente al proyecto Gadgets haciendo dos veces clic en el icono de
componente. El componente Status Bar corre un diálogo que le permite elegir si la barra de
estado nueva visualiza la fecha y la hora. Para incluir la hora en la barra de estado, haga
clic en el botón de radio Use System Default en el paso segundo (acepte los ajustes prede-
terminados para los otros pasos). Cuando cierre Gallery, encontrará código que se ha
añadido al archivo MainFrm.cpp que crea una barra de estado con un reloj pequeño en la
parte más a la derecha. Sólo necesita añadir esta línea sombreada al mapa de mensaje en
MainFrm.cpp:
244 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
SetTimer(1, 7 5 0 , N U L L ) ;
en el archivo Splash.cpp. Para ver los componentes nuevos en acción, reconstruya el pro-
grama Gadgets y ejecútelo. La Figura 7.3 muestra cómo es el programa nuevo.
Los componentes personalizados que crea tienen ciertas limitaciones. Por alguna ra-
zón, el botón More Info en el diálogo Gallery está inactivo para los componentes persona-
lizado~porque no hay un modo directo de añadir ayuda en línea para explicar cómo fun-
ciona el componente. Tampoco puede un componente personalizado modificar
automáticamente los archivos fuente existentes en un proyecto del mismo modo que lo
hacen los componentes de Property Sheet y Splash Screen. La creación de componentes de
calidad profesional y para el mercado de software requiere el Component Builder's Kit de
Microsoft, lo que le permite crear componentes ejecutables que pueden utilizar el sistema
de ayuda en línea. Microsoft no le cobra por el conjunto, pero actualmente está disponible
sólo en compañías de software, no para particulares. Para solicitar una copia del Builder's
Kit, envíe una solicitud con el membrete de su compañía a:
Visual C++ Manager
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052-6399
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
(Continúa)
250 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Parámetro Descripción
El invocador proporciona en sus datos de recurso hasta cinco imágenes de icono, cada
16 píxeles cuadrados, que representan los distintos tipos de controladores y nombres de
archivos visualizados en el control de visualización de lista. La Figura 7.5 ilustra las imá-
genes de icono posibles, que incluyen una disquetera, disco duro, disco de CD-ROM,
carpeta de archivo y una pequeña forma de diamante para representar archivos. Create
adjunta cada icono al control invocando la función CImageList::Add, empezando con el
icono de disquetera identificado por el parámetro idilcon. Los identificadores para las
imágenes de icono siguen el orden secuencial, así que idilcon+l es el identificador para la
imagen de disco duro, idiIcon+2 es el identificador para la imagen de disco de CD-ROM,
y así sucesivamente. Los iconos son opcionales y no es necesario que aparezcan.
Antes de que existan, la función Create invoca CListBox::Create para ajustar un con-
trol de cuadro de lista invisible llamado ListDummy. El cuadro de lista ListDummy nunca se
visualiza, sirviendo sólo como una papelera de almacenamiento intermedia para los nom-
bres de archivo que forman parte del listado de directorios. El nombre de cada archivo y
carpeta que la función FindFiles localiza en el directorio se añade al cuadro de lista list-
Dummy. Como el cuadro de lista se crea con la etiqueta LBS-SORT, clasifica automática-
mente su colección de cadenas conforme recibe cada nombre de archivo y de carpeta.
Cuando la función ShowList extrae las cadenas del cuadro de lista, el cuadro de lista
entrega las cadenas una a una por orden alfabético.
La función en línea SetCallBack toma la dirección de una rutina opcional de retrolla-
mada. Si la dirección pCallBack no es nula, la función FindFiles presupone que la rutina
de retrollamada existe y la invoca para cada archivo añadiendo el nombre de archivo al
cuadro de lista ListDummy:
bOkay = (pCallBack) ? pCallBack( &fd ) : TRUE;
if (bOkay)
listDummy.SendMessage( LBADDSTRING, O , (LPARAM) fd.cFileName );
Supone algo de trabajo extra, pero puede adornar el aspecto sencillo del componente.
El primer paso es eliminar el archivo OGX fuera de la carpeta DirCtrl y reemplazarlo por
un archivo de atajo. Haga clic en el botón derecho del ratón en el icono nuevo DirList-
Ctrl.ogx y elija el comando Cut del menú contextual. Cree una nueva subcarpeta en la
carpeta nueva del diálogo de la Gallery haciendo clic en el botón derecho del ratón en el
área en blanco del cuadro de lista grande y elija el comando New. Déle a la carpeta nueva
un nombre genérico como OGX Files, a continuación pegue el archivo DirListCtrl.ogx en
la carpeta nueva. Haga clic en el botón derecho del ratón sobre el icono DirListCtrl.ogx,
elija el comando Create Shortcut del menú contextual, a continuación corte y pegue el
archivo de atajo de nuevo en la carpeta DirCtrl.
Confíe en mí; casi hemos terminado. A estas alturas, el archivo OGX original se ha
desplazado a la carpeta llamada OGX Files y reemplazado por el atajo en la carpeta
DirCtrl. Nuestro paso final es mejorar el aspecto del icono de atajo en la carpeta DirCtrl.
Dé al icono de atajo un nombre más descriptivo -Directory List, por ejemplo- haciendo
clic en el botón de la derecha del ratón sobre el icono y eligiendo Rename. Los archivos
OGX de componente no tienen sus propios iconos, pero los enlaces de atajo sí. La razón
principal para sustituir un atajo por el archivo OGX original es que pueden adjuntar un
icono para distinguir el componente Directory List nuevo. Haga clic en el botón derecho
del ratón en el icono Directory List, elija Properties, y a continuación haga clic en el botón
Change Icon en la pestaña Shortcut del diálogo Properties. Introduzca (o explore) el cami-
no al archivo DirCtrl.ico que copió antes del CD que acompaña. Cuando hace clic en OK
para cerrar el diálogo Properties, el componente ahora aparece del modo siguiente en la
carpeta DirCtrl:
E
m
Directory List
Un poco de trabajo, por supuesto, pero el componente tiene un aspecto mejor que
cuando lo empezamos. Si no le importa hacer este esfuerzo, puede dejar todos sus archivos
de componente nuevos en la carpeta OGX Files y reemplazarlos con atajos del mismo
modo.
Desafortunadamente, Visual C++ no proporciona un modo fácil de adjuntar una des-
cripción a un componente de clase nueva, ni en el archivo OGX ni en su atajo. Es decisión
del nombre de archivo expresar a los otros desarrolladores lo básico de su propósito de
componente. La creación de un componente que puede describir usted mismo requiere el
Components Builder's Kit citado anteriormente. La Figura 7.1 muestra cómo aparece una
descripción en la Gallery cuando está seleccionado el componente de barra Dialog. Aun-
que más allá del ámbito de este capítulo, la construcción del componente Directory List
utilizando el Builder's Kit nos permitiría adjuntar una descripción similar que aparece
cuando el icono DIR está seleccionado en el diálogo, algo así como:
visualizar sólo archivos creados dentro del mes pasado que tienen, por ejemplo, entre 5 y
10 Kb de tamaño. Esta filtración adicional se realiza utilizando el rasgo de retrollamada
CDirListCtrl descrito anteriormente.
Con adiciones menores a las que llegaremos en un momento, el programa DirList2
utiliza el mismo archivo Res0urce.h que el mismo archivo RC que el programa DirList.
Los contenidos de estos archivos están enumerados en el Capítulo 5. Si quisiera construir
el programa DirList2 sin ejecutar el programa Setup para instalar el proyecto de ejemplo
del CD que se acompaña, haga clic en New en el menú File y seleccione la pestaña Pro-
jects. Como DirList2 no es un programa de AppWizard, haga clic en el icono Win32
Application para crear el proyecto. Introduzca el nombre de proyecto y acepte los ajustes
predeterminados cuando aparezca el diálogo del asistente. Después de que Visual C++
cree el proyecto vacío, haga clic en Settings en el menú Project. En la pestaña General del
diálogo Project Settings, seleccione la opción Use MFC In A Shared DLL, como se mues-
tra a continuación:
DirList2 opera de forma muy parecida a DirListl, excepto que la clase Cpagel incluye
una función de miembro nueva llamada CheckDateSize. CheckDateSize es una función de
retrollamada registrada con una invocación a CDirListCtr1::SetCallBack.Como se descri-
be en el comentario del código fuente de la página 250, CDirListCtr1::FindFiles invoca la
retrollamada para cada nombre de archivo que propone para añadir a la lista de directorios,
dando a la retrollamada un puntero a la estructura WIN32FIND-FILE que contiene infor-
mación sobre el archivo. CheckDateSize determina si el archivo se adapta a los filtros que
el usuario ha ajustado en las páginas Size y Date, y devuelve un valor TRUE o FALSE
para habilitar o deshabilitar el archivo.
Los archivos fuente modificados están listados al principio de la siguiente página. Los
archivos DirList2.r~y Res0urce.h no se incluyen aquí porque difieren sólo ligeramente de
sus homólogos del Capítulo 5, incorporando líneas adicionales de recursos de icono
IDI-FLOPPY, IDI-HARDDISK, IDI-CD-ROM e IDI-FOLDER. Como el programa eli-
ge abandonar los nombres de archivo en la lista sin marcar por un icono, IDI-FILE no está
definido. Puede encontrar todos los archivos en la subcarpeta Codigo\Capitulo.O7\Dir-
List2 en el CD que se acompaña.
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
vidor que se ha vuelto difícil justificar la escritura de una aplicación de contenedor sin la
ayuda de la biblioteca de clase MFC o soporte similar. Sin embargo, si prefiere no utilizar
MFC, la Biblioteca de plantillas activa (ATL) ofrece una altemativa viable. Visual C++
incluye un proyecto de ejemplo llamado AtlCon que demuestra cómo escribir una aplica-
ción de contenedor utilizando ATL. Los archivos fuente están ubicados en la carpeta
MSDN\Samples\VC98\ATLMtlCon. El Capítulo 10 se parece un poco más al tipo de
soporte que ofrece ATL para el desarrollo de las aplicaciones de contenedor.
Aunque este capítulo y los dos capítulos siguientes ahondan en los requisitos y opera-
ciones internas de los controles ActiveX, sólo pretenden ser una introducción a lo que es
un tema largo, adecuado para un libro entero. Los capítulos se concentran en mostrarle
algunos de los medios en los que Visual C++ facilita el trabajo del programador cuando
trata con controles ActiveX, tanto si está escribiendo un control como si la aplicación de
control utiliza el control. Para abarcar con más detalle un campo que se hará con muchas
probabilidades más importante que la programación de Intemet, consulte referencias espe-
cializadas, como el A fondo OLE, segunda edición, de Kraig Brockschmidt, el texto entero
que puede encontrar en la ayuda en línea de MSDN.
UN POCO DE TRASFONDO
El nombre es nuevo, pero la tecnología es madura. Los controles ActiveX forman sólo
parte de las tecnologías ActiveX de Microsoft, que están basadas en el Modelo de objeto
de componente (COM) y OLE. OLE quiere decir vinculación e incrustación de objetos,
pero como la incrustación de objetos hace mucho tiempo que es una parte menor de las
posibilidades de OLE, Microsoft no utiliza el nombre como acrónimo. Hoy, OLE ha toma-
do un nuevo significado y ya no tiene un número de versión. Ha evolucionado desde una
tecnología creada para un propósito específico para convertirse en una arquitectura gene-
ral en la que se basan otras tecnologías específicas, ActiveX entre ellas. OLE define un
anteproyecto estándar para la creación y conexión de distintos componentes de programa,
incluyendo módulos de servidor llamados controles personalizados OLE. Al menos se
llamaban controles OLE; Microsoft ahora los llama controles ActiveX.
Entonces, ¿qué es un control OLEIActiveX? La respuesta corta es que un control
OLEIActiveX es una biblioteca de enlace dinámico que opera como un servidor basado en
COM y puede estar incrustada en una aplicación huésped de contenedor. La respuesta
larga; bueno, en cierto modo este capítulo es la respuesta larga. Vamos a empezar con algo
de historia para ver exactamente qué hace un control OLE antes de acometer la tarea más
comprometida de explicar cómo funciona.
Quizás el primer tipo de software de componente que llamó la atención de los desarro-
lladores de Windows fue el control personalizado del modelo Visual Basic Extension. Los
controles personalizados eran familiarmente conocidos como VBX, llamados para la ex-
tensión de tres letras agregada al nombre de archivo de control. La arquitectura VBX
permitía a los desarrolladores crear unas adiciones eficientes y reutilizables para los pro-
gramas de Visual Basic que se pudiesen colocar como componentes autocontenidos en una
ventana, llamados un formulario en Visual Basic, Las ventajas de los controles VBX eran
triples:
uso DE CONTROLES ACTIVEX 273
Como una biblioteca dinámica, un control VBX era reutilizable en forma binaria en
lugar de código fuente.
Propiedades. Datos públicos dentro del control y el contenedor que sirven para
describir un partido al otro. Como inicio, un control puede leer las propiedades de
control y ajustar sus procedimientos de inicialización de modo que concuerden con
la apariencia del contenedor y sus características. Aunque el contenedor esté activo,
el contenedor puede leer las propiedades del control para aprender su estado actual, y
si el control lo permite, volver a escribir las propiedades para alterar el comporta-
miento del control.
CONTENEDORES DE CONTROL
AniBtn32.0~~ Botón animado. Utiliza un mapa de bits o metafile para crear un botón con
imágenes cambiantes.
BtnMenu.ocx Menú. Visualiza un botón y un menú emergente, como se muestra en la
Figura 8.2.
DBGrid32.o~~ Cuadrícula. Un control de hoja de cálculo que visualiza celdas en formato
estándar de cuadrícula. El usuario puede seleccionar celdas, y al contrario
que el viejo control Grid32, introducir datos directamente en la celda. Las
celdas también se pueden llenar de forma programática por el contenedor o
atar a un dato recordset para actualizarse automáticamente.
1ELabel.ocx Etiqueta. Visualiza texto rotado en un ángulo o a lo largo de la curva
especificada.
1EMenu.ocx Menú emergente. Visualiza un menú emergente, como se muestra en la
Figura 8.2.
1EPopWnd.o~~ Ventana emergente. Visualiza un documento HTML en una ventana
emergente.
1EPrld.ocx Precargador. Descarga el contenido del URL especificado y lo almacena
en la caché. El control dispara un evento después de completar la carga.
Tira de almacenamiento. Carga y visualiza el contenido de un URL en un
intervalo fijo especificado. Como sugiere su nombre, este control es útil
para visualizar datos que cambian continuamente, como la cinta de tira de
almacenamiento que se muestra en la Figura 8.2.
1ETimer.o~~ Reloj. Un control invisible que dispara un evento a intervalos específicos.
KeySta32.0~~ Estado de las teclas. Visualiza y modifica opcionalmente los estados de las
teclas CAPS LOCK, NÚM LOCK, INSERT Y SCROLL LOCK.
Marquee.ocx Marquesina. Desplaza el texto en un archivo de HTML tanto en dirección
horizontal como vertical y se puede configurar para que cambie la cantidad
y demora de desplazamiento.
MCI32.ocx Multimedia. Administra la grabación y reproducción de archivos multime-
dia en dispositivos Media Control Interface (MCI). Este control puede vi-
sualizar un conjunto de botones de presionar que provocan comandos MCI
a dispositivos como tarjetas de sonido, secuenciadores MIDI, discos CD-
ROM, reproductores CD de sonido, reproductores de vídeo disco y graba-
dores de vídeo. El control también soporta la reproducción en segundo pla-
no de archivos de vídeo AV1 para Windows.
MSCal.ocx Calendario. Un calendario de pantalla desde el que el usuario puede selec-
cionar fechas.
MSChart.ocx Diagrama. Un control de diagramas sofisticado que acepta datos numéri-
cos y luego visualiza uno de los varios tipos de diagramas, incluyendo
diagramas de línea, barra y columna. El control interpreta las visualizaciones
tanto en dos como en tres dimensiones, según se muestra en la Figura 8.3.
( Continúa)
276 MICROSOFT VISUAL CC+ 6.0. MANUAL DEL PROGRAMADOR
El prefijo «IE» en algunos de los nombres de archivos en la Tabla 8.1 quiere decir
Intemet Explorer, que indica que los controles se incluyen con ese programa. Los archivos
OCX pueden estar en su sistema en cualquier parte, pero normalmente están colocados en
las subcarpetas Windows\OCCache y Windows\System. Si por alguna razón no tiene estos
archivos y desea seguir las demostraciones de este capítulo, copie los archivos de la carpe-
ta OCX en el CD que acompaña a su carpeta OCCache, Sistema o Sistema32. Sin embar-
go, no suponga que este pequeño ejemplo representa la última palabra en controles ActiveX.
Aparecen controles nuevos en el mercado cada día, muchos de ellos versiones de demostra-
ción que usted puede utilizar en sus aplicaciones sin pagar. Si quisiera explorar Internet para
buscar algunos de los controles disponibles, estas dos direcciones ofrecen cargas libres y
proporcionan enlaces a otros sitios Web de interés a los desarrolladores de aplicaciones:
Tira de almacenamiento
enu emergente
Diagrama
Figura 8.2. Unos cuantos de los controles ActiveX de Microsoft como pueden
aparecer en el contenedor.
uso DE CONTROLES ACTIVEX 277
Cuando copia un archivo de control a su disco duro desde el CD que acompaña u otra
fuente, regístrelo utilizando la utilidad RegSvr32.exe que se encuentra en la subcarpeta
VC98Bin. RegSvr32 invoca la función de autorregistro del control, que escribe identifi-
cando la información sobre el control al sistema Registry. Hasta que se registra un control,
una aplicación de contenedor normalmente no tiene medio de ubicarla para su incrusta-
ción. Haga clic en el botón Start y ejecute RegSvr32 desde el diálogo Run, especificando
un archivo OCX en la línea de comandos:
Antes de insertar un control ActiveX en su proyecto, puede que desee echar primero un
vistazo al control. Todo lo que necesita es un editor de texto y explorador que soporte
ActiveX, uno para crear un documento de HTML y el otro para visualizarlo. HTML quiere
decir Lenguaje de marcas de hipertexto, que define una convención simple para la crea-
ción de páginas Web que están bien documentadas en distintos libros y artículos. Puede
aprender la mayor parte de lo que necesita saber sobre HTML con sólo unos cuantos
minutos de estudio. El editor de texto de Visual C++ soporta HTML hasta cierto límite
automáticamente las etiquetas de codificación de color y otros elementos de documento en
la ventana de visualización.
Para utilizar un control ActiveX en un documento de HTML, debe primero ubicar el
número de 32 dígitos identificador de clase de control. Hablaremos más sobre CLSID en el
capítulo siguiente, pero por ahora todo lo que necesita saber es cómo buscar el número. El
editor Registry proporciona un modo conveniente de encontrar un CLSID de control. Haga
clic en el botón Start y teclee regedit o regedit32 en el diálogo Run, dependiendo de si su
sistema es Windows 95 o Windows NT. Haga clic en el comando Find en el menú Edit del
editor Registry y teclee el nombre del archivo de control.
Por ejemplo, una búsqueda de ietimer.ocx en el editor Registry encuentra esta jerarquía
en Registry:
278 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
] -
m _i lrnplernented Categotier
4 InprocServei32
Con estos dos números en mano, puede escribir un documento de HTML simple que
utilice los controles Timer y Label para visualizar texto que parezca dar sacudidas, saltan-
do sin parar en la parte inferior de un cuadro coloreado:
Para ver la animación, utilice un explorador Web, como por ejemplo Internet Explorer,
o cualquier otra herramienta de autoría que soporte ActiveX, para abrir el documento
Tumble.htm ubicado en la carpeta Codigo\Capitulo.08 en el CD que se acompaña. En
Internet Explorer 3.0 y versiones posteriores, haga clic en el comando Open y navegue por
el documento, a continuación haga dos veces clic para abrirlo. El Listado 8.1 muestra los
contenidos del documento Tumble.htm.
Figura 8.3. La utilidad Test Container, invocada a través del menú Tools.
El control ActiveX seleccionado puede programarse a través de las funciones
de método. Haga clic en la herramienta Invoke Methods o elija el comando co-
rrespondiente desde el menú Control para hacer emerger el diálogo Invoke
Methods que se muestra en la Figura 8.4. La lista desplegable del cuadro de
combinación Method Name detalla todos los métodos de control, que pone den-
tro de dos categorías llamadas métodos normales y métodos de propiedad. Los métodos
normales se etiquetan como Method en la lista desplegable. Los métodos de propiedades
se marcan como PropGet o PropPut, dependiendo de si se corresponden a un método «get»
de propiedad, que obtiene el valor de propiedad, o al método «pub, que escribe el valor.
La Figura 8.4, por ejemplo, muestra que el control Button Menu exporta ambas clases de
métodos, permitiendo al contenedor leer y escribir la propiedad Caption del control -es
decir, el texto que aparece en el botón- mediante los métodos get y put.
Para añadir un elemento al menú emergente del control Button Menu, un contenedor
invoca el método Addltem. Podemos hacer lo mismo utilizando el diálogo Invoke Meth-
ods, añadiendo una lista de elementos de menú como los que se muestran en la Figura 8.3.
Seleccione Addltem en el cuadro Method Name, a continuación teclee el texto deseado en
el cuadro de edición etiquetado Parameter Value. Cuando haga clic en el botón Invoke, el
Test Container invoca el método Addltem para añadir el texto a la lista de comandos de
menú del control. Cierre el diálogo Invoke Methods y haga clic en el control Button Menu
para ver el comando nuevo.
Cuando seleccione un método put para una propiedad de tipo de número entero, debe
también hacer una selección tal como VT-14 en el cuadro Parameter Type. Determine el
tipo de parámetro correcto invocando primero el método get correspondiente y anotando
el valor devuelto, o elija VT-UNKNOWN en el cuadro Parameter Type. Si un método put
lleva más de un parámetro, seleccione cada una de las variables en el cuadro de lista
Parameters y haga clic en el botón Set Value después de teclear su valor. Cuando todos los
valores aparecen correctamente en la columna Value, haga clic en Invoke para pasar los
parámetros al método.
Las propiedades de color tales como BackColor son valores COLORREF de 24 bits,
que se pueden representar como tipos de números enteros VT-14. Los tres tipos de bytes
de un valor COLORREF se corresponden a los componentes de color completo rojo, verde
y azul, como se muestra en el proyecto de ejemplo Color del Capítulo 5. Aunque un valor
COLORREF se expresa más fácilmente como un número hexadecimal del tipo OxFF para
rojo fuerte, el diálogo Invoke Methods reconoce sólo los valores tecleados en un formato
decimal. Para introducir un valor de color nuevo en el diálogo, teclee un número tal como
16.711.680 para azul fuerte, 65.280 para verde fuerte o 255 para rojo fuerte. La selección
de VT-COLOR en el cuadro Parameter Type activa un botón etiquetado Choose Color
que visualiza una variedad de colores de muestra. Sin embargo, esta opción actualmente
no traduce correctamente un color seleccionado a un parámetro de método válido.
Muchos controles proporcionan su propia hoja de propiedades, que el Test Contai-
4 ner pone a disposición a través del botón de herramientas, Properties. Haciendo clic
en la herramienta, hace que el Test Container tramite un verbo OLEIVERB-PRO-
JPrope"leSJ PERTIES al control, diciéndole que visualice su hoja de propiedad si tiene una. Ha-
ciendo dos veces clic en el borde de una ventana de control también invoca el coman-
do, como lo hace eligiendo Properties del menú Edit del Test Container.
La ventana Test Container está dividida por una barra separadora móvil en dos vistas
horizontales. La vista inferior normalmente visualiza un registro de tiempo real de eventos
disparados por el control seleccionado. El registro, llamado un acceso de evento, puede ser
encaminado de nuevo en otra parte eligiendo el comando Logging del menú Options del
Test Container. Durante el desarrollo de un control ActiveX, el acceso de evento puede
ahorrar mucho trabajo de adivinación permitiéndole probar rápidamente si sus eventos de
control se disparan correctamente. Echaremos un vistazo a la característica de acceso de
evento otra vez en el capítulo siguiente cuando pruebe un control ActiveX de ejemplo.
Seleccionar la opción ActiveX Controls implica mucho código adicional en juego para
soportar contención de control, pero el marco de trabajo se encarga de todo. En la superfi-
cie, la opción añade simplemente esta línea a la función InitInstance de clase de aplicación:
Cuando AppWizard termina de crear el proyecto, elija el comando Add To Project desde
el menú Project, a continuación haga clic en Components And Controls en el menú secun-
dario para visualizar el diálogo Gallery. Seleccione el icono Anibutton Control en la car-
peta Registered ActiveX Controls y haga clic en el botón Insert. Acepte los ajustes prede-
terminados en el diálogo Confirm Classes, a continuación salga de la Gallery.
Ir a la Gallery no es estrictamente necesario, porque también puede añadir un control
ActiveX a un proyecto desde el editor de diálogo. Cuando el área de trabajo del editor
aparece (como se describe en el paso siguiente), haga clic en el botón derecho del ratón en
el área de trabajo y elija Insert ActiveX Control del menú contextual. Esto hace emerger
una lista de los mismos controles registrados en el diálogo Gallery. Simplemente haga dos
veces clic en la lista para añadirla al diálogo.
Nota: Si se pierden algunas entradas del Registry, Visual C++ visualiza un mensaje
que dice de forma errónea que el control Animated Button necesita una licencia de
tiempo de diseño. Si el mensaje aparece cuando sigue los pasos descritos aquí, indica
que ha instalado Visual C++ sólo con privilegios de USER o que el Registry está co-
rrupto. Instalar de nuevo Visual C++ es la única solución. Para obtener más informa-
ción sobre este posible problema y de otros controles ActiveX que pueden causarlo,
visite el sitio Knowledge Base:
Haga clic varias veces en la nueva ventana de control ActiveX para ir de una imagen
de mapa de bits a otra, una de las cuales se muestra en la Figura 8.6. Haga clic en el botón
de diálogo OK para regresar al modo edición.
Ahora que tenemos una idea de las muchas formas que puede tomar el control Acti-
veX, vamos a examinar una para ver cómo opera.
286 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
matriz vuelve a convertir la información dentro del paquete en una lista de parámetros e
invoca a la función de destino en el servidor. Cualquier comunicación desde el servidor
toma su camino de regreso al cliente a través de la misma ruta. El proceso de conectar el
cliente y el servidor a través de bibliotecas proxy y matriz se llama ordenación. Como se
esperará, la ordenación es más lenta que la interacción más directa entre un cliente y un
control ActiveX, ya que un servidor en proceso no depende de invocaciones a procesos
remotos para comunicarse con el cliente y no requiere ordenación a menos que la comuni-
cación sea entre hilos (el Capítulo 10, «Escritura de controles ActiveX utilizando ATL»,
discute la ordenación de hilos con más detalle).
La comunicación se ejecuta en ambas direcciones entre un control ActiveX y su conte-
nedor, así la aplicación de contenedor debe proveer su propio juego de interfaces para
recibir llamadas desde el control. Microsoft publica guías que especifican un mínimo jue-
go de interfaces que debe soportar un contenedor. Las directrices se documentan en la
ayuda en línea, accesibles a través de la pestaña Índice de la ventana de la biblioteca
MSDN. Escoja el comando Índice en el menú Help de Visual C++, luego escriba required
interjGaces para localizar el artículo de ese título.
Al soportar estas interfaces, una aplicación de contenedor se asegura que puede intero-
perar con cualquier control ActiveX que también cumpla con las directrices. La Tabla 8.2
de la página siguiente describe las ocho interfaces que debería soportar el contenedor para
cumplir con las especificaciones OLEIActiveX.
Al proveer sólo las primeras tres interfaces, la Tabla 8.2 le proporciona un contenedor
de documento compuesto. Escribir un programa de contenedor con MFC le libera de tener
que preocuparse de los detalles del soporte de interfaz. Como se ha descrito anteriormente
en este capítulo, si selecciona un soporte de control ActiveX en AppWizard para un pro-
yecto de contenedor, añade al código de suministro una llamada al marco de trabajo de la
función AfxEnableControlContainer.Esta función activa todas las interfaces enumeradas
en la Tabla 8.2. Una vez preparadas las interfaces, la comunicación entre el control Acti-
veX y su contenedor discurre a través de eventos, métodos y propiedades.
Tabla 8.2. lnterfaces que un contenedor debería soportar para cumplir
con las especificaciones OLE/ActiveX
Interfaz Descripción
Eventos
Aunque un control ActiveX es autocontenido, puede mantener la aplicación de contenedor
llena de actividad dentro del control disparando eventos. Los eventos disparados por un
control en particular son lo que el control de desarrollo piense que las aplicaciones de
contenedor desean saber. Por ejemplo, el control puede disparar un evento en respuesta a
un clic del ratón dentro de la ventana de control, o pasar al contenedor cualquier entrada
recogida del teclado cuando el control tiene la atención. El disparo del evento puede signi-
ficar el término de algún trabajo, como el de localizar un URL, cargar datos u ordenar una
lista. Podemos trazar una analogía entre disparo de eventos y la forma en que el control envía
mensajes de notificación, tales como CBN-DROPDOWN o BN-DOUBLECLICKED a
su ventana padre, excepto que un control ActiveX dispare un evento al invocar a una
función en el contenedor, no al enviar un mensaje.
La función en el contenedor que recibe el evento disparado es un tipo de retrollamada.
Si la aplicación de contenedor desea que se le notifique un evento de control en particular,
para recibir la llamada debe proveer una función, conocida como conductor de evento o
función de implementación de evento. El contenedor almacena una lista de punteros a sus
uso DE CONTROLES ACTIVEX 289
conductores de eventos en una IDispatch v-tabla conocida como el evento sink. El evento
sink conecta cada evento con su propia función de conductor. La aplicación de contenedor
tiene que proveer una función de conductor por cada evento que el control dispara, ni
tampoco cada evento disparado por el control ActiveX.
Los estándares OLEIActiveX predefinen un número de eventos almacenados que in-
forma al contenedor de sucesos en la ventana de control. Por ejemplo, notificar al contene-
dor cuándo se ha hecho clic con el ratón en la ventana de control, un control usando MFC
puede activar el evento Click almacenado a través de la macro EVENT-STOCK-CLICK:
El control no necesita ningún otro código, ya que el marco de trabajo identifica el clic
del ratón y dispara el evento. Si el contenedor desea saber cuándo tiene lugar un clic del
ratón en la ventana de control, provee una función de manejador para el evento Click, al
cual se refiere en un mapa del evento sink que concuerde.
BEGIN-EVENTSINK-MAP ( C D e m o C o n t a i n e r , Cdialog)
//{{AFX-EVENTSINK-MAP(CdemoContainer)
ON-EVENT(CDemoContainer, IDC-CTRL, D I S P I D - C L I C K ,
O n C l i c k , VTS-NONE )
//}}AFX-EVENTSINK-MAP
END-EVENTSINK-MAPP ( )
una sola función, ocultando la actividad de bajo nivel de IDispatch que ocurre entre ambas.
Los nombres de las funciones son arbitrarias. MFC forma los nombres de las funciones de
disparo añadiendo el prefijo Fire a un nombre de evento; la función Fire-Click, por ejemplo,
dispara el evento Click.
Métodos
Un método es lo opuesto a una función que maneja eventos. Mientras que las funciones
que manejan eventos están ubicadas en el contenedor y son llamadas por el control, los
métodos están situados en el control y son llamados por el contenedor. El contenedor uede
llamar a un método para aprender una condición o para solicitar que el control realice
alguna acción.
OLEIActiveX predefine tres métodos de almacenamiento, llamados DoClick, Refresh
y AboutBox, ninguno de los cuales toma parámetros o devuelve un valor. DoClick provoca
que el control dispare su evento de almacén Click (si lo soporta), el método Refresh dice al
control que invalide su ventana y que la redibuje él mismo, y AboutBox indica al control
que visualice un cuadro de diálogo informativo. Cualquier otro método que exporte un
control ActiveX se denomina método cliente, diseñado por el autor del control. Para el
contenedor, un método aparece como una función normal exportada por una librería de
enlace dinámico, con una lista de parámetros opcionales de hasta 15 parámetros y un valor
de retorno de cualquier tipo.
Propiedades
Las propiedades son datos públicos contenidos dentro de ambos el contenedor y el control
que cada uno expone al otro. OLEIActiveX define cuatro categorías de propiedades, lla-
madas almacén, cliente, ambiente y extendidas. Las propiedades de almacén y cliente
pertenecen al control, y las propiedades de ambiente y extendidas pertenecen al contenedor.
OLE-COLOR CDemoCtrl::GetBackColorO
I
OLE-COLOR result:
294 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Como los manejadores de eventos pertenecen a la clase del contenedor, que normal-
mente se deriva de una clase basada en diálogos tal como CDialog, la Galería no agrega
código fuente para las funciones que gestionan eventos. Ese trabajo se deja a ClassWizard
después de que el control es agregado al diálogo.
El procedimiento se explica mejor utilizando un ejemplo. Esta sección construye una
aplicación contenedor simple llamada Hour que utiliza uno de los controles ActiveX libres
de licencia incluidos en el CD que acompaña al libro. El control es el mismo control
temporizador 1 E T i m e r . o ~utilizado
~ anteriormente en el documento Tumble.htm. Puede
encontrar el control IETimer enumerado bajo el nombre Timer Object en la carpeta Regis-
tered ActiveX Controls de la Gallery. La lista de controles de la carpeta puede incluir otro
control ActiveX temporizador, creado a partir de un proyecto MFC de ejemplo denomina-
do Time Control (los archivos fuente de Time Control están en la carpeta MSDN\Sam-
ples\VC98\MFC\Controls\Time). Ambos controles temporizadores exportan los mismos
métodos y realizan la misma función, por lo que no importa cuál de los dos utilice para el
proyecto Hour.
A diferencia de otros controles ActiveX, tales como Anibutton y Calendar, el Timer
Object no es un control visible. No se visualiza a sí mismo como una ventana dentro del
contenedor, sino que simplemente dispara un evento en el intervalo especificado, sirvien-
do de mecanismo de temporización para el programa que lo contiene. El programa Hour
utiliza los eventos del temporizador para gestionar tres indicadores de progreso mostrados
en la Figura 8.9. Los controles de progreso visualizan márgenes de tiempo en minutos,
segundos y decenas de segundo. El programa Hour toma su nombre del hecho de que las
tres visualizaciones comienzan cuando el control de progreso Minutes se llena después de
60 minutos.
Construir el programa Hour sólo tiene cinco pasos desde el inicio hasta el final,
Como el control ActiveX Timer Object no crea su propia ventana cuando el programa
se ejecuta, no importa dónde lo coloque en el diálogo. Muestre el cuadro Properties de
cada uno de los controles y teclee en las capturas mostradas en la imagen de la pantalla
superior junto con los identificadores listados en la segunda columna de la Tabla 8.6.
La clase de aplicación CHourDlg requiere una variable miembro para cada uno de los
controles del diálogo, que puede agregar a través de ClassWizard. Con el editor de diálo-
gos todavía activo, haga clic en la orden ClassWizard del menú Ver para invocar al diálo-
go MFC ClassWizard descrito en el Capítulo 6. En la pestaña Member Variables, seleccio-
ne cada uno de los nuevos controles del cuadro Control IDs y haga clic en el botón Add
Variable para visualizar el diálogo Add Member Variable. En el cuadro de texto etiqueta-
do Member Variable Name, teclee la variable del control listada en la tercera columna de
la Tabla 8.6. La Figura 8.10 muestra el resultado final.
Necesitamos además una función para gestionar el evento disparado por el control
Timer Object. En la pestaña Message Maps de ClassWizard, seleccione IDC-TIMER1 del
cuadro Objects IDs y Timer del cuadro Messages, haga clic entonces en el botón Add
Function. ClassWizard agrega código matriz para una función manejadora de eventos
denominada OnTimerTimerl.
El prefijo «E» designa OnTimerTimerl como función para manejar eventos. Haga clic
en OK para cerrar el diálogo ClassWizard.
y seleccione Go To Class Definition del menú desplegable. Visual C++ abre automática-
mente HourD1g.h en el editor de texto y coloca el cursor al comienzo de la declaración
CHourDlg, en la que se ha agregado las nuevas variables de control:
/ / Datos de diálogo
//{{AFX-DATA(CHourD1g)
enum I IDD = IDD-HOUR-DIALOG 1 ;
CProgressCtrl progTen;
CProgressCtrl progSec;
CProgressCtrl progMin;
CString strMin;
CString strsec;
CString strTen;
CIeTimer time ;
//}}AFX-DATA
private:
int iMin,iSec;
Como antes, la zona sombreada indica adiciones al código que debe teclear por sí
mismo en el editor de texto.
Las variables iMin e iSec llevan la cuenta de los minutos y segundos que han pasado,
que se escriben en los controles estáticos adyacentes a los indicadores de progreso del
diálogo. No es necesaria una cuenta similar para las decenas de segundo, porque la posi-
ción del indicador de progreso IDC-PROGRESS-TEN avanza con cada evento disparado
por el control Timer Object. Esto será más claro en el momento en que agreguemos código
al manejador de eventos.
Las últimas modificaciones al código fuente se realizan en la función CHourD1g::On-
InitDialog. Haga clic en cualquier cuadro Members de WizardBar para visualizar una lista
desplegable de funciones miembro y seleccione OnInitDialog desde la lista aquí mostrada:
llama a un método del Timer Object para decirle al control que comience a disparar even-
tos cada 100 milisegundos.
Utilice el WizardBar para navegar hacia abajo hasta la función CHourD1g::OnTimer-
Timerl y agregue las siguientes líneas sombreadas:
void CHourDlg::OnTimerTimerlO
{
/ / TODO: Agregue su código de gestión de la notificación del control
/ / aquí
300 MICROSOFT VISUAL C+t 6.0. MANUAL DEL PROGRAMADOR
private
Cpmenu btnmenu ;
El siguiente paso implica escribir el código que inicializa el objeto btnmenu. Esto se
hace mejor con la función CButtonView::OninitialUpdate, que asegura que la aplicación
crea el control solamente una vez cuando la vista aparece por primera vez. ClassWizard
puede generar el código inicial para la función; sólo tiene que seleccionar CButtonView en
el cuadro Class Name de la pestaña Message Maps de ClassWizard, y después haga doble
clic en OnInitialUpdate del cuadro Messages. Salga de ClassWizard mediante el botón
Edit Code, que automáticamente abre el documento fuente ButtonView.cpp en el editor de
texto con el cursor colocado en la nueva función OnInitialUpdate. Agregue a la función el
código de inicialización que se muestra aquí:
void CButtonView::OnInitialUpdate()
{
CView::OnInitialUpdate();
COleVariant v( 1L ;
CRect rect( 3 0 , 3 0 , 2 5 0 , 1 2 0 ) ;
btnmenu.Create( NULL, WS-VISIBLE, rect, this, IDC-BTNMEWU);
btnmenu.SetCaption( "Haga click aquí" ) ;
btnmenu.Invalidate0;
btnmenu.AddItem( "Menu Item #1", v ) ;
302 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
v = 2L;
btnmenu.AddItem( "Menu Item # 2 " , v ) ;
v = 3L;
btnmenu.AddI~em( "Menu Item k 3 " , v 1 ;
v = 4L;
btnmenu.AddItem( "Menu Item # 4 " , v ) ;
1
Cpmenu se deriva de CWnd, proporcionando dos versiones de una función Create que
se tiene como en el archivo Pmenu.h. Por cuestiones de simplicidad, el fragmen-
to que se muestra aquí utiliza un objeto CRect para codificar el tamaño del control y
posición en la ventana principal. La función Cpmenu::Addltem agrega cadenas de órdenes
a los botones del menú emergente, ordenando las órdenes en el menú de acuerdo al valor
VARIANT dado como segundo parámetro de la función. La función OnInitialUpdate de
este ejemplo simplemente crea un objeto COleVariant para mantener los valores VARI-
ANT y llama cuatro veces a AddItem para insertar una lista representativa de las órdenes
del menú.
Si compila y ejecuta la aplicación Button en este momento, visualizará correctamente
el control Button Menu en la ventana principal. Aunque al hacer doble clic sobre el control
se invoque su menú emergente, la aplicación por sí misma no responde cuando se seleccio-
nan las órdenes del menú y se hace clic sobre ellas. Esto es porque no hemos agregado
todavía funciones para manejar eventos que dispara el control Button Menu durante la
interacción con el usuario. Eso es lo siguiente.
Todavía no sabemos qué eventos dispara el control Button Menu. Esa información se
almacena en el archivo OCX del control como parte de su recurso tipo de biblioteca, pero
ClassWizard no puede leer los datos porque no sabe nada sobre el control o la clase Cp-
menu. Visual C++ proporciona una utilidad que le permite explorar la biblioteca tipo de
control para aprender qué eventos dispara el control y la lista de parámetros necesarios
para las funciones manejadoras. El programa se llama OleView.exe, y se invoca haciendo
clic sobre OLEICOM Object Viewer del menú Tools. La orden View TypeLib del menú
File de Object Viewer le permite abrir el archivo BtnMenu.ocx y visualizar su biblioteca
tipo. Nos devuelve que el control Button Menu dispara solamente dos eventos, llamados
Click y Select. El guión biblioteca muestra cómo una aplicación contenedor debe declarar
funciones de gestión para recibir adecuadamente los eventos disparados:
[id(OxOOOOOO97)]
void ClickO.
[id(OxOOOOOO96)]
void Celect(int item).
uso DE CONTROLES ACTIVEX 303
Esta es toda la información que necesitamos para escribir funciones de gestión para los
eventos. Pero especialmente para un control que proporcione muchos eventos diferentes,
una alternativa más sencilla para pasar de las bibliotecas tipo es simplemente informar a
ClassWizard de la existencia del control. ClassWizard puede entonces realizar todo el
trabajo de leer la información de tipo del control, generando código para el esqueleto de
las funciones de gestión y agregar sus entradas al mapa de eventos sink. Todo lo que se
necesita es un poco de masaje de la base de datos de la clase del proyecto y un poco de
añadido a la declaración de la clase CButtonView. Aquí tenemos cómo se hace.
Primero, abra el archivo Button.clw con el editor de texto y agregue una nueva entrada
de recurso para un cuadro de diálogo:
No hay tal diálogo en el proyecto, sólo el nombre del identificador, pero ClassWizard
no necesita saberlo. La entrada dice a ClassWizard que un recurso diálogo identificado
como IDD-FAKEDLG pertenece a la clase CButtonView y contiene el control Button
Menu, que es identificado por su número CLSID.
Después, vuelva al archivo Butt0nView.h y corrija el código que añadimos anterior-
mente, agregando la sentencia #define para el identificador IDD-FAKEDLG y declare el
valor en la declaración CButtonView. La nueva entrada se marca mediante líneas especia-
les AFX-DATA, que hacen que el identificador de diálogo sea reconocible a ClassWizard,
como se explicó en el Capítulo 6. El resultado es como sigue:
#include "prnenu.hN
#define IDC-BTNMENU 1001
#define IDD-FAKEDLG 1002
Cstring str;
Construya la aplicación Button y ejecútela. Debería ver un cuadro de mensaje cada vez
que haga clic sobre un elemento del menú del control, como se ilustra en la Figura 8.1 1.
CONTROLWIZARD
Del mismo modo que AppWizard crea un proyecto para una aplicación MFC, Control-
Wizard crea un proyecto para un control ActiveX. Un control creado con la ayuda de
ControlWizard utiliza MFC, dotándole de las ventajas y desventajas descritas en la sec-
ción anterior. ControlWizard es una forma personalizada de AppWizard, y un proyecto
ControlWizard empieza del mismo modo como un proyecto AppWizard normal. Elija el
comando New del menú File para invocar el diálogo New, y en la pestaña Projects haga
clic en el icono para el ControlWizard de MFC ActiveX. La Figura 9.1 ilustra los pasos.
ControlWizard le guía a través de los dos pasos antes de la creación del proyecto. Esta
sección examina opciones que el asistente ofrece y explica cuándo y por qué una opción
puede ser apropiada para su control ActiveX.
La Figura 9.2 muestra la pantalla de apertura de ControlWizard, que primero pide el
número de controles en el proyecto. Como un control personalizado VBX, un archivo
OCX puede contener más de un componente de control ActiveX. Especifique el número
de controles que desea que aparezcan en el cuadro de texto en la parte superior del diálogo.
También puede añadir controles más tarde durante el desarrollo del proyecto. La opción
siguiente en la pantalla ControlWizard le permite restringir el uso de controles a través de
una licencia. Aunque la opción soporte de licencia está desconectada por defecto, muchos
controles pensados para el uso general deberían tener la protección de una licencia, que se
describe en breve.
308 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Por defecto, ControlWizard activa sólo dos de las opciones enumeradas anteriormente:
Activates when visible y Has an «About» box. Si deselecciona el cuadro de control Acti-
vates when visible despeja la etiqueta de estado OLEMISC-ACTIVATEWHENVISIBLE
de control, que el control coloca en los datos Registry por medio de la función global
AfxOleRegisterControlClass de MFC. El contenedor que incrusta el control puede deter-
minar el estado de la etiqueta invocando el método IOle0bject::GetMiscStatus de control.
Una etiqueta sencilla señala al contenedor que el control debería permanecer inactivo
cuando se hace visible, posponiendo de este modo la creación de la ventana de control
hasta que el usuario la necesite. Para los controles ActiveX que el usuario nunca pueda
invocar dentro el servicio, esto puede ahorrar la costosa operación de crear una ventana
innecesariamente. El Capítulo 10 trata con más detalle las etiquetas de estado de control
ActiveX, como por ejemplo OLEMISC-ACTIVATEWHENVISIBLE.
También puede que considere deseleccionar la opción About Box porque el soporte
añadido para el cuadro About aumente el tamaño del control ActiveX final. El cuadro
About estándar que genera ControlWizard, por ejemplo, añade aproximadamente 2 Kb de
código extra y datos de recurso al archivo finalizado OCX. Consulte la sección final del
Capítulo 4 para una explicación sobre otros medios de minimizar datos de recurso, que es
especialmente importante para los controles ActiveX.
Si está seleccionado el cuadro de diálogo Available In Insert Object, el procedimiento
de autorregistro del control añade una clave Registry llamada Insertable a la jerarquía
Registry CLSID del control. La clave Insertable informa a un contenedor que el control
ActiveX puede actuar como un objeto incrustado pasivo. El contenedor puede de este
modo crear un objeto del control ActiveX a través de las interfaces OLE Documents. Estas
interfaces, identificadas por el prefijo IOle, incluyen IOleCache, IOleClientSite, IOleCon-
tainer, IOleInPlaceObject y IOleInPlaceSite. Las aplicaciones que pueden incrustar un
objeto en un documento de contenedor buscan en Registry objetos que tengan la palabra
clave Insertable y visualicen una lista de los objetos en un diálogo Insert Object. Para ver
la lista en Microsoft Word, por ejemplo, haga clic en el comando Object en el menú Insert
de Word. Un control ActiveX creado utilizando ControlWizard aparece en la lista sólo si
está seleccionado el cuadro de comprobación Available In Insert Object Dialog. Las apli-
caciones que no soportan documentos de contenedor ignoran la palabra clave Insertable.
Por ejemplo, la herramienta New Control de la utilidad Test Container que encontramos
en el Capítulo 8 visualiza una lista de todos los controles registrados, tanto si están marca-
dos como insertables como si no.
Si desea que su control ActiveX subclasifique un control Windows estándar o común,
haga clic en el cuadro mostrado en la parte inferior de la pantalla en la Figura 9.3. La
ventana desplegable visualiza una lista de 16 controles de Windows que van desde botones
a visualizaciones de árbol. Seleccionando una entrada de la lista, hace que ControlWizard
genere código fuente para el control ActiveX que subclasifica el control de Windows
seleccionado. Utilice esta opción para producir un control ActiveX que tiene las caracte-
rísticas de un control Windows particular, pero que quiere modificar para añadir efectos
deseados.
Haga clic en el botón Advanced en el paso 2 de ControlWizard para abrir el diálogo
Advanced ActiveX Features que se muestra en la Figura 9.4. El diálogo proporciona op-
ciones que instala o elimina etiquetas de bit definidas por el conjunto de enumeración
COleControl::ControlFlags, que describe las características de comportamiento de con-
Figura 9.4. Diálogo Advanced ActiveX Features, invocado haciendo clic en el botón
Advanced del ControlWizard.
trol cuando se activa. Al ajustar cualquiera de los cuadros de comprobación, hace que
ControlWizard añada código que anula el método COleControl::GetControlFlags,que
informa al contenedor de los ajustes ControlFlags. Para añadir la anulación usted mismo a
un proyecto de control existente, ajuste una etiqueta de bit, como por ejemplo window-
lessActiuate, como se muestra a continuación:
DWORD CDemoCtrl::GetContro1Flags()
{
return COleControl::GetControlFlags() % windowlessActivate;
1
Algunas veces, no todos los contenedores soportan las funciones Ambient de COle-
Control. Un control ActiveX debería comprobar un valor devuelto válido después de invo-
car una función tal como AmbientBackColor.
LICENCIAS
Un control ActiveX colocado en una página Web conocida puede acabar pronto en todos
los ordenadores del mundo, visualizado en cientos de exploradores. Esta posibilidad de
volver a utilizar fácilmente un control ActiveX es quizás el rasgo más convincente de la
tecnología y la mejor ventaja. Sin embargo, la gran distribución de una propiedad intelec-
tual de programador también plantea el problema potencial de uso no autorizado. Para ver
el problema claramente, considere cómo un control ActiveX pasa a través de tres partidos
diferentes, identificados como Autor, Webmaster y Usuario.
Por el pago de unos derechos, el Autor permite al Webmaster instalar el control Ac-
tiveX en una página Web. El Usuario visita el sitio de Webmaster mediante Internet, y
como resultado el control ActiveX se copia del ordenador de Webmaster al ordenador del
Usuario, donde aparece en el programa de explorador del Usuario. Hasta ahora, todo es
como debería ser y el control se está utilizando como pretendía el Autor. Pero sin algún
tipo de garantía, nada impide a otros programadores que se apoderen del control ActiveX y
que lo utilicen en sus propias aplicaciones. Muchos desarrolladores puede que prefieran
que sus creaciones no se vuelvan a utilizar de este modo sin autorización, especialmente
en aplicaciones de mercado que saquen beneficio de un control sin compensación para el
autor del control.
La garantía más común contra el uso no autorizado de un control ActiveX implica una
licencia. Una licencia no sólo identifica al Autor en nuestro ejemplo como el propietario
de los derechos de autor del control, sino que también puede impedir la reutilización
subsiguiente del control por los desarrolladores que no hayan recibido una licencia del
Autor. El control estándar OLEIActiveX está diseñado pensando en una licencia. El están-
dar define la interfaz IClassFactory2 a través de la cual un contenedor crea un ejemplo del
objeto de control y al mismo tiempo se prueba a sí mismo con licencia para utilizar el
control. La creación del objeto de control finaliza sólo si el contenedor comprueba que en
el control existe una licencia válida.
Las licencias se están convirtiendo en algo generalizado para los controles ActiveX,
así que merece la pena examinar el esquema de licencia de ControlWizard con algo de
detalle. Otra razón para dedicar tiempo al tema es que las descripciones de licencia en la
documentación en línea de Visual C++ pueden ser un poco más confusas, principalmente
porque la documentación habla de «el contenedor» cuando puede que haya varios contene-
dores implicados. Es importante recordar que cualquier programa es un contenedor que
puede crear un ejemplo de control ActiveX y proporcione un sitio para el mismo. El
Capítulo 8 mostró varios contenedores diferentes que pueden incrustar un control ActiveX
bajo circunstancias diferentes:
Internet Explorer u otro explorador que soporte ActiveX, que coloque un control a
través de un identificador de clase especificado por la etiqueta OBJECT en un docu-
mento de HTML.
El editor de diálogo de Visual C++, que crea un ejemplo de un control cuando se
despliega en un diálogo en desarrollo.
Una aplicación de contenedor, como por ejemplo el programa Hour, que incrusta un
control ActiveX en tiempo de ejecución.
314 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Un esquema de licencia impide a una aplicación de contenedor que haga uso no autori-
zado de un control, pero ¿qué contenedor? Cuando el Usuario en nuestro escenario carga
el control del Autor junto con las instrucciones de HTML que lo visualiza, el explorador
del Usuario debe ser capaz de ejecutar libremente el control sin una licencia. El acceso
debería estar restringido sólo para los contenedores de otros dos tipos en la lista; es decir,
programas de desarrollo (como Visual C++) y las aplicaciones de contenedor que crean
(como Hour).
Considere la cadena de sucesos cuando el Webmaster decide desarrollar una aplica-
ción que utiliza el control ActiveX del Autor. Para crear la aplicación, el Webmaster
ejecuta un programa de desarrollo Windows tal como Visual C++ o Visual Basic. El
programa diseña invocaciones para que la aplicación visualice el control en el cuadro de
diálogo, así que el Webmaster utiliza el editor de diálogo -siendo él mismo un contene-
dor- para crear un ejemplo de control y visualizarlo en el diálogo. En este punto, invoca-
da la etapa de tiempo de diseño, la licencia se convierte en un tema. En la creación de un
ejemplo del control, el programa de desarrollo invoca el método IClassFactory2::Cre-
atelnstanceLic del control con un parámetro NULL, al que el control responde devolvien-
do un puntero a una interfaz sólo después de confirmar que la licencia existe (en un mo-
mento veremos cómo).
Como el Webmaster está autorizado a utilizar el control en una aplicación, la verifica-
ción de la licencia tiene éxito y el editor de diálogo es capaz de crear un ejemplo del
control. El Webmaster completa el desarrollo de la aplicación y vende una copia del ejecu-
table al Usuario. Como parte del paquete, el Webmaster proporciona un programa de
instalación que coloca una copia del control ActiveX del Autor en el disco duro del Usua-
rio y lo registra. Aunque el Usuario nunca ha entrado en un acuerdo de licencia con el
Autor, la nueva aplicación de contenedor consigue crear un ejemplo de objeto de control
cuando se ejecuta (de nuevo, el proceso se explica dentro de un momento). Esto se llama el
escenario de tiempo de ejecución de verificación de licencia.
Ahora considere qué ocurre cuando el usuario (que también resulta ser un programa-
dor) intenta crear otra aplicación de contenedor que incrusta el control del Autor. El pro-
grama de desarrollo del Usuario invoca el método IClassFactory2::CreateInstanceLic del
control como antes, pero esta vez el control detecta que el Usuario no posee una licencia y
por ello no permite el intento de creación. El Usuario decidido puede desarrollar la aplica-
ción sin la ayuda del editor de diálogo, pero la aplicación terminada ya no es capaz de
crear un ejemplo del control más que el programa de desarrollo. El código de verificación
de licencia en el control bloquea el uso no autorizado, tanto en el tiempo de diseño como
en el tiempo de ejecución.
El código que ControlWizard añade a un proyecto de control implementa un esquema
de licencia como el que se acaba de describir. La sección siguiente explica cómo funciona
el esquema.
Advertencia: Tiene licencia para este producto con arreglo a los términos
del acuerdo de licencia incluido con el software original y está protegido
por la ley de derechos de autor y tratados internacionales. La reproducción
o distribuición no autorizada puede conllevar penas civiles y criminales
severas y se perseguirá bajo la ley en la máxima medida posible.
.......................................................................
/ / CLicenseCtrl::CLicenseCtrlFactory::VerifyUserLicense -
/ / Comprueba la existencia de licencia de usuario
316 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
BOOL CLicenseCtr1::CLicenseCtrlFactory::VerifyUserLicense~)
I
return AfxVerifyLicFile(AfxGetInstanceHandle(), -szLicFileName,
-szLicString);
1
.......................................................................
/ / CLicenseCtrl::CLicenseCtrlFactory::GetLicenseKey -
/ / Devuelve una clave de licencia en tiempo de ejecución
BOOL CLicenseCtrl::CLicenseCtrlFactory::GetLicenseKey(DWORDdwReserved,
BSTR FAR* pbstrKey)
{
if (pbstrKey == NULL)
return FALCE;
*pbstrKey = SysAllocString(-szLicString);
return (*pbstrKey ! = NULL);
1
FALSE, en cuyo caso el programa de desarrollo visualiza un mensaje de error que explica
el problema. Por ejemplo, aquí tiene cómo Visual C++ maneja la situación cuando el
archivo License.lic se ha alterado o se le ha cambiado el nombre (si intenta este experi-
mento usted mismo, asegúrese de que altera el archivo License.lic en la subcarpeta que
contiene el archivo OCX, porque alterando la copia maestra de la carpeta de proyecto no
tiene efecto una vez que se ha construido el control). Imagine que usted está desarrollando
una aplicación de contenedor llamada DemoContainer y desea añadir el control License
ActiveX al cuadro About de DemoContainer. Cargue el recurso de cuadro About en el
editor de diálogo y haga clic en el botón derecho del ratón sobre el área de trabajo para
visualizar el menú contextual del editor. Elija el comando Insert ActiveX Control del
menú contextual, a continuación seleccione License Control de la lista mostrada en la
Figura 9.5.
El editor de diálogo intenta crear un ejemplo del control License, llevando a una serie
de invocaciones de función anidadas. El editor invoca el método IClassFactory2::Cre-
ateInstanceLic del control, que a su vez invoca la función VerifYUserLicense del control,
que invoca la función AfxVeriJjlLicFile del marco de trabajo. Esta función MFC lee el
archivo identificado por la cadena -s&icFileName -License.lic, en este caso-, y si el
archivo existe, compara la primera línea del archivo con la clave de licencia contenida en
-szLicString. Si el archivo no existe o si la primera línea no concuerda con la clave de
licencia, AfxVeribLicFile devuelve un valor de FALSE para no prohibir la creación del
objeto. El resultado es un mensaje de error de Visual C++ que explica por qué falló el
intento:
318 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
I
Control
- - . - ActiveX en
regsvr32 /u \demo\release\demo.ocx
Cuando se elimina el registro del control satisfactoriamente, puede borrar los archivos
de proyecto. Una función miembro anulada llamada COleContro1::OnDraw traza la ven-
tana de control que se muestra en la Figura 9.6. Como cuando se genera por Control-
Wizard, la función OnDraw visualiza una elipse dentro de un rectángulo blanco:
void CDemoCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcinvalid)
Esta función es uno de los primeros lugares que utilizó cuando desarrolló un control
ActiveX utilizando el ControlWizard. Un proyecto de ejemplo muestra cómo volver a
escribir OnDraw para visualizar una ventana de control con más significado.
Esta sección amplía la sección anterior, presentando un proyecto simple que ilustra cómo
desarrollar un control ActiveX útil que empieza con ControlWizard. He nombrado el pro-
yecto Tower porque es una variación de un puzzle llamado Torres de Hanoi, atribuido al
matématico francés del siglo xrx Edouard Lucas. La Figura 9.7 muestra el control Tower
conforme aparece en un diálogo, visualizado en una ventana rectangular dividida en tres
paneles. El objeto del juego es arrastrar los siete bloques coloreados uno a uno del primer
panel y reunir el montón en el tercer panel. Puede mover un solo bloque coloreado de un
panel cualquiera a otro, pero no puede colocar un bloque encima de otro más pequeño.
Aunque es sólo un juego, el control Tower muestra toda la parafernalia de un control
ActiveX típico. Tower contiene propiedades de bloque y personalizadas, exporta métodos
Figura 9.7. El control ActiveX Tower incrustado en un diálogo típico.
y dispara eventos para permitir a la aplicación de contenedor conocer el estado actual del
juego. Más adelante en este mismo capítulo utilizaremos la utilidad Test Container para
monitorizar los sucesos de control conforme tienen lugar.
Si quisiera construir por sí mismo el control ActiveX Tower, los siguientes ocho pasos
explican cómo hacerlo. Sin embargo, las explicaciones no son específicas del proyecto
Tower, y exploran algunos de los caminos alternativos que puede que desee considerar
cuando instala su propio proyecto de control ActiveX. El primer paso ejecuta Control-
Wizard para crear el proyecto y los siguientes cuatro pasos utilizan ClassWizard para
añadir propiedades, métodos, sucesos y funciones de manejador de mensaje al proyecto
Tower. El sexto paso crea una página de propiedad simple para el control. Con el código
matriz de ClassWizard en su lugar, el séptimo paso muestra cómo dar cuerpo al programa
con código adicional para correr el control, que se construye y se prueba en el octavo paso.
El programa Game de la Figura 9.7 es un simple contenedor escrito explícitamente
para mostrar Tower. Es una aplicación basada en un diálogo creada con la ayuda de App-
Wizard, similar al programa Hour descrito en el capítulo anterior. Como ya hemos estudia-
do este tipo de programa, Garne se menciona sólo brevemente en las secciones subsiguien-
tes. Encontrará código fuente para los programas de contenedor Tower y Game en la
carpeta Codigo\Capitulo.09 del CD que se acompaña.
inicio, el control inicializa el texto de título «Tower», pero un contenedor puede especifi-
car un texto nuevo en la propiedad Caption si así se desea. CurrentBlock es una propiedad
personalizada que contiene un número entero que representa el bloque que se arrastra. El valor
del número entero en CurrentBlock va desde O para el bloque más pequeño hasta 6 para el
bloque más grande. Como el contenedor no tiene motivos para cambiar este valor, Tower
mantiene CurrentBlock como una propiedad de sólo lectura, como se explicará en breve.
La especificación de propiedades en un control ActiveX de MFC como Tower requiere
una colocación precisa y palabras de varias macros, de modo que el trabajo se deja mejor
para ClassWizard. En la pestaña Automation de ClassWizard, haga clic en el botón Add
Property para invocar el diálogo Add Property (Fig. 9.8), y a continuación haga clic en el
cuadro Externa1 Name para visualizar una lista de propiedades de almacenamiento. Añada
una propiedad de almacenamiento al proyecto seleccionándola de la lista. Para especificar
una propiedad personalizada, teclee cualquier nombre externo que no esté en la lista de
nombres de almacenamiento.
El botón de radio Stock en el grupo Implementation le permite especificar inequívoca-
mente si una propiedad es de almacén o personalizada, pero el botón se ajusta por defecto
cuando selecciona una propiedad de almacén. Los botones de radio etiquetados Member
Variable y Get/Set Methods se utilizan normalmente sólo para propiedades personaliza-
das, dándoles dos opciones de cómo su control muestra una propiedad personalizada a una
aplicación de contenedor. Si desea conceder al cliente acceso no restringido a una propie-
dad, deje la instalación del botón de radio Member Variable. ClassWizard crea una varia-
ble para la propiedad que el cliente puede cambiar a través de la página de propiedad y
también genera una rutina de notificación simple que permite al control saber cuándo el
Desplace la pestaña ActiveX Events del diálogo ClassWizard y haga clic en el botón Add
Event para invocar el diálogo Add Event que se muestra en la Figura 9.10. Añadiremos un
evento de almacén más al control Tower y cuatro sucesos personalizados, que de forma
colectiva mantienen informado al contenedor sobre lo que está pasando en el control Tower.
El evento de almacén es Click, seleccionado desde la lista desplegable en el cuadro Exter-
nal Name del diálogo Add Event. El evento Click informa al contenedor de cuándo y
dónde tiene lugar un clic de ratón en la ventana de Tower. Haga clic en el botón OK, a
continuación haga aparecer el diálogo Add Event una segunda vez y teclee el nombre
externo FromPanel para el primer evento personalizado del control. Siempre que el usua-
rio seleccione un bloque en un panel, el control Tower dispara el evento FromPanel invo-
cando la función FireFromPanel. FireFromPanel invoca la función de manejador de
Figura 9.10. Especificación de un evento personalizado para el control ActiveX Tower.
de ClassWizard después de añadir las tres funciones necesarias (otras funciones miembro
de la clase CTowerCtrl, como por ejemplo OnDraw y OnResetState, se generaron ante-
riormente por el ControlWizard). Cuando haya terminado, haga clic en el botón OK para
salir de ClassWizard. Todavía tenemos que escribir código para rellenar todas las funcio-
nes principales que ControlWizard y ClassWizard han añadido al proyecto, pero queda
una tarea más que requiere los servicios del editor de diálogo de Visual C++.
Haga clic en el botón OK para salir de ClassWizard. Ahora el usuario tiene un modo de
cambiar la captura de Tower invocando la página de propiedad en tiempo de diseño y
tecleando una cadena nueva. Veremos cómo se hace eso en la sección siguiente.
El archivo de cabecera TowerCt1.h necesita sólo unos cuantos cambios. Cargue el archivo en
el editor de texto y modifíquelo como se muestra aquí, añadiendo las líneas sombreadas:
/ / TowerCt1.h : Declaración CTowerCtrl ActiveX Control.
#define MAGENTA RGB( 2 5 5 , 0, 255 )
#define RED RGB( 2 5 5 , 0, O )
#define YELLOW RGB( 2 5 5 , 2 5 5 , O )
.......................................................................
/ / CTowerCtrl : Véase TowerCtl.cpp para la implementación.
/ / Constructor
public :
CTowerCtrl ( ) ;
Encontraremos las seis variables de miembro de la clase CTowerCtrl más tarde cuando
expliquemos el código de implementación. La Tabla 9.1 proporciona una descripción bre-
ve de las variables.
TowerCtl.cpp
El archivo de implementación de clase TowerCtl.cpp es lo siguiente. Abra el archivo
haciendo clic en el icono en forma de varita mágica en la WizardBar y desplácese hasta el
mapa de página de propiedad que se muestra aquí. Realice los cambios indicados por las
líneas sombreadas para añadir páginas de propiedades mediante el marco de trabajo MFC
para el color y las propiedades fuente:
.......................................................................
/ / Páginas de propiedad
.......................................................................
/ / CTowerCtr1::CTowerCtrl - Constructor
llll//ll/l//l//l/////////////////////////////////////////////////
/ / CTowerCtr1::-CTowerCtrl - Destructor
El constructor de clase inicializa la matriz color con los colores de bloque e invoca el
método Reset para inicializar la matriz rzPanel. Una invocación a la función COleCon-
tro1::SetInitialSize le da al control Tower un tamaño de ventana predeterminada de 200
por 75 píxeles. Muchos programas de contenedor anulan el tamaño inicial de control cuan-
do crean un sitio, así que la invocación de SetInitialSize es a menudo un esfuerzo inútil
para un control ActiveX. El propósito de la función se hace evidente cuando abre el con-
trol en la utilidad Test Container, que acepta cualquier tamaño inicial que establezca un
control para sí mismo. Como vimos en el Capítulo 8, algunos controles ActiveX, como por
ejemplo Button Menu, aparecen en el Test Container como un bloque cuadrado pequeño al
que el usuario cambia de tamaño para mostrar la ventana de control. Eso no sucede en
Tower porque ajusta las dimensiones de la ventana predeterminada para sí mismo a través
de SetInitialSize.
.......................................................................
/ / CTowerCtr1::OnDraw - Dibujar la función
void CTowerCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
332 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
.......................................................................
/ / CTowerCtr1::DoPropExchange - Soporte persistente
.......................................................................
/ / CTowerCtr1::OnResetState - Resetear el controla suestadopredeterminado
void CTowerCtrl::OnResetState()
{
COleControl::OnResetState(); / / Volver a los valores predeterminados
/ / en DoPropExchange
1
.......................................................................
/ / CTowerCtr1::AboutBox - Visualizar un cuadro "About" al usuario
void CTowerCtrl::AboutBox()
{
CDialog dlgAbout(IDD_ABOUTBOX_TOWER);
dlgAbout .DoModal ( ) ;
334 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
short CTowerCtrl::GetCurrentBlock()
(
return nPanel [nFromPanel][nBloclcNdxl;
1
Tower simula el arrastrar y soltar monitorizando el botón izquierdo del ratón. Cuando
el usuario pulsa el botón del ratón dentro de la ventana de Tower, el control cambia el
cursor a una forma de cruz, proporcionando retroalimentación visual simple al usuario que
indica que se está realizando la operación de arrastre. La función PreCreateWindow, invo-
cada cuando el contenedor incrusta primero el control Tower, carga el cursor en forma de
cruz y almacena el manejador en hCrossHairs. La función también invoca COleCon-
tro1::SetText para inicializar la propiedad Caption.
COleControl::OnLButtonDown(nFlags,point);
1
Cuando el usuario pulsa el botón izquierdo del ratón en algún sitio sobre la ventana de
Tower, la función OnLButtonDown maneja el mensaje WM-LBUTTONDOWN resultan-
te. La primera función examina las coordenadas de clic en el argumento point y determina
en qué panel tiene lugar el clic. Si el panel está vacío, se ignora el clic. De lo contrario,
OnLButtonDown cambia el cursor a una forma de cruz y dispara el evento FromPanel para
informar al contenedor de que se está arrastrando un bloque.
Como sólo se puede mover el bloque más pequeño en un panel, OnLButtonDown sólo
necesita determinar en qué panel tiene lugar el clic, no en qué bloque. Aunque esto simpli-
fica mucho la prueba de acierto en la función de ayuda GetPanel, tiene el efecto de empe-
zar una operación de arrastre para un bloque incluso si el clic no aterriza con precisión en
un bloque.
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO MFC 337
1
else / / Si el soltar es inválido,
FireError () ; / / indicárselo al contenedor
1
bMoving = FALSE; / / Ahora no se mueve
COleControl::OnLButtonUp(nFlags, point);
Towerppg.cpp
En el paso 6 (pág. 327) modificamos la página de propiedad genérica de Tower añadiendo
un cuadro de texto que permite al usuario volver a escribir la propiedad Caption. Tower
almacena los contenidos del cuadro de texto en la variable strcaption, que es un objeto
Cstring creado en ClassWizard. Se necesita un enlace entre la cadena strcaption y la
propiedad de almacén Caption, de modo que cuando el usuario cambia strcaption en la
hoja de propiedad Tower, el control adelanta el cambio para almacenar la propiedad ente-
rrada en el marco de trabajo.
Este es el propósito de las funciones de transferencia de datos de propiedad de MFC,
reconocibles por el prefijo DDP-. La función que necesitamos para Tower es la función
DDP-Text, que copia texto desde una variable de cadena (strCaption) para una propiedad
de cadena (Caption).Para añadir la invocación DDP-Text, abra el archivo de implementa-
ción TowerPpg.cpp en el editor de texto e inserte la línea sombreada que se muestra aquí:
Tower.rc
Por defecto, ControlWizard etiqueta la página de propiedad genérica, almacenando la
etiqueta como un recurso de cadena en el archivo de guión Tower.rc. Para cambiar la
etiqueta de pestaña, abra el archivo Tower.rc en el editor de texto y cambie «General» a
«Caption» en la línea sombreada que se muestra a continuación:
STRINGTABLE DISCARDABLE
BEGIN
IDS-TOWER "Tower Control"
END
También puede llevar a cabo la misma modificación utilizando el editor de cadena de
Visual C++, descrito en el Capítulo 4. La Figura 9.13 muestra el resultado.
bTower
Un icono es generalmente demasiado grande para este propósito, así que los ActiveX
estándar sugieren la inclusión de una imagen de mapa de bit de 16 por 16 en unos datos de
recurso de control. Los controles que proporcionan dicho recurso lo anuncian a través de la
clave ToolboxBitmap32 en los datos Registry de clase. Encontraremos esta clave otra vez
en el capítulo siguiente.
Ajuste la configuración de proyecto a Win32 Release y elija el comando Build desde el
menú Build. Cuando el código fuente se compila y enlaza con éxito, Visual C++ registra
automáticamente el control. Si desea experimentar con Tower sin construirlo como un
proyecto, debe registrar el control usted mismo antes de utilizarlo. Para registrar Tower,
copie primero el archivo Tower.ocx en su disco duro si es necesario y a continuación
ejecute la utilidad RegSvr32:
trol. De las otras tres pestañas del diálogo, la etiquetada Caption visualiza la página de
propiedad modificada en el paso 6. Cuando teclee el texto nuevo para la captura, el cambio
se refleja inmediatamente en la ventana de control.
El Test Container visualiza un registro en tiempo real de los sucesos de Tower y una
ayuda de valor incalculable cuando está depurando un control ActiveX. Desplace la barra
separadora hacia arriba para mostrar el espacio adicional para el acceso de evento, a conti-
nuación arrastre un bloque del primer panel de Tower y despliéguelo en otro panel. Res-
pondiendo a la operación de arrastrar y soltar, Tower dispara los sucesos Click, From-
Panel y ToPanel, que se registran en el acceso de evento a medida que tienen lugar. Como
se muestra en la Figura 9.14, cada entrada del acceso empieza con una etiqueta que indica
desde qué control se origina el evento. Como el Test Container puede incrustar más de un
control, las etiquetas ayudan a mantener las entradas de acceso cuando los sucesos tienen
lugar en grupos desde controles que puede que no tengan la atención.
2. Pulse CTRL+S para guardar el recurso de diálogo nuevo, a continuación haga clic
en el comando ClassWizard en el menú View. Cuando se le pregunte si le gustaría
crear una clase nueva para el recurso de diálogo, haga clic en el botón OK para
aceptar. Teclee un nombre para la clase nueva y seleccione COlePropertyPage
como la clase base:
STRINGTABLE DISCARDABLE
BEGIN
IDS-TOWER "Tower Control"
BOOL CTowerPropPage2::CTowerPropPage2Factory
::UpdateRegistry(BOOL bRegister)
I
if (bRegister)
return AfxOleRegisterPropertyPageclass(
AfxGetInstanceHandleO,
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO MFC 343
m-clsid, I
else
return AfxOleUnregisterClass(m-clsid, NULL);
1
' Una plantilla de clase C++, también conocida como tipo parametrizado, es una forma sofisticada de macro
que el compilador convierte en una definición de clase normal basada en parámetros pasados a la plantilla. Al
contrario que las macros creadas con la sentencia #define, las plantillas se convierten en tipos normales, hacién-
dolas seguras del tipo. El compilador puede supervisar el uso del programa de una clase convertida en plantilla
como lo hace con una clase normal y reconoce correctamente cualquier tipo de disparidad.
346 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ha mejorado ATL añadiendo un soporte especial para los controles ActiveX. Hoy, la sabi-
duría que prevalece sugiere estas pautas cuando elija una herramienta de desarrollo para
los proyectos de control ActiveX:
m Considere MFC sólo para crear controles pensados para aplicaciones de contenedor
normal, como el programa Game del Capítulo 9. MFC adquiere más atractivo como
una herramienta de desarrollo cuando el propio contenedor enlaza dinámicamente
con MFC, ya que el control no es entonces responsable de cargar la biblioteca. La
condena ya se ha cumplido, por así decirlo.
Para los controles ActiveX que puedan servir para Internet, utilice en su lugar ATL.
Encontrará que este capítulo explica los asuntos COM con más detalle que los dos
capítulos anteriores, pero también muestra cómo ATL le permite escribir controles Ac-
tiveX sofisticados sin sumergirle en COM. Más aún que MFC, ATL coloca al desarrolla-
dor más cerca de la superficie COM, pero no necesariamente bajo ella.
A TL Y APLICACIONES DE CONTENEDOR
Aunque no sea el tema central de este capítulo, el soporte de ATL para la programación de
contenedor garantiza unas cuantas palabras antes de abandonar completamente el tema.
ATL corta una amplia franja de la programación COM, pero se apoya pesadamente sobre
el desarrollo de los servidores, no clientes. Sin embargo, la biblioteca proporciona dos
plantillas de clase, llamadas CComPtr y CComQIPtr, que a menudo resultan eficaces
cuando escriben código de cliente. Estas plantillas crean punteros de interfaz inteligentes,
diseñados para asegurar si un cliente lanza interfaces de control incluso cuando un error de
excepción interrumpe el curso normal de la ejecución.
Vamos a ver primero el problema y después la solución. Un contenedor invoca un
método QueryInter$ace para solicitar punteros para interfaces que el control proporciona.
La única limitación es que cuando se termina de utilizar una interfaz, el contenedor debe
invocar el método Release de la interfaz para informar al control que la interfaz ya no se
necesita. Si se falla al hacer esta regla fundamental de COM, puede abandonar el objeto de
control aislado en el pozo de memoria del sistema después de que haya terminado el
control. Un fragmento demuestra cómo un contenedor solicita un puntero para una de las
interfaces de control, utilizando un ejemplo de IOleObject:
destructor del puntero, que invoca el método Release de la interfaz, se ejecuta incluso si el
programa termina de forma abrupta mientras que IOleObjects está en uso:
Una diferencia notable entre los dos fragmentos es que la versión revisada no invoca
explícitamente IOle0bject::Release cuando termina de utilizar IOleObject. Cuando
pOleObj sale del ámbito, ya sea porque Functionl vuelve normalmente o porque tiene
lugar un error, el destructor del puntero se encarga de soltar la interfaz.
La función tiene otra ventaja en lanzarpOleObj como un puntero inteligente. Recupe-
rar el puntero de interfaz pUnk es más simple ahora, porque CComPtr proporciona su
propia función QueryIntegace, que deduce el identificador de interfaz del propio
p01eObj. Además de la conveniencia de codificación, esto también le asegura la seguridad
de tipo, garantizándole que tanto el identificador (IID-OleObject) como el puntero de
objeto (p01eObj) se refieren a la misma interfaz (IOleObject).De este modo, se le previe-
ne para que no cometa errores como:
IUnknown es rara vez la interfaz que un cliente necesita en última instancia, pero es la
única interfaz de la que el cliente puede estar seguro y que soporta controles ActiveX.
Obtener una interfaz es normalmente un proceso de dos pasos en el que el invocador
obtiene primero un puntero IUnknown -pUnk en el código de fragmento-, luego invoca
IUnknown::Querylnte$ace para recuperar el puntero de interfaz que se necesita actual-
mente. ATL proporciona otro puntero inteligente que combina estos dos pasos en uno. La
plantilla CComQIPtr incorpora una invocación a IUnknown::QueryInte~aceen uno de los
constructores. Aquí tiene una lista condensada de la plantilla CComQIPtr de ATL que
muestra cómo el constructor obtiene el puntero de interfaz deseado y cómo el destructor lo
recupera más tarde:
348 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
CComQIPtr( IUnknown* lp )
I
p = NULL
if (lp !=NULL)
lp->QueryInterface( *piid, (void **) &p );
1
-CComQIPtr ( )
(
if (P)
p->Release( ) ;
CComQIPtr<IoleObject> p01eObj;
pOleObj = punk;
if (p01eObj.p)
{
/ / Utiliza la interfaz IOleObject
}
Un invocador puede utilizar CComQIPtr para crear un puntero para cualquier interfaz,
excepto IUnknown. Para crear un puntero inteligente para IUnknown, utilice CComPtr en
su lugar.
Las plantillas CComPtr y CComQIPtr de ATL están inspiradas en la clase de puntero
inteligente auto-ptr de la Biblioteca de plantillas estándar, lo que asegura que un objeto
reservado mediante new se devuelve de forma adecuada al almacén libre de la aplicación.
Aunque las plantillas obtienen beneficio principalmente de las aplicaciones de cliente que
utilizan controles ActiveX, ellas mismas pueden dar servicio a controles con la misma
facilidad, y son particularmente útiles cuando un control agrega o contiene otro control.
Un proyecto de ejemplo más adelante en este capítulo demuestra cómo un control ActiveX
puede hacer uso de punteros inteligentes.
El soporte de ATL para la programación de cliente es estrictamente pasivo, consistien-
do sólo en archivos de código fuente para inclusión en un proyecto. Estos archivos, reco-
nocible~por su prefijo Atl, residen en la carpeta VC98WTL\Include. Por ejemplo, el
proyecto de contenedor que hace uso de CComPtr y CComQIPtr se refiere al proyecto
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO ATL 349
AtlCon en la carpeta Samples\VC98\ATL de MSDN. ATL tiene mucho más para contri-
buir al desarrollo de los servidores COM como los controles ActiveX, y para el resto del
capítulo nos centraremos en este aspecto de ATL.
Tabla 10.1. Interfaces que debe soportar un control ActiveX para que cumpla
las directrices
Interfaz Descripción
Como MFC, ATL produce una sola clase representativa para cada objeto que el control
contiene. La clase deriva de todas las interfaces que el objeto soporta, un truco que hace
MFC anidando clases. ATL cumple el mismo resultado con más flexibilidad utilizando
herencia múltiple en la que la clase deriva de vanas clases base, heredando datos de miem-
bro y funciones de cada una. La lista de clases base, llamada lista de herencia, es así en la
declaración de clase:
Class CmyClass :
public CClassl,
public CClass2,
public IInterfacel,
public IInterface2,
El servicio más importante que proporciona ATL para el desarrollo de controles Acti-
veX es el código de implementación de biblioteca para muchas interfaces COM que nor-
malmente soportan los controles. Tomando la forma de plantillas de clase, el código de
biblioteca le ahorra tener que escribir su propio código para soportar interfaces comunes.
ATL proporciona código de implementación con plantillas para cada interfaz enumerada
en la Tabla 10.1 (y unas cuantas más), dándole a cada plantilla el nombre de la interfaz
seguida por un sufijo Impl. La plantilla IQuickActivateImpl, por ejemplo, proporciona
código para los métodos IQuickActivate, como SetExtent y GetExtent. Como lo requiere
COM, aparecen todos los métodos de una interfaz soportada, pero no dan servicio necesa-
riamente. Muchos métodos simplemente invocan la macro ATLTRACENOTIMPL, que
escribe un mensaje de pista escueto a la pestaña Debug de la ventana Output de Visual
C++ y devuelve E-NOTIMPL al invocador. Si desea que su control ActiveX dé servicio a
métodos semejantes, debe añadir código usted mismo.
Junto con su biblioteca de código, ATL también proporciona un AppWizard que le
inicia en un proyecto de servicio y otro asistente que genera código de clase necesario para
su control ActiveX. Una vez que se familiarice con ATL, no lo encontrará más difícil que
utilizar MFC, pero ATL espera que vigile más detalles de proyecto que MFC y que trate con
más asuntos COM. Antes de apresurarnos a la construcción de un proyecto de demostra-
ción con ATL, vamos a paramos aquí y adquirir un poco de fondo en tres aspectos de ATL
que encontraremos más tarde: mapas de interfaz, mapas de objeto y modelos de hilos.
Mapas de interfaz
Los mapas de interfaz surgen del modelo común típico de la mayor parte de las implemen-
taciones de la función QueryInter$ace. Todas las interfaces de un objeto COM soportan
esta función, permitiendo que una aplicación de contenedor invoque Querylnteface en
cualquier interfaz y reciba un puntero para cualquier otra interfaz soportada. Si el control
no soporta la interfaz solicitada, devuelve un valor E-NOINTERFACE. El fragmento
siguiente ilustra cómo puede ser una función QueryIntegace cuando no se utiliza ATL. El
parámetro riid es una referencia al identificador de la interfaz que el invocador está solici-
tando, y el parámetro ppvobject recibe el puntero de interfaz.
STDMETHODIMP MyClass::QueryInterface( REFIID riid, PVOID *ppvObject )
{
switch( riid )
{
case IID-UNKNOWN:
case IID-IInterfacel:
*ppvObject = (IInterfacel*) this;
break;
352 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
case IID-IInterface2:
*ppvObject = (IInterface2*) this;
break;
case IID-IInterface3:
*ppvObject = (IInterface3") this;
break;
de£ault :
*ppvObject = 0;
return E-NOINTERFACE;
(IUnknownx) *ppvObject->AddRefO;
return S-OK;
Este tipo de modelo repetitivo está dispuesto para prestarle macros él mismo, una
explicación de lo cual aclarará la composición física del mapa de interfaz. Cuando un
cliente invoca el método QueryInte$ace de objeto de clase, el mapa de interfaz encamina
la invocación a través de la función ATL CComObjectRootBase::InternalQueryInter$ace,
prototipada en el archivo At1Com.h así:
Static HRESULT WINAPI InternalQueryInterface( PVOID pThis,
const ATL-INTMAP-ENTRY *pEntries, REFIID riid, PVOID *ppvObject )
Como antes, riid identifica la interfaz y ppvobject recibe su puntero. El segundo pará-
metro de la función pEntries apunta al principio del mapa de interfaz, que consiste en una
matriz de las estructuras -ATL-INTMAP-ENTRY. Cada estructura de la matriz contiene
un identificador de interfaz, una variable DWORD y un puntero de función:
Struct ATL-INTMAP-ENTRY
{
const IID* piid / / Identificador de interfaz
DWORD dw ; / / Desplazamiento
-ATL-CREATORARGFUNC* pFunc; / / Puntero de función
>;
El valor de pFunc determina cómo interpreta InternalQueryInte$ace el valor de dw. Si
pFunc es ATL-SIMPLEMAPENTRY (definido como l), dw contiene un desplazamiento
en el objeto de clase, permitiendo que InternalQuerylnte$ace cumpla la invocación como
esta:
En el código que utiliza ATL se forma un mapa de interfaz mediante una serie de
macros COM-INTERFACE, cada una de las cuales se expande en una estructura
ATL-INTMAP-ENTRY. ATL proporciona 17 macros diferentes COM-INTERFACE
identificadas por sufijos, como por ejemplo E N T R Y y -TEAR-OFF, que indican el tipo
de interfaz que maneja la macro. El nombre de macro se forma uniendo la cadena de sufijo
a COM-INTERFACE, como en COM-INTERFACE-ENTRY. La Tabla 10.2 describe las
macros de COM-INTERFACE y explica cuándo las utiliza el usuario; para ayudar a man-
tener la tabla despejada, la primera columna enumera sólo el sufijo de macro. Para más
información sobre las macros, consulte el artículo «COM-INTERFACE-ENTRY Ma-
cros» en la ayuda en línea.
ATL se refiere a las series de macros en el código fuente como un mapa COM, un
término a menudo utilizado de forma intercambiable con el mapa de interfaz que crean las
macros. Un mapa COM en ATL comienza invocando la macro BEGIN-COMMAP y
termina con la macro END-COM-MAP. Entre ellos hay una serie de macros COM-IN-
TERFACE, uno para cada interfaz que la soporta:
La forma del mapa se parece mucho al mapa de mensaje estándar de MFC (MFC
proporciona sus propias macros para la creación de un mapa de interfaz, aunque no los
vimos en el Capítulo 9. Un mapa de interfaz en MFC comienza y acaba con las macros
BEGIN-INTERFACEMAP y END-INTERFACEMAP). El orden de las estructuras de
mapa COM no es importante, pero la primera interfaz en la lista debe utilizar una entrada
de mapa simple, es decir, COM-INTERFACE-ENTRY o cualquier otra macro COM-IN-
TERFACE que se expanda a una estructura -ATL-INTMAP-ENTRY con un valor pFunc
de ATL-SIMPLEMAPENTRY. Esta necesidad brota del uso de la primera interfaz en el
mapa que responde a las solicitudes para la interfaz IUnknown del objeto.
Mapas de objeto
Un control ActiveX puede contener varios objetos, cada uno representado por una clase y
cada uno proporcionando su propio mapa de interfaz. La Figura 10.1 muestra la relación
jerárquica entre un control, el objeto que contiene y las interfaces implementadas por los
objetos. Igual en concepto a un mapa de interfaz, un mapa de objeto sigue la pista de
objetos de control, asociando cada objeto con su identificador de clase (CLSID). ATL
administra un mapa de objeto como una matriz de las estructuras ATL-OBJMAFENTRY,
cada una de las cuales define una serie de funciones de asistente. Aquí tiene una forma
abreviada de la estructura ATL-OBJMAP-ENTRY que muestra los prototipos de función.
Struct ATL-OBJMAP-ENTRY
{
HRESULT UpdateRegistry( BOOL bRegister ) ;
HRESULT GetClassObject( void* pv, REFIID riid, PVOID* ppv ) ;
HRESULT CreateInstance( void* pv, REFIID riid, PVOID* ppv ) ;
LPCTSTR GetObjectDescriptionO;
HRESULT RevokeClassObjectO;
HRESULT RegisterClassObject( DWORD dwClsContext, DWORD dwFlags 1 ;
1;
Cuando un cliente solicita primero un objeto de clase, se invoca la función GetClass-
Object para crear un ejemplo de objeto y proporcionar al invocador un puntero a la interfaz
IClassFactory o IClassFactory2 solicitada. La función almacena el puntero de clase de
fábrica dentro de la estructura de mapa de objeto, haciendo que las solicitudes subsiguien-
tes para un ejemplo nuevo del objeto se lleven a cabo más rápidamente. Como la clase de
objeto se instancia en la pila o en la pila de memoria en lugar de en los datos estáticos de
control, los controles ActiveX construidos con ATL no necesitan enlazarse a una bibliote-
ca de tiempo de ejecución de C. Al evitar que las funciones de tiempo de ejecución y
constructores estáticos de C asegura que el tamaño del ejecutable del control terminado
permanece pequeño, libre de código de inicialización extra que de otro modo enlazaría la
biblioteca de C.
Modelos de hilos
ATL soporta cuatro modelos de hilos, llamados sencillo, apartamento, libre y ambos. Un
modelo de hilos describe el tipo y grado de la seguridad de hilos que implementa un
control, aunque cualquier aplicación de cliente, independientemente de su propia adminis-
tración de hilos, puede acceder de forma segura al control construido desde cualquier
modelo de hilo. Si el hilo de cliente no es compatible con el del servidor, COM se interpo-
ne entre los dos para asegurar la comunicación segura de hilos. Dada esta seguridad, la
selección de un modelo de hilos para su control no tiene que ser una decisión agonizante.
Hay pros y contras para cada elección, por supuesto; generalmente, enfrentar el rendimien-
to con el tamaño del código y eficacia con simpleza. El código que ATL añade a un
proyecto soporta cualquier modelo de hilos que seleccione, así que sólo necesita asegurar-
se de que el código que usted mismo escribe también cumple con los requisitos del modelo
seleccionado. Esta sección explica las diferencias entre los cuatro posibles modelos, ayu-
dándole a decidir el mejor para su proyecto.
Es más fácil prever los hilos desde la perspectiva del cliente. Vamos a empezar echan-
do un vistazo a los modelos de hilos tal y como se aplican a una aplicación de contenedor,
y a continuación examinaremos cómo afecta cada modelo de hilos al control ActiveX que
la aplicación incrusta. Los modelos de hilos no son difíciles de entender, pero las reglas a
veces son complicadas. Un ejemplo de proyecto que veremos más adelante en este capítu-
lo aplica algo de la teoría explicada aquí para ilustrar cómo se comporta un control bajo los
diferentes modelos de hilos.
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO ATL 357
Hilos únicos
El modelo de hilo único es el más simple de los cuatro, porque no es necesario que se evite
el control del uso simultáneo de sus datos, incluso si son datos estáticos. El modelo permi-
te a un cliente crear cualquier número de ejemplos de un objeto de control, pero confina a
un hilo único todos los accesos de cliente a esas instancias. Los hilos únicos no restringen
el número de hilos que un cliente puede ejecutar, y por supuesto un control ActiveX
debería asumir que muchos de sus clientes son multihilos. El modelo sólo dicta que todas
las invocaciones a las interfaces de objeto se hacen desde un hilo de cliente que primero
invoca CoInitializeEx para inicializar el marco de trabajo COM. Como el control ActiveX
no requiere esfuerzo extra para asegurar el acceso seguro de hilo a sus interfaces u otros
datos, el hilo único proporciona el tamaño de objeto más pequeño de los cuatro modelos de
hilo. La comunicación entre cliente y servidor es directa y rápida, siempre que el cliente se
adhiera a las restricciones de modelo.
Pero considere lo que sucede cuando dos hilos en el cliente utilizan el mismo control
ActiveX. El hilo A, que no es necesariamente el hilo principal del cliente, inicializa COM
a través de CoInitializeEx, a continuación invoca CoCreateInstance para crear un ejemplo
del objeto de control. Como consecuencia, el hilo B necesita los servicios del control, y al
igual que el hilo A, crea un nuevo ejemplo del control. Pero esta vez COM no devuelve
un puntero de interfaz que apunte directamente al código de control, como hizo cuando el
hilo A invocó CoCreateInstance. En su lugar, el puntero devuelto hace referencia a un
objeto proxy invisible que se ejecuta en el hilo B. Cuando el cliente utiliza el puntero para
invocar un método desde el hilo B, el proxy envía un mensaje a un objeto matriz que se
ejecuta en el hilo A. Cada matriz invoca directamente al control ActiveX en el mismo hilo,
entonces vuelve a pasar el valor de retorno por medio de otro mensaje. Este intercambio de
mensajes cambia entre los hilos y asegura que el control ActiveX siempre se ejecute den-
tro del contexto del hilo A, un hilo que inicializó por primera vez a COM.
Volver a encaminar una invocación desde un hilo y completarla en otro se conoce
como clasificación entre hilos, que es conceptualmente similar a la clasificación entre
procesos, explicada en el Capítulo 8. La diferencia principal es que el proxy y los objetos
matriz que llevan a cabo la clasificación entre hilos están ocultos en las ventanas ajustadas
por COM, no bibliotecas dinámicas de enlace ajustadas en procesos separados. Los men-
sajes entre las ventanas son análogos a las invocaciones de procedimiento remoto que
pasan en un sentido y en otro entre un proxy en proceso de cliente y su matriz fuera de
proceso. La clasificación entre hilos es normalmente más rápida que la clasificación entre
procesos, pero intercambiar mensajes y cambiar los contextos de hilo todavía ralentiza el
acceso a un ejemplo de objeto varios cientos de veces si se compara con la invocación
directa al objeto. Cada vez que el cliente invoca uno de los métodos de control desde el
hilo B, se repite el mismo proceso tortuoso para clasificar la invocación al hilo A. Todavía
peor, el hilo A puede que esté ocupado lejos de su bucle de mensaje, en cuyo caso los
mensajes enviados al proxy deben esperar en la cola hasta que se extraigan y encaminen a
la matriz. Todas las invocaciones de métodos que el cliente hace desde el hilo A van
directamente al control y no se clasifican, dando al hilo A unos privilegios especiales no
concedidos a otros hilos que utilicen el control. El modelo único de hilos tiene una penali-
zación de rendimiento sólo cuando el cliente invoca el control desde los hilos que no son el
primer hilo que se registra a sí mismo con COM a través de CoInitializeEx.
358 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Hilos de apartamento
Hilos libres
Hilos libres es sólo otro nombre para el modelo de apartamento multihilo. Los hilos dentro
de un apartamento multihilo pueden compartir con seguridad punteros de interfaz aplica-
dos por una única instancia de un objeto independientemente de qué hilo creó el ejemplo.
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO ATL 359
COM queda al margen y no clasifica invocaciones que tengan lugar dentro de los confines
del apartamento. Sin embargo, al igual que en el modelo de apartamento de hilo único, es
necesaria la clasificación cuando un hilo invoca un objeto instanciado en otro apartamento.
Esto suena bastante simple, pero el hilo libre deposita una gran responsabilidad sobre
sus espaldas, el desarrollador. Elegir el soporte del modelo de hilo libre quiere decir escri-
bir código que se pueda acceder de forma segura en cualquier momento y por cualquier
número de hilos. La sección siguiente echa un vistazo a algunos de los requisitos que cada
modelo impone en un control ActiveX.
Entender los modelos de hilos en el cliente COM le permite elegir el mejor modelo de hilo
para su control ActiveX. Afortunadamente, los hilos en los servidores implican más códi-
go y menos teoría. Sólo necesita seleccionar un modelo de hilo para su control y escribir el
código correspondiente, estando seguro de que COM resolverá cualquier error de concor-
dancia mediante la clasificación. Un hilo de cliente se ajusta al modelo STA o MTA, pero
un control ActiveX adopta uno de los cuatro modelos enumerados anteriormente: único
(sin hilo), apartamento (STA), libre (MTA) o ambos. El modelo de hilos ambos significa
STA y MTA; es decir, el control debe escribirse para ajustarse a cualquiera de los dos modelos
posibles de un hilo de cliente sin necesidad de que COM clasifique las interacciones.
Hemos visto cómo un hilo de cliente identifica su modelo para COM cuando invoca
ColnitializeEx, pero el hilo principal de un control ActiveX, que es el hilo que recibe las
invocaciones de cliente, no se registra a sí mismo. Esto tiene sentido porque, después de
todo, estamos hablando de sólo un hilo junto con el que la lógica de programa fluye del
cliente en el control ActiveX y de nuevo de vuelta, sin pasar a través del clasificador. Un
control identifica su modelo de hilo a través de una entrada en el registro de sistema, ya sea
de apartamento, libre o ambos. Si no aparece la entrada, COM supone que el control se
ajuste al modelo de hilo único. Los proyectos que veremos más tarde en el capítulo mues-
tran cómo un control ActiveX diseña su modelo de hilo en el registro.
Al habernos metido tan de lleno en el laberinto de los modelos de hilos, podemos
finalmente abordar una cuestión importante: ¿cómo sabe por anticipado un control Ac-
tiveX qué modelo utiliza un cliente? La respuesta es de hermosa simpleza: no lo sabe, ni
tampoco necesita saberlo. El modelo de hilos que selecciona para su control le dice a COM
el tipo de hilo de cliente para cuyo manejo está diseñado su control sin la necesidad de
clasificación. COM reconoce cuándo sucede cualquier error de concordancia y ajusta de
forma transparente la clasificación sólo cuando es necesario resolver las diferencias. Por
ejemplo, un cliente de hilos STA que instancia el control marcado apartment interactúa
directamente con el ejemplo de control. Cuando un hilo MTA instancia el control, sin
embargo, COM debe clasificar todas las invocaciones para asegurar tanto al cliente como
al control que corren en el mismo hilo. Registrando el modelo de hilo de apartamento, el
control ha informado a COM de que cada ejemplo puede acomodar sólo las invocaciones en
un único hilo; la clasificación asegura que esto es lo que pasa. La Tabla 10.3 resume bajo
qué condiciones COM clasifica las invocaciones entre un cliente y su control incrustado.
Qué modelo de hilo es el mejor para su control depende del tipo de aplicación cliente
que anticipe que utilizará su control y de cuánto trabajo extra quiere realizar para asegurar
360 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Tabla 10.3. Condiciones bajo las que las clasificaciones COM invoca entre cliente
y control
el acceso seguro a los hilos. El modelo de hilos no único no necesita código extra en
absoluto para adquirir la seguridad de hilo, ya que un único hilo de cliente puede acceder
directamente a todos los ejemplos del control. Los hilos libres, por otro lado, sirven mejor
sólo si sabe por adelantado que su control lo utilizarán exclusivamente aplicaciones MTA.
Sin embargo, rara vez tiene esta seguridad, a no ser que escriba al cliente usted mismo.
Como la gran mayoría de aplicaciones cliente hoy día se ajustan a los hilos STA, el mode-
lo de apartamento generalmente representa la mejor opción para un control ActiveX dise-
ñado para servir a muchos clientes diferentes. Internet Explorer y Netscape Navigator son
aplicaciones STA, como los programas de contenedor que utilizan MFC y aquellos escri-
tos en las versiones 5 de Visual Basic y posteriores. Microsoft Transaction Server también
se ajusta a las reglas de STA, así que un control debería utilizar el modelo de apartamento
si soporta MTS.
La seguridad de hilo se programa fácilmente bajo el modelo de apartamento, que nece-
sita sólo garantía contra la escrita simultánea de datos estáticos, normalmente a través de
secciones críticas o algunos mecanismos similares. Si desea asegurar el acceso rápido a su
control independientemente del modelo de hilos de cliente, elija el modelo ambos. Ajustar
el acceso a STA y MTA sin clasificación requiere trabajo extra, especialmente para con-
troles que ejecuten más de un hilo. Para echar un vistazo a lo más profundo de los hilos
MTA, lea detenidamente el artículo de David Platt en Microsoft Systems Journal, volu-
men 12, número 8. Puede localizar el artículo en la ayuda en línea de MSDN bajo la
entrada Periodicals en la pestaña Contents.
evstudio Add-inWizard
tended Stored ProcedureAppWizard
API ExtensionWizard
FC ActiveX ControlWizatd
Object Wizard añade automáticamente código ATL a proyecto para cualquiera de los
17 tipos de objetos diferentes, algunos de los cuales se enumeran en la Tabla 10.4. La
opción Full Control se selecciona de forma normal cuando desarrolla un proyecto de con-
trol ActiveX, pero la opción presupone que el control visualiza una ventana y soporta lujos
tales como una hoja de propiedad y un icono representativo. Para un control invisible
pequeño como Pulse, normalmente es preferible elegir uno de los tipos de componente y
construirlo a partir de ahí, en lugar de tener que eliminar más tarde el código no deseado.
El proyecto Pulse no necesita mucho código inicial, así que seleccione Objects en el panel
de la izquierda del diálogo del asistente y haga dos veces clic en el icono etiquetado
Simple Object, como se muestra en la Figura 10.5.
Object Wizard visualiza a continuación el diálogo Properties (Fig. 10.6), que consulta
las características del control. En la pestaña Names, teclee PulseCtl para el nombre corto,
desde el que el Object Wizard rellena los otros cuadros de edición con entradas apropia-
das. El cuadro de edición Class contiene el nombre de la clase CPulseCtl que implementa
sólo el objeto de control. Esta clase es importante porque hereda todas las interfaces que el
control Pulse soporta.
Add-in Object lnternet
Tabla 10.4. Tipos de objeto común que soporta ATL Object Wizard
Simple Object Crea un simple objeto COM. Se debe añadir manualmente la interfaz de
soporte.
Add-in Object Crea un simple objeto añadido que conecta con la interfaz IApplication de
Developer Studio. El Capítulo 13, ~Personalizaciónde Visual C++», ex-
plica con más detalle los añadidos de Developer Studio.
Internet Explorer Crea un objeto COM que soporta las interfaces esperadas por Internet
Object Explorer, pero sin soporte adicional para una interfaz de usuario.
ActiveX Server Añade soporte para OnStartPage y OnEndPage, con punteros a interfaces
Component ASP, tales como IRequest, IResponse y IServer.
(Continúa)
366 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Microsoft Transaction Crea un archivo de implementación esqueleto que incluye el archivo ca-
Server becera Mtx.h necesario por un servidor de transacciones.
Component Registrar Suministra acceso al sistema Registry mediante el Registrar de ATL im-
plementado por la interfaz [Registrar.
Lite Control Crea un control con una interfaz de usuario que se puede incrustar en
Internet Explorer, pero que no soporta interfaces necesarias por muchos
otros contenedores. Suministra punteros a las interfaces de cliente IOleln-
PlaceSiteWindowless, IOleClientSite e IAdviseSink.
Full Control Crea un control que se puede incrustar en todos los contenedores que
cumplen con las directrices ActiveX. Suministra los mismos punteros a
interfaces sitio que el Lite Control.
Composite Control Crea un control similar a un cuadro de diálogo que puede contener otros
controles ActiveX y controles normales.
Property Page Agrega un objeto de página de propiedad al control del proyecto. Selec-
cione esta opción una vez para cada página de una hoja de propiedad.
Dialog Agrega un recurso de diálogo genérico al proyecto.
Provider Crea un objeto que realiza los servicios de traducción de datos de un
proveedor OLE DB.
Object Wizard escribe el código fuente de clase CPulseCtl a los archivos H y CPP. El
cuadro de edición CoClass mantiene el nombre de la clase de componente de control, que
sirve como el equivalente de biblioteca de tipo de la clase de objeto. La clase CPulseCtl y
la clase de componente PulseCtl se refieren al mismo objeto, pero distinguiendo entre los
dos lugares donde el objeto está definido. CPulseCtl se refiere al código fuente de C++ que
Un cliente que posea sólo un identificador programático para un control, puede deter-
minar el identificador de clase correspondiente invocando la función OLE CLSIDFrom-
ProgID. La función ProgIDFromCLSID lleva a cabo la traducción opuesta.
En la pestaña Attributes, seleccione el cuadro de comprobación Support Connection
Points, que se muestra en la Figura 10.7. Los puntos de conexión son necesarios para los
controles ActiveX como Pulse que dispara eventos. La misma pestaña visualiza botones
de radio que determinan el modelo de hilo de control, tipo de interfaz y si soporta agrega-
ción. Ya hemos visto los modelos de hilos que soporta ATL, pero las interfaces duales y la
agregación merecen más explicación.
Dual interface. Vimos en el Capítulo 8 que Microsoft extendió el diseño de OLE
para soportar controles personalizados, que pretenden que dichos componentes ac-
túen como sustitutos de 32 bits para los componentes VBX. Sin embargo, debido al
modo en que se pasaron los parámetros de función, Visual Basic antes de la versión 4
no podía invocar un método de control directamente. Para resolver este problema,
Microsoft incorporó en OLE la interfaz IDispatch para proporcionar el enlace nece-
sario. La interfaz IDispatch hoy sirve más a menudo como un conducto para los
clientes de guión, como por ejemplo VBScript y JavaScript, permitiéndoles invocar
los métodos de control a través de servicios de función IDispatch::Invoke, que con-
vierte los parámetros y devuelve valores hacia y desde el tipo de datos nativos de
cliente. Aunque es una solución viable, las conversiones extras e indirectas de invo-
car Invoke ralentizan las interacciones de contenedor con un control.
Para los guiones de páginas Web y los clientes más antiguos de Visual Basic,
IDispatch es un acuerdo necesario. Sin embargo, las aplicaciones de contenedor
escritas en lenguajes como C++ pueden acceder directamente a los métodos de un
control invocando a través de punteros. No son necesarias IDispatch::Invoke y sus
conversiones que llevan mucho tiempo. Para alojar clientes que soporten tipos de
datos que no sean Variant, OLE soporta la idea de dos interfaces diferentes. A una
función de método de una interfaz dual se puede acceder o bien indirectamente a
través de 1Dispatch::Invoke o bien directamente invocando un puntero al método,
dando de este modo servicio a todas las aplicaciones de contenedor, independiente-
mente de en qué lenguajes estén escritas.
Aggregation. A través de la agregación, otro objeto puede aparecer a sus invocado-
res para tener todas las habilidades del control Pulse, cualesquiera que sean los servi-
cios nuevos que proporciona el segundo objeto. El objeto que se agrega, al que se
hace referencia a menudo como el objeto externo o el que contiene, incrusta el con-
trol Pulse del mismo modo que lo haría una aplicación de contenedor. El control
externo entonces filtra selectivamente las solicitudes Querylnterface desde su clien-
te, pasando a Pulse cualquier solicitud para la interfaz IPulseCtl. El cliente de objeto
externo recibe el puntero deseado e invoca Pulse, sin saber que Pulse ahora, no el
objeto que Pulse ha agregado, está proporcionando el servicio.
La selección de agregación en el diálogo Object Wizard Properties no determina
si su control puede agregar otro objeto, sólo si su propio control puede agregarse.
Si no pretende que su control sirva para las antiguas aplicaciones de Visual Basic o
estar guionado en una página Web o página activa de servidor, seleccione el botón de radio
Custom en el diálogo. Esto tiene la ventaja de reducir ligeramente el tamaño del control
terminado, porque cada interfaz implementada no incorpora los cuatro métodos de IDis-
patch. Aunque un control con interfaces personalizadas puede aparecer en un documento
HTML, no puede programarse a través de un guión. El intérprete de guiones debe encon-
trar la interfaz IDispatch en el objeto de control o no puede acceder al control.
Para el control Pulse, acepte los ajustes predeterminados para activar los hilos del
modelo apartamento y añada soporte para interfaces duales, pero no agregación. Después
de reflexionar un momento, vamos a hacer Pulse no agregable seleccionando el botón de
radio No del grupo Aggregation. Esto añade una única línea a la definición de clase CPul-
seCtl, como se muestra aquí:
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Haga clic en el botón OK para quitar el diálogo ATL Object Wizard, momento en el
que el asistente genera tres archivos:
Pu1seCtl.h y PulseCtl.cpp. Código de definición e implementación para la nueva
clase CPulseCtl.
Pu1seCtl.rgs. Archivo de texto que contiene un guión de información de registro de
control. El guión de registro en el archivo RGS se convierte en parte de los datos de
recurso contenidos en el archivo ejecutable de control, que la función DlZRegister-
Server lee e instala en Registry. Object Wizard añade una línea al archivo RC de
proyecto para referenciar el guión de registro:
BEGIN-OBJECT-MAP(0bjectMap)
OBJECT-ENTRY(CLS1D-PulseCtl, CPulseCtl)
END-OBJECT-MAPO
Finalmente, ATL Object Wizard hace los cambios necesarios al archivo IDL de pro-
yecto, desde el que se genera la biblioteca de tipo de control. IDL quiere decir lenguaje de
descripción de interfaz, y el archivo IDL sirve como entrada para la herramienta compila-
dora de Microsoft IDL, MIDL. Echaremos un vistazo al archivo IDL de proyecto con más
detalle cuando añadamos la función evento de Pulse.
Esto invoca el diálogo Add Property To Interface que se muestra en la Figura 10.8, que
solicita la misma información que el diálogo Add Property de ClassWizard que se encuen-
tra en el Capítulo 9. Teclee nlnterval para el nombre de propiedad Pulse, dándole un tipo
372 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
de propiedad long (la propiedad podría igualmente ser short, pero eso restringiría
el intervalo de máximo del tic de reloj a poco más de un minuto). Los prototipos IDL
predeterminados para los métodos getlput de propiedad aparecen en la parte inferior del
diálogo:
[propget, . . . 1
HRESULT nInterval([out, retval] 10ng *pVal);
[propput, . . . 1
HRESULT nInterval([in] long newVal);
Los métodos getlput de COM son el equivalente de bajo nivel de las funciones GedSet
de MFC, proporcionando los medios para que un contenedor lea y escriba los datos de
propiedad de control. Cuando el compilador MIDL compila el archivo IDL de proyecto,
define los dos métodos en la interfaz anteponiendo el nombre g e t y p u t al nombre de
propiedad. El parámetro pVal de la función get apunta al valor actual de la propiedad; el
parámetro newVal de la función put contiene un valor de propiedad nuevo que reemplaza
al antiguo. Si sus métodos getlput requieren una lista de parámetros más amplia, añada las
variables en el cuadro Parameters del diálogo. El diálogo inserta cualquier adición delante
de los parámetros pVal y newVal, y los hace comunes tanto a los métodos get como a los
put. Si no quiere la misma lista para ambas funciones, debe hacer los cambios más adelan-
te editando manualmente el archivo IDL utilizando el editor de texto. No tenga reparo en
cambiar el orden de los parámetros o los nombres en el archivo IDL, pero deje los paráme-
tros pVal y newVal los últimos en la lista de parámetros junto con sus atributos [out,
retval] y [in].
El cuadro de comprobación Put Function va acompañado de dos botones de radio
etiquetados PropPut y PropPutRef. PropPutRef es parecido a la opción PropPut predeter-
ESCRITURA DE CONTROLES ACTIVEX UTILIZANDO ATL 373
minada, excepto que indica al contenedor que la función put de propiedad acepta el pa-
rámetro newVal por referencia en lugar de por valor. En este caso, el contenedor ajusta
la propiedad invocando IDispatch::Invoke con la etiqueta DISPATCH-PROPERTY-
PUTREF en lugar de DISPATCH-PROPERTYPUT. Los clientes de Visual Basic, por
ejemplo, utilizan la palabra clave Set para indicar que una asignación de propiedad es por
referencia y no por valor:
Set PulseCtl.nInterva1 = x
Atributo Descripción
defaultcollelem Permite a los clientes escritos en Visual Basic for Applications acceder direc-
tamente a las funciones de las propiedades getíput.
displaybind Indica al contenedor que la propiedad debería visualizarse al usuario como
vinculable. La propiedad también debe tener el atributo bindable.
helpcontext Especifica un número de 32 bits que identifica información en el archivo de
ayuda de control que pertenece a la propiedad.
hidden Solicita que el contenedor no debería visualizar la propiedad al usuario.
irnmediatebind Solicita que se notifique a la base de datos a la propiedad en lugar de esperar
hasta que el control pierda la atención de entrada. La propiedad también debe
tener el atributo bindable.
local Especifica que el compilador MIDL debería generar únicamente los archivos
cabecera de la interfaz, no el código matriz. El atributo local no es relevante
para controles ActiveX en proceso como Pulse.
nonbrowsable Indica al contenedor que no incluya la propiedad en el explorador de propieda-
des del contenedor.
requestedit Indica que el control pedirá al contenedor permiso antes de cambiar el valor de
la propiedad. El permiso se solicita mediante la función IPropertyNotify-
Sink::OnRequestEdit, que notifica al contenedor de que la propiedad está a
punto de cambiar y que el objeto solicita permiso para continuar. Un valor
devuelto de S-FALSE a partir de OnRequestEdit deniega la solicitud; un valor
devuelto de S-OK concede permiso para cambiar el valor de la propiedad.
Cuando se reciba S-OK, el control debe entonces invocar IPropertyNotify-
Sink::OnChanged si la propiedad también tiene el atributo bindable.
restricted Especifica que los métodos de las propiedades getíput no se deben invocar
desde una macro.
Indica que las funciones getlput devuelven un objeto o Variant que es una
fuente de eventos. El atributo source se utiliza raramente para propiedades,
pero se aplicará brevemente a la lista de interfaces de Pulse.
vararg Indica que los métodos de las propiedades getíput pueden aceptar un número
variable de argumentos. El último argumento del método debe ser una matriz
segura del tipo Variant que contiene valores predeterminados para cada valor
no especificado.
1e @ Pulse classes
4 ~IPulasCtEventi
1.Haga clic en el botón derecho del ratón en la entrada -IPulseCtlEvents del panel
ClassView y elija el comando Add Method desde el menú contextual. En el diálo-
go Add Method To Interface, seleccione un tipo de vuelta void, nombre la función
del evento como Pulse y haga clic en OK para cerrar el diálogo.
2. Muestre el panel FileView en la ventana Workspace, haga clic en el botón del
ratón en la entrada Pulse.id1 y elija el comando Compile Pulse.id1. Esto lanza el
compilador MIDL, que produce el archivo de biblioteca de tipo Pulse.tlb y lo
añade al proyecto.
3. Cuando el compilador MIDL termina, vuelva a cambiarse al panel ClassView y
haga clic en el botón derecho del ratón en la entrada CPulseCtl. Elija el coman-
do Implement Connection Point del menú para mostrar un diálogo del mismo
nombre.
4. Ajuste el cuadro de comprobación etiquetado -IPulseCtlEvents y haga clic en OK
para cerrar el diálogo.
La mitad inferior del archivo IDL del proyecto enumera un bloque library titulado
PULSELib, que describe el nuevo evento Pulse para el compilador MIDL. Algunas aplica-
ciones de contenedor sólo leen el bloque library cuando buscan una biblioteca de tipo
para los métodos de control y propiedades, así que normalmente es prudente editar el
archivo IDL para desplazar los dos primeros bloques de código en el bloque library. El
primer bloque, formado por corchetes [], contiene atributos de la interfaz IPulseCtl; el
segundo bloque encierra entre llaves { } una lista de los métodos de la interfaz. El Lista-
do 10.1 muestra cómo debería ser el resultado volviendo a arreglar el código. Si está
desarrollando el proyecto Pulse usted mismo siguiendo estos pasos, los números GUID
para su propio archivo IDL no concordarán con los que se muestran en el listado.
Tenga en cuenta que la función proxy Fire-Pulse es de tipo void. Los eventos, al
contrario que los métodos, nunca devuelven un valor.
A través de su clase base, CProxy-IPulseCtlEvents implementa el punto de conexión
para la interfaz identificada por el segundo parámetro de plantilla. En nuestro caso, la
interfaz es -IPulseCtlEvents y su identificador de interfaz de lanzamiento es DIID-IPul-
seCtlEvents, definida en el archivo Pulse-i.c. El parámetro de la tercera plantilla especifi-
ca una clase ATL que maneja las conexiones. La clase C C o m D y n a m i c U n k A r r a y permite
un número ilimitado de conexiones; la clase alternativa C C o m U n k A r r a y sólo permite un
número fijo de conexiones.
Pulse podría capturar simplemente los mensajes WMTIMER para activar su disparo de
eventos, pero eso requeriría la creación de una ventana para recibir los mensajes. Una
ventana implica un gran esfuerzo en recursos de sistema, necesitando mucho tiempo para
iniciarse y desactivarse, y un control Active X aerodinámico como Pulse debería evitar la
creación de ventanas siempre que fuera posible. Afortunadamente, el proyecto de ejemplo
AtlButton incluido con Visual C++ contiene una clase segura CTimer de hilo único que
hace justo lo que necesitamos. Como el código no se genera por un asistente ATL, aparece
en su totalidad como Listado 10.2.
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
El proyecto volvió a empezar en el paso 2 con la selección del tipo de objeto disponible
más simple desde el ATL Object Wizard, que ajustó la clase CPulseCtl para heredar sólo
las tres clases base y dos interfaces. Un control ActiveX real debe implementar más inter-
faces que éstas, así que en este paso expandiremos la lista de herencia de clase para incluir
interfaces adicionales que los contenedores típicos esperan que un control implemente.
384 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Abra el archivo PulseCt1.h en el editor de texto y añada las dos sentencias #include
que se muestran aquí en gris, una para la clase CTimer que creamos en el paso anterior y la
otra para aportar implementaciones de interfaz adicionales que el proyecto requiere:
Aunque el control Pulse sea invisible en lugar de sin ventana, la clase CPulseCtl debe
de todos modos derivar de IOleInPlaceObjectWindowlessImpl (recuerde del Capítulo 9
que los controles sin ventana se basan en el cliente para los servicios de visualización).
Esto es un ejemplo de cómo ATL no se optimiza para los controles invisibles como Pulse.
En lugar de ofrecer una implementación separada de la interfaz IOleInPlaceObject que
nuestro control necesita, ATL proporciona sólo IOleInPlaceObjectWindowless, que es una
extensión de IOleInPlaceObjec que añade soporte para los mensajes de ventana y las
operaciones de arrastrar y desplegar. Pulse no requiere métodos extras, pero debe incluir-
los, sin embargo, para obtener la implementación de IOleInPlaceObject.
Siguiendo la lista de herencia de clase, añada una propiedad para la función miembro
de FinalRelease, que invoca CComObject de ATL cuando carga el control Pulse:
Tenga en cuenta que CPulseCtl no declara un destructor de clase. Esto es porque los
destructores no son virtuales en las clases base de ATL desde las que deriva CPulseCtl, así
que la clase no puede presuponer con certeza que se invoque su destructor alguna vez. En
su lugar, una clase de control ActiveX que utilice ATL debería llevar a cabo cualquier
tarea necesaria de limpieza en la función FinalRelease, que se invoca justo antes de que se
destruya la instancia del objeto. Su corolario es la función FinalConstruct, para la que un
control debería confinar sus tareas de inicialización. Un segundo proyecto más adelante en
este capítulo muestra FinalConstruct.
Para hacer concordar las plantillas de interfaz colocadas en la lista de herencia CPul-
seCtl, debemos añadir las entradas correspondientes al mapa COM de clase que se muestra
a continuación:
BEGIN-COM-MAP(CP~~~~C~~)
~0~~1~~~R~~~~~E~~~Y~I~PL(1~onnectionPointContainer~
COMINTERFACE-ENTRY(IPulseCt1)
COM-INTERFACE-ENTRY(1Dispatch)
-
COM~INTERFACE~ENTRY(IConnectionPointContainer)
Debajo del mapa de punto de conexión, añada el mapa de propiedad que se muestra a
continuación. El mapa está vacío porque Pulse no soporta una hoja de propiedad, pero
algunas implementaciones de interfaz que hemos añadido a la clase CPulseCtl esperan que
el mapa exista:
*pVal = m-dwTimerInterva1;
return S-OK;
)
STDMETHODIMP CPulseCti::EndPulse()
return S-OK;
1
STDMETHODIMP CPulseCt1::-OnTimer()
{
return S-OK;
HRESULT CPulseCtl::FinalRelease()
return EndPulse ( ) ;
)
El método get-nInterval informa al invocador del intervalo actual del cronómetro, que
está almacenado en la variable de miembro CTimer::m-dwTimerInterva1.No hay función
put que concuerde, porque hemos especificado la propiedad nInterval como de sólo lectu-
ra en el paso 2. StartPulse y EndPulse implementan los dos métodos de control, ajustando
y deteniendo el cronómetro. En el caso de que el contenedor no invoque EndPulse cuando
se termina con el control, la función FinalRelease invoca EndPulse para asegurar que se
sale correctamente del hilo trabajador del cronómetro antes de que termine el control.
388 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ForceRemove ( 8 C 9 B A B D D - B C E S - D ~ ~ C A ~ C B
= ~sF ~'PulSeCtl
~) ClaSS'
{
ProgID = s 'Pulse.PulseCt1.i'
VersionIndependentProgID = s 'Pulse.PulseCt1'
ForceRemove 'Programrnable'
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
1
ForceRemove 'Control'
'MiscStatus = S '0'
I
'1' = S 148624'
1
La entrada Control anuncia Pulse como un control ActiveX incrustable. El valor Misc-
Status 148.624 representa el valor combinado de las cinco etiquetas:
OLEMISC-SETCLIENTSITEFIRST,OLEMISC-INSIDEOUT,
OLEMISC-CANTLINKINSIDE, OLEMISC-INVISIBLEATRUNTIME
y OLEMISC-NOUIACTIVATE, todas descritas en la barra lateral.
Normalmente, ATL Object Wizard añade un mapa de bits predeterminado a un pro-
yecto de control, pero no para tipos de objeto simples como Pulse. Si quisiera mejorar el
control Pulse añadiendo un mapa de bits pequeño, aquí tiene cómo hacerlo. Hemos visto
cómo algunos contenedores 4 1 editor de diálogo de Visual C++, por ejemplo- pueden
visualizar un mapa de bits en una herramienta para representar un control para el usuario.
El contenedor extrae la imagen de mapa de bits desde los propios datos del control Acti-
veX, en los que se identifica el mensaje por la clave ToolBoxBitmap32 de control en el
sistema Registry. Para insertar la clave ToolboxBitmap32, incluya esta línea junto con las
otras que hemos añadido al archivo PulseCtl.rgs:
Un mapa de bits de control es completamente opcional, pero añade un toque de profesio-
nalidad a los controles pensados para el mercado. El tamaño del mapa de bits es de 16 por
15 píxeles, así que una imagen de color ocupa 512 bytes de la sección de datos de recurso
del control. Elija el comando Resource del menú Insert, elija Bitmap y pulse ALT+INTRO
para mostrar el diálogo Bitmap Properties. Cambie las dimensiones del área de trabajo a
16 por 15 y asigne al identificador de recurso el mismo identificador dado en la sentencia
ForceRemove para la clave ToolBoxBitmap32. En nuestro caso, el valor es 1:
(Continúa)
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Para los dos proyectos de control ANSI y Unicode, los destinos MinSize y MinDepen-
dency ofrecen una elección de si el control se basa en un archivo de tiempo de ejecución
auxiliar para los servicios ATL o incorpora todo el código que necesita dentro de su propio
archivo ejecutable. La elección está entre el archivo reducido y la dependencia de tiempo
de ejecución reducida, muy parecida a un proyecto MFC para el que debe elegir si enlazar
estáticamente o dinámicamente con la biblioteca MFC.
La configuración MinSize reduce el tamaño de un control enlazándolo dinámicamente
a Atl.dl1, un archivo de biblioteca de 54 Kb que Visual C++ instala en la carpeta Win-
dows1System. Cuando el control se ejecuta, se invoca en Atl.dl1 para las funciones de
servicio que el control necesita. Esta administración resulta, en uso de memoria, de lo más
eficaz cuando varios controles ActiveX enlazados a Atl.dl1 se ejecutan juntos. En este
caso, el destino MinSize también puede llevar a tiempos de carga más rápidos cuando
transfiera los controles a una red o a Internet, porque el tamaño de archivo combinado de
los controles está reducido, incluso teniendo en consideración la adición de Atl.dl1, que
también se puede transferir con los controles.
La configuración MinDependency instala el proyecto de control de modo que el com-
pilador expanda las plantillas de clase a clases enteras, en lugar de funciones matriz, que se
invocan en Atl.dl1. En esta configuración, el propio control contiene el código de imple-
mentación de interfaz que necesita, como si los servicios ATL estuviesen enlazados es-
táticamente (esto es sólo una analogía, porque no hay archivo LIB de biblioteca estática o
ATL como lo hay para MFC). El control ActiveX resultante no se basa en el archivo
Atl.dl1, una independencia cuyo precio es una imagen de ejecutable más grande. El destino
MinDependency es mejor para los controles únicos que no se espera que operen junto con
otros controles ATL. Como esta descripción se aplica a Pulse, seleccione la configuración
Release MinDependency en la barra de herramientas Build:
Los archivos Project instalados del CD-ROM que se acompaña abrevian los nombres
de destino a MinSize y MinDep, manteniendo los nombres de carpeta a menos de ocho
caracteres. Puede que prefiera nombres de destino cortos para sus propios proyectos ATL,
ni más ni menos que porque ocupan menos espacio en el sistema Registry. Para modificar
los nombres de destino que instala COM AppWizard, cierre temporalmente el área de
trabajo y abra el archivo de proyecto DSP en el editor de texto. Utilizando el comando
Replace, sustituya todas las apariciones de Release MinDependency y ReleaseMinDepen-
dency con «MinDep». Abrevie el nombre de destino MinSize a través de pasos similares.
Guarde el archivo DSP y vuelva a abrir el proyecto. Debería ver los nombres de destino
nuevos enumerados en la barra de herramientas Build.
Todavía no hemos hablado de las optimizaciones de la compilación -ese es el tema
del Capítulo 12-, pero el optimizador de Visual C++ puede incrementar la velocidad de
ejecución o reducir el tamaño de código. Lo segundo es casi siempre la mejor opción para
los controles ActiveX, así que COM AppWizard preselecciona el ajuste de optimización
de tamaño de código. Si no es esto lo que quiere, cambie la selección en la pestaña C++ del
diálogo Project Settings. Una vez que ha seleccionado la configuración de destino, haga
clic en el comando Build Pulse.dl1 del menú Build para compilar y enlazar el control
Pulse. Si los distintos pasos de enlace y compilación finalizan con éxito, Visual C++
ejecuta servicialmente RegSvr32.exe para registrar el control y visualizar los resultados en
la ventana Output:
La utilidad Test Container también puede incrustar con éxito nuestro control nuevo,
como se prueba en la Figura 10.13. Inicie Test Container, a continuación cargue el control
Pulse seleccionando la herramienta New Control y haga dos veces clic en la entrada Pul-
seCtl Class en la lista (la lista se clasifica por orden alfabético; pulsando P en el teclado se
desplaza automáticamente al primer control que empiece por P). Una vez que el control
esté cargado en el Test Container, haga clic en la herramienta Invoke Methods, seleccione
el método StartPulse en el cuadro Method Name como se muestra en la Figura 10.13,
394 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ajuste el parámetro nRate a 2000 y haga clic en el botón Invoke. Esto hace que el control
dispare su evento Pulse cada 2 segundos, lo que resalta el Test Container añadiendo una
entrada nueva al registro de evento. Al invocar el método EndPulse, finaliza el disparo.
Si quisiera ver cómo Pulse.ocx puede estar incrustado en una aplicación de contenedor
normal, ejecute el programa Hour2 proporcionado en el CD que se acompaña en la carpeta
Codigo\Capitulo.IOVIour2.Hour2 es un duplicado del programa Hour del Capítulo 8,
excepto que utiliza el control Pulse para su control de tiempo en lugar del control IETimer.
TowerATL una biblioteca dinámica de enlace que no utiliza MFC. Cuando COM App-
Wizard termine, elija New ATL Object del menú Insert para lanzar ATL Object Wizard.
Seleccione Controls en la primera lista de diálogo y haga dos veces clic en el icono etique-
tado Full Control. TowerATL soporta más interfaces que el control Pulse, y necesitaremos
más interfaces como IQuickActivate y ISpecifiPropertyPages que la opción Full Control
añade al código fuente.
Tenga en cuenta que el diálogo ATL trata una página de propiedad de control como un
proyecto separado. Volveremos a Object Wizard de nuevo para añadir un objeto de página
de propiedad antes de terminar el proyecto. Teclee TowerCtl para el nombre abreviado en
la pestaña Names del diálogo Properties, aceptando los nombres predeterminados en los
otros cuadros. En la pestaña Attributes, seleccione el cuadro de comprobación Support
Connection Points como hicimos para el proyecto Pulse. Acepte otros ajustes predetermi-
nados en la pestaña para activar el hilo de apartamento, interfaces duales y agregación.
La pestaña Miscellaneous contiene cambios que ajustan distintas etiquetas OLEMISC.
Al seleccionar el cuadro de comprobación Acts Like Button, por ejemplo, ajusta la etique-
ta OLEMISC-ACTSLIKEBUTTON, que indica al contenedor que el control responde a
los clics del ratón y generalmente se comporta como un botón. El cuadro de comprobación
ajusta la etiqueta OLEMISC-ACTSLIKELABEL, informando al contenedor que el con-
trol sirve como un rótulo para el control que le sigue en el orden de tabulación, es decir, el
control enumerado en el guión RC del contenedor. El cuadro de comprobación The Invisi-
ble At Runtime activa la etiqueta OLEMISC-INVISIBLE-ATRUNTIME,apropiada para
los controles como Pulse que permanecen invisibles incluso cuando están activados.
Sáltese la pestaña Miscellaneous para aceptar sus ajustes predeterminados y continúe
hacia la pestaña Stock Properties. Al igual que la versión MFC de Tower, el control ATL
de Tower contiene cuatro propiedades de almacenamiento, llamadas BackColor, Fore-
Color, Caption y Font. Añada estas propiedades al cuadro Supported seleccionando cada
una de la lista y haciendo clic en el botón >, como se muestra en la Figura 10.14 (las
propiedades BackColor y ForeColor están etiquetadas Background Color y Foreground
Color en la lista). Haga clic en el botón OK para cerrar el diálogo Object Wizard de ATL.
Igual que el control original Tower, TowerATL dispara cinco eventos, llamados Click,
FromPanel, ToPanel, Error y Winner, que mantienen colectivamente al contenedor infor-
mado sobre qué está pasando en el control. La adición de eventos al proyecto implica los
mismos pasos que seguimos para el control Pulse:
1 . Haga clic en el botón derecho del ratón en la entrada /TowerCtlEvents en el panel
ClassView y elija el comando Add Methods del menú contextual, seleccione a
continuación el tipo de retorno void e introduzca un nombre de función en el
diálogo. Repita lo mismo para cada una de las funciones de evento. Sólo From-
Panel y ToPanel toman parámetros, llamados nFromPanel y nToPanel, respecti-
vamente, ambos del tipo short:
//{{AFX-ODL-EVENT(CTowerCtr1)
[id(DISPID-CLICK)] void Click();
[id(l)1 void FromPanel(short nPanel);
[id(2)] void ToPanel(short nPanel);
[id(DISPID-ERROREVENT)] void Error();
[id(3)1 void WinnerO;
//}}AFX-ODL-EVENT
A partir de esta información, ClassWizard genera funciones proxy tales como Fire-
Click y FireWinner, que invocan la función COleContro1::FireEvent de MFC. FireEvent,
a su vez, invoca el método IDispatch::lnvoke del contenedor, exactamente igual que las
funciones proxy que genera ATL.
Cuando aparece el diálogo Object Wizard Properties de ATL, teclee TowerPPG para
el nombre corto del objeto:
Como antes, el asistente rellena servicialmente los cuadros con nombres sugeridos. El
cuadro Interface está en gris porque el objeto de página de propiedad no necesita una
interfaz personalizada. Acepte los ajustes predeterminados en la pestaña Atributes y mues-
tre la pestaña Strings. Teclee &Caption para el título de página y Caption property en el
cuadro etiquetado Doc String. TowerATL no proporciona un archivo de ayuda, así que
borre el texto en el cuadro de edición tercero para dejarlo en blanco.
Object Wizard escribe la cadena de título y documento en el archivo RC de proyecto,
donde forman parte de los datos de recurso de la cadena de control. El título especifica la
etiqueta que aparece en la pestaña de la nueva página de propiedad. La cadena de docu-
mento está pensada para servir como texto de herramienta de consejo de pestaña, descri-
biendo el propósito de una página cuando el cursor del ratón se detiene sobre la pestaña,
pero la cadena casi siempre queda sin utilizar y no aparece como herramienta de consejo ni
ninguna otra cosa. Esto es porque la función OleCreatePropertyFrame de tiempo de eje-
cución de OLE responsable de la creación de la ventana de hoja de propiedad, conocida
como marco de propiedad en el lenguaje OLE, no soporta herramientas de consejos de
soporte.
Haga clic en el botón OK para hacer desaparecer el diálogo, entonces Object Wizard
hace estas adiciones al proyecto:
Añade los archivos TowerPPG.cpp y T0werPPG.h para implementar la nueva clase
CTowerPPG.
Escribe recursos de cadena para el título y el texto de consejo de herramienta en
T0werATL.r~:
IDS-TITLETowerPPG "&CaptionV
IDS-DOCSTRINGTowerPPG "Caption property"
[
U U ~ ~ ( O ~ D ~ B A A ~ - C ~ ~ ~ - ~ ~ D ~ - B E C ~ - F B ~ A F ~ ~ F C C ~ ~ ) ,
helpstring("TowerPPG Class"
1
coclass TowerPPG
{
interface IUnknown;
} ;
S T D M E T H O D ( A ~ (void)
~~~)
m-bDirty = F A L C E ;
return S-OK;
1
LRESULT OnChangeCaption(W0RD wNotifyCode, WORD wID,
HWND hWndCtl, BOOL& bHandled)
La función Apply invoca GetDlgItemText para copiar la cadena de captura nueva del
cuadro de edición al buffer szcaption, y a continuación invoca el método put-Caption del
control para actualizar la propiedad Caption con la cadena nueva. El método put-Caption
HRESULT FinalConstructO;
Un objeto como CTowerCtl que inicializa datos debería anular la función de miembro
CComObjectRootEx::FinalConstr~~ct como se muestra aquí. Más o menos análogo a la
útil función OnInitDialog de MFC, FinalConstruct se invoca después de que ATL haya
terminado la instalación del objeto pero antes de que el objeto llegue a activarse. Es aquí,
en lugar de en el constructor de la clase, donde el control debería realizar la mayor parte de
su trabajo de inicialización. La función FinalRelease correspondiente mostrada anterior-
mente en el control Pulse permite al control llevar a cabo cualquier limpieza necesaria.
Modifique el mapa de propiedad para establecer el orden en que las páginas Caption,
Color y Font aparecen en la hoja de propiedad del control:
El mapa de mensaje contiene entradas para los tres manejadores de mensaje de control
añadidos en el paso 4:
BEGIN-MSG-MAP(CTowerCt1)
MESSAGE-HANDLER(WMMOUSEMOVE, OnMouseMove)
MESSAGE-HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
MESSAGE-HANDLER(WM_LBUTTONUP, OnLButtonUp)
Los tres primeros parámetros son estándar para los mensajes. El manejador OnLBut-
tonDown, por ejemplo, recibe el valor WM-LBUTTONDOWN en el parámetro uMsg, el
estado actual de la clave en wParam, y las coordenadas del cursor del ratón en IParam. La
macro MESSAGE-HANDLER le da a la etiqueta bHandled un valor de TRUE antes de
invocar la función de manejador. Un manejador que no da pleno servicio al mensaje debe-
na despejar la etiqueta antes de devolver:
404 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
que desee utilizar, simplemente incorpore el guión del diálogo a TowerATL.rc, añadiendo
cualquier sentencia #define al archivo Res0urce.h. De lo contrario, diseñe el cuadro About
en el editor de diálogo, que se inicia automáticamente cuando cierra ObjectWizard. Aquí
tiene una idea de cómo debe aparecer el diálogo:
El diálogo toma prestados el mapa de bits de proyecto, así que la decoración no repre-
senta una pérdida derrochadora del espacio de recurso. Primero copie a su proyecto el
archivo TowerCtl.bmp de la carpeta Codigo\Capitulo.10\TowerATL, superponiendo el
archivo BMP genérico del mismo nombre pro porcionado por ATL Object Wizard. Utilice
la herramienta Picture para colocar la imagen en el editor de diálogo, entonces traiga el
diálogo Properties y seleccione Bitmap en el cuadro Type y el identificador en el cuadro
Image. El cuadro de edición de la última línea es opcional, simplemente sirve como un
lugar conveniente para visualizar el identificador del hilo en el que está ejecutando el
control. Esto nos permitirá confirmar más tarde el comportamiento de un control de hilo
de apartamento como TowerATL cuando se ejecuta en apartamentos separados. Asigne al
cuadro de edición un identificador IDC-THREAD-ID y ajuste el cuadro de comprobación
Read Only en su diálogo Properties.
Guarde el recurso de diálogo cuando termine de diseñar el cuadro About, a continua-
ción haga clic en el botón derecho del ratón sobre ITowerCtl en el panel ClassView y elija
el comando Add Method. Introduzca AboutBox para el nombre del método nuevo que se
muestra a continuación. Al igual que el método Reset, AboutBox no toma parámetros.
A continuación, vuelva a abrir el archivo ToweiCtl.cpp y añada esm líneas a la función About-
Box:
STDMETHODIMP CTowerCtl::AboutBox()
I
CTowerBox dlgAbout;
DlgAbout .DoModal ( ) ;
recurn S-OK
Cuando una aplicación de contenedor invoca el método del control AboutBox, la fun-
ción crea un objeto CTowerBox e invoca el diálogo. El programa Game2 descrito en la
sección siguiente muestra cómo una aplicación señala a TowerATL para visualizar el
cuadro About.
adopta el modelo de hilo de apartamento, que es perfecto para los clientes STA como
Internet Explorer. Pero si hubiésemos seleccionado en su lugar el único modelo sin hilos
para Tower ATL, el segundo ejemplo de control correría más despacio que el primero,
porque COM estaría forzado a marshall en todas las interacciones con él.
El CD adjunto proporciona un programa nombrado Game2 diseñado específicamente
para exhibir el control TowerATL nuevo. El archivo rc, ubicado en la carpeta Code\Chap-
ter.l0\Game2, referencia el valor de identificador de clase del control TowerATL.ocx
proporcionado en el CD. Si prefiere ejecutar Game2 utilizando una versión de TowerATL
que haya creado usted mismo, copie de nuevo el identificador de clase correcta desde su
archivo TowerCtl.id1 para sustituir la cadena de identificador en Game2.r~.Pegue la cade-
na de identificador nueva en la sentencia IDC-TOWERATL de guión de diálogo antes de
construir el programa Game2:
CONTROL ",
"",IDC~TOWERATL,"(xxxxxxxx-xxxx-xxxx-~~~x-xxxxxxxxx~x)
WS-TABSTOP, 5, 20, 225,100
END
lanzamiento, un problema que no afecta ni con mucho a los controles ActiveX. Por ejem-
plo, imagine que decidimos que Pulse fuese un cronómetro de mayor precisión si invocase
a las funciones API timeSetEvent y timeKillEvent en lugar de confiar en las excentricida-
des del cronómetro Sleep del sistema. La versión modificada puede que ofrezca nuevos
objetos y capacidades a los nuevos clientes, mientras que las aplicaciones de clientes
antiguos como Hour2 incrustarían el nuevo Pulse como antes, no sería muy sabio que nada
hubiera cambiado. Incluso al superponer el antiguo archivo Pulse.ocx con la versión nueva
no afecta a las aplicaciones de cliente, estén o no escritas para hacer uso de los rasgos
extendidos. Este tipo de estabilidad es difícil de adquirir con versiones progresivas de la
biblioteca dinámica de enlace.
El depurador
Después de diseñar y codificar viene la depuración, el tercer paso del desarrollo de soft-
ware. Su programa de 3.000 líneas puede compilarse sin apenas una advertencia, sin em-
bargo fallan con regularidad, y lo que es mucho peor, a veces sólo lo hacen ocasionalmen-
te. Cuando su programa no trabaja correctamente y no está seguro del porqué, es hora de
recurrir al depurador para obtener una vista interna del programa tal y como se ejecuta.
El depurador de Visual C++ es una de las mejores características del producto entero.
Inteligente y fácil de utilizar, el depurador puede ayudarle a encontrar cualquier error que
probablemente encontrará en el desarrollo de software de Windows. Pero la depuración es
a menudo más un arte que una ciencia, necesitando claridad mental y buen conocimiento.
El depurador es como un microscopio en el que puede expandir su visualización, pero sólo
si sabe dónde mirar.
Las bibliotecas dinámicas de enlace, incluyendo los controles ActiveX, no son casos
especiales para el depurador de Visual C++. El depurador cruza sin esfuerzo el límite entre
proyectos, lo que significa que puede empezar a depurar un programa en un proyecto, y
luego continuar depurando cuando el programa invoque la función exportada de una bi-
blioteca dinámica, incluso si la biblioteca y sus archivos fuente existen como otro proyecto
o subproyecto. Lo contrario también se aplica. Puede empezar una sesión de depuración en
el proyecto de biblioteca dinámica de enlace, en cuyo caso el depurador automáticamente
ejecuta la aplicación de invocación y le devuelve el control cuando el flujo de ejecución
alcanza una de las funciones de la biblioteca.
El depurador maneja aplicaciones ActiveX y multihilos y tiene la habilidad para correr
en un ordenador mientras que el programa que está depurando corre en otro ordenador.
Veremos estos casos especiales más adelante en el capítulo. Primero vamos a conocer el
depurador.
420 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Un proyecto en Visual C++ puede producir dos tipos de código ejecutable, llamados ver-
sión de depuración y versión final o «destino». La versión de depuración es la que trabaja
durante el desarrollo y prueba para hacer el programa sin errores; la versión definitiva es el
resultado final, distribuido a sus clientes. La versión de depuración es mayor y normal-
mente más lenta que la versión definitiva, llena de información de símbolos que el compi-
lador coloca en el archivo objeto. La información de símbolo es un registro de todo lo que
el compilador sabe sobre los nombres de funciones y variables del programa y las direc-
ciones de memoria que identifican. Al leer tanto los archivos fuente originales como la
información de símbolo contenido en el archivo ejecutable, el depurador puede asociar
cada línea de código fuente con las instrucciones binarias correspondientes en la imagen
del ejecutable. El depurador corre el ejecutable pero utiliza el código fuente para mostrar
el progreso del programa.
La versión definitiva contiene sólo instrucciones ejecutables optimizadas por el com-
pilador, sin la información de símbolo. Puede ejecutar una versión definitiva dentro del
depurador, pero si lo hace, el depurador informa que el archivo no tiene símbolo de datos.
Del mismo modo, puede ejecutar la versión de depuración de un programa sin el depura-
dor. Esto tiene consecuencias prácticas, debido a una característica de Visual C++ conoci-
da como depuración Just-in-time, que se muestra más adelante en el capítulo. Cuando
ejecuta una versión de depuración del programa sin el depurador, el cargador de Windows
ignora la información de símbolo extra en el archivo, permitiendo que el programa se
ejecute con normalidad. Sin embargo, si el programa comete un error, el manejo de excep-
ciones del sistema hace que el control se devuelva a Visual C++, que a continuación
ejecuta el depurador. El depurador muestra la instrucción que provocó el fallo y visualiza
valores de datos tal y como existían cuando se detuvo el programa. Esta característica
sobresaliente es especialmente útil para seguir el rastro de los errores que son difíciles de
reproducir, la pesadilla permanente en programación.
A propósito, si conservar su propiedad intelectual es importante para usted, debería
tratar de depurar la versión de su programa a medida que hace código fuente. Un archivo
de programa con la información de símbolo es mucho más fácil de devolver al ingeniero,
ya que el archivo contiene los nombres de todas las variables de programa y funciones. En
lugar de sentencias anónimas inconexas como estas:
MyClass::InitInstance:
004017ae push ebp
004017af mov ebp, esp
PUNTOS DE RUPTURA
La relación entre el depurador y el programa que corre es única en Windows; no hay otros
dos programas que operen tan bien compenetrados enlazados juntos. El depurador y el
programa no corren de forma simultánea en el mismo sentido que las aplicaciones norma-
les lo hacen en un entorno multitarea. Mientras el programa corre, el depurador está en
reposo, sin tener nada que hacer. Vuelve a tener el control cuando el programa que se
ejecuta dispara un punto de ruptura.
El depurador le permite instalar dos tipos diferentes de puntos de ruptura: uno basado
en la ubicación en el código y otro basado en los datos de programa. Un punto de ruptura
de ubicación es un marcador adjuntado a una instrucción particular en su código fuente,
parecido a un marcador en el editor de texto. Instale un punto de ruptura de ubicación en el
principio de cualquier sección de código sospechoso que desee investigar en detalle para
ver cómo surge el error. Cuando el programa que se ejecuta intenta ejecutar la instrucción
marcada, se para o se «rompe» (veremos cómo en la sección siguiente).
Un punto de ruptura de datos depende de los datos en lugar de depender del código.
Utilice un punto de ruptura de datos cuando sospeche que se está modificando una variable
de forma incorrecta en su programa pero no está seguro dónde. Los puntos de ruptura de
datos le indican al depurador que rompa la ejecución cuando la variable cambie o se
vuelva de un cierto valor, como, por ejemplo, cuando un puntero se vuelva a asignar o
cuando la variable x supere un valor de 500.
Cuando ,se dispara una ubicación o punto de ruptura de datos, el control vuelve al
depurador. Este actualiza su ventana mostrando los valores actuales de variables y la sec-
422 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
ción de código fuente donde la ruptura tuvo lugar. Ahora puede caminar a placer por el
código, un instrucción cada vez, para ver cómo las variables cambian y cómo se comporta
el programa.
También puede hacer clic en el comando Set Active Configuration del menú Build
para ver la configuración actual y si es necesario cambiarla a la versión de depuración. La
configuración Win32 Debug modifica los ajustes del programa de forma automática, vi-
sualizado en el diálogo Project Settings. Abra el diálogo Project Settings haciendo clic en
el comando Settings del menú Project y muestre las pestañas C/C++ y Link. Los ajustes en
el diálogo deberían ser semejantes a los que se muestran en la Figura 11.1, en la que:
i El cuadro de combinación Optimizations en la pestaña C/C++ visualiza la opción
Disable (Debug).
i Aparece una marca de comprobación en el cuadro de comprobación Generate Debug
Info en la pestaña Link.
Con estos ajustes en su sitio, puede construir el proyecto normalmente. El resultado es
una versión de depuración de su programa que contiene la información de símbolo para el
depurador.
El diálogo Breakpoints
Para visualizar el diálogo Breakpoints que se muestra en la Figura 11.3, pulse CTRL+B o
haga clic en el comando Breakpoints del menú Edit. Las tres pestañas del diálogo le
permiten colocar puntos de ruptura de ubicación, de datos, condicionales y de mensaje.
Los siguientes párrafos describen estos cuatro tipos de puntos de ruptura.
Como puede ver, un punto de ruptura de mensaje es una forma especializada de punto
de ruptura condicional. Puede obtener los mismos resultados en la pestaña Location ajus-
tando un punto de ruptura de ubicación en la etiqueta ButtonProc junto con esta condición:
msg = W C R E A T E
Ejecutar el depurador
Una vez que haya establecido dónde y bajo qué condiciones desea que su programa se
detenga, está preparado para ejecutarlo. En este punto el editor de texto, no el depurador,
está activo. El ejecutar la versión de depuración de su programa es una cuestión de iniciar
el depurador, que a su vez ejecuta el programa.
Elija el comando Start Debug del menú Build, que le presenta cuatro opciones, llama-
das Go, Step Into, Run To Cursor y Attach To Process, que se muestran en la Figura 11.6.
Utilice el comando Go cuando haya colocado al menos un punto de ruptura en el código
fuente. El depurador ejecuta el programa normalmente, parando cuando (y si) el flujo de la
ejecución de su programa alcanza un punto de ruptura de ubicación o dispara un punto de
ruptura de datos. El comando Step Into hace sólo lo que su nombre indica: se introduce en
el programa y se detiene en el primer comando. La primera instrucción de un programa de
Windows es el inicio de la función WinMain, o para un programa MFC, la función -tWin-
Main. En cualquier caso, el depurador abre el módulo fuente - q u e para -tWinMain es el
archivo Appmodul.cpp en la carpeta MFC- y lo visualiza en la ventana fuente.
El comando Run To Cursor detiene la ejecución en la línea fuente sobre la que descan-
sa el cursor. Si no hay ningún archivo fuente abierto en el editor de texto, se deshabilita el
comando Run To Cursor. De lo contrario, le proporciona un medio adecuado de saltar
rápidamente a un programa sin ajustar un punto de ruptura. Si el flujo del programa dispa-
ra un punto de ruptura antes de alcanzar el cursor, la ejecución se para en el punto de
ruptura, no en la línea donde esté el cursor. Para continuar la ejecución, vuelva a colocar el
cursor en la línea destino y haga clic en Run To Cursor otra vez. El comando Attach To
f d s f -DEBUG
efins new DEBUG-NE
urideí THIS-FILE
t ñ t ic c h a r THIS-FIL
IMPLEMENT-DYNCREATE(
Tabla 11.1. Información contenida en las seis ventanas del depurador activadas por
botones de la barra de herramientas Debug
ventana fuente. Esto visualiza una ventana emergente de herramienta de consejo que con-
tiene el valor actual:
Para algunas variables, como los elementos de estructura y los miembros de clase, el nom-
bre solo puede que no proporcione la resolución suficiente al depurador para identificar ine-
quívocamente la variable. En estos casos, debe primero seleccionar los nombres de las varia-
bles y de los objetos junto con el operador de punto que conecta (como en MyClass.Member)
y a continuación detener el cursor del ratón sobre la selección en la ventana fuente.
Mientras que la ventana Watch proporciona una visualización de variables en ámbito
independientemente de dónde se accedan en el programa, la ventana Variables se centra en
el punto congelado de ejecución. Cualquier variable referenciada en la última instrucción
ejecutada antes de que el programa fuese suspendido, y quizás una o dos instrucciones
previas, aparecen en la ventana Variables. Puede cambiar el valor de la variable haciendo
dos veces clic en la ventana Variables y tecleando un valor nuevo.
436 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Run To Cursor
Step Over Step Out
El comando Step Over hace como su nombre implica, procesar toda la sentencia if
incluyendo las invocaciones a Functionl y Function2. El programa se detiene otra vez en
la sentencia x=3 o y=IOO, dependiendo del resultado de la expresión if. El comando Step
Into maneja la situación de forma diferente. Cuando hace clic en el botón Step Into de la
sentencia if, el depurador da un paso en Function2 y se de&ieneen la primera instrucción.
Si comprueba la ventana Cal1 Stack en este punto, verá Function2 en la parte superior de la
lista y más abajo del nombre de la función que acaba de dejar.
Aquí tiene dónde resulta útil el comando Step Out. Este comando ejecuta el resto de la
función actual y a continuación se detiene en la siguiente sentencia después de la invoca-
ción de la función. En otras palabras, cuando se aplica a una invocación de función los
comandos Step Into y Step Out juntos tienen el mismo efecto que Step Over. Sin embargo,
si la instrucción contiene más de una invocación de función como en nuestro ejemplo, las
cosas se complican. Cuando hace clic en el botón Step Into de la sentencia if para detener
la primera instrucción de Function2 y a continuación hace clic en Step Out, la flecha del
puntero de instrucción se queda apuntando a la sentencia if. Esto es así porque Function2
ha terminado de ejecutarse, pero Functionl no se ha invocado todavía. Al activar Step Into
otra vez, implica que avanza hasta la primera instrucción de Functionl. Si hace clic en
Step Out para volver desde Functionl, la flecha de instrucción todavía apunta a la senten-
cia if porque la propia prueba if no se ha procesado todavía.
Una vista desmontada del código muestra más claramente lo que está sucediendo. Las
líneas sombreadas indican las sentencias de fuente C, que están seguidas de las instruccio-
nes equivalentes desmontadas (las líneas desmontadas sirven sólo para ilustrar la cadena
interna de sucesos dentro de la sentencia if, y no están destinadas a sugerir que la ventana
Disassembly está visible). Al principio de la secuencia de código, la flecha del puntero de
instrucción amarilla apunta a la sentencia if.
Windows 95 no permite que se den pasos en las funciones API del sistema como los
que se muestran aquí:
::selectObject( hdc, ::GetStockObject( BLACK-PEN ) );
haga clic en el botón derecho del ratón en la línea deseada de la ventana fuente y elija el
comando Set Next Statement del menú contextual:
El diálogo no es un mensaje de error. Sólo sirve para recordarle que la función termi-
nará ejecutando su código original, y sus modificaciones no tendrán efecto hasta que la
próxima vez la función se ejecute a través de la misma cadena de invocaciones. Haga clic
en el botón Yes para continuar la ejecución; haga clic en No para cancelar Edit and Conti-
nue y volver al depurador.
442 MICROSOFT VISUAL C + i 6.0. MANUAL DEL PROGRAMADOR
#else
#define CHECK (b, S)
#endif
Con la macro en su sitio, puede probar la lógica de su programa de este modo:
iRet = FunctionlO ;
CHECK( iRet ! = 1, "Valor de retorno equivocado de Functionl" );
Desarrollo de ShockWave
ShockWave es una aplicación MFC creada con la ayuda de AppWizard. Puede instalar el
proyecto desde los archivos de construcción en la subcarpeta Codigo\Capitulo. 1l\Shock-
Wav copiada desde el CD que se acompaña. Los archivos en esta subcarpeta contienen el
código fuente defectuoso; la versión corregida del programa está en la carpeta Shock-OK.
También puede desarrollar ShockWave usted mismo siguiendo estos seis pasos. El progra-
ma es lo suficientemente simple que sólo requiere edición su clase de visualización, así
que desarrollar el proyecto partiendo de cero es un ejercicio interesante que no requiere
una cantidad inconmensurable de tecleado.
ShockWave requiere sólo un comando de menú para salir del programa, por ello no nece-
sita los otros comandos que AppWizard añade al recurso de menú. Al utilizar el editor de
menú de Visual C++, modifica los menús de ShockWave, de modo que sólo tiene un menú
File y un menú Help como estos:
ShockWave cambia el tamaño del modelo de onda para rellenar la ventana de cliente,
realizando cambios en el tamaño de la ventana. También centra modelos de onda nuevos
con los clics de ratón dentro del área de cliente. Para responder a estos eventos, Shock-
Wave captura los mensajes de WM-SIZE y WM-LBUTTONDOWN con funciones de
manejador llamadas OnSize y OnLButtonDown.
Añada estas funciones de manejador a la clase CShockWave haciendo clic en el co-
mando ClassWizard en el menú View. En la pestaña Message Maps, ajuste CShockWave-
View como el nombre de clase y seleccione WM-SIZE en el cuadro de mensaje. Haga clic
en el botón Add Function para crear automáticamente la función OnSize, que maneja el
mensaje WM-SIZE. Haga lo mismo para que el mensaje WM-LBUTTONDOWN añada
la función OnLButtonDown, como se muestra en la Figura 11.12. Añadiremos código a las
#ifdef -DEBUG
#define new DEBUG-NEW
#unde£ THIS-FILE
static char THIS-FILE[] = -FILE-;
#endif
.......................................................................
/ / CShockWaveView
IMPLEMENT-DYNCREATE(CShockWaveView, CView)
BEGIN-MESSAGE-MAP(CShockWaveView, CView)
//{{AFX-MSG-MAP(CShockWaveView)
ON-WSIZE ( )
ON-WM-LBUTTONDOWN ( )
//}}AFXMSG-MAP
ENDMESSAGE-MAPO
.......................................................................
/ / CShockWaveView construcción/destrucción
sualiza un modelo de onda de choque. La variable iColor mantiene un valor de índice para
rgb que determina qué color utiliza el programa para pintar las ondas de choque.
El constructor invoca la función API de Windows GetSystemTime para recuperar el
componente de milisegundos del tiempo del sistema actual. Este valor, que va desde O a
999, proporciona un valor inicial conveniente para la función srand, la generadora de
números aleatorios en tiempo de ejecución de C.
BOOL SShockWaveView::~reCreateWindow(CREATESTRUCT& c s )
I
HCURSOR hCur = ::LoadCursor( NULL, IDC-CROSS ):
return TRUE;
1
ShockWave visualiza el cursor del ratón en forma de cruz en vez de en forma de flecha
normal. Este ligero refinamiento expresa más claramente al usuario la idea de apuntar
algún punto en el área de cliente con el cursor del ratón y hacer a continuación clic en
ellas. ShockWave utiliza las coordenadas de clic como el centro para el nuevo modelo de
onda. Para cambiar la forma de cursor de ventana, CShockWaveView anula la función
virtual PreCreateWindow. El marco de trabajo invoca PreCreateWindow justo antes de
crear la ventana principal del programa, pasando a la función un puntero para una estructu-
ra CREATESTRUCT. La estructura contiene los ajustes que MFC planea utilizar para la
ventana. Al anular PreCreateWindow le da al programa la oportunidad de modificar cual-
quiera de las características de la ventana, como por ejemplo su forma de cursor, antes de
que MFC cree la ventana.
En la implementación de CShockWaveView, PreCreateWindow carga primero la for-
ma del cursor en forma de cruz estándar de Windows, que tiene un valor de identificación
de IDC-CROSS. La función a continuación registra una clase de ventana, asignándole el
cursor en forma de cruz y un pincel de fondo con un valor de NULL. Un color de fondo
NULL indica al sistema operativo que no debería volver a pintar el fondo de la ventana
cuando cambia el tamaño de la ventana. Como el color de ventana cambia de forma aleato-
ria con cada onda de choque nueva, el propio ShockWave asume la responsabilidad de
pintar el fondo.
Puede que se acuerde de que el programa de ejemplo Color presentado en el Capí-
tulo 5 también pinta su propio fondo. El programa Color no ajusta las etiquetas de
creación de ventana como lo hace ShockWave, pero en su lugar consigue el mensaje
WMERASEBKGND para impedir que el sistema operativo pinte la ventana. Los dos
programas demuestran técnicas diferentes que consiguen el mismo resultado.
.......................................................................
/ / Dibujo de CShockWaveView
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
void CShockWaveView::OnDraw(CDC* p D C )
EL DEPURADOR 449
Como el color de fondo de la ventana tiene un valor de intensidad de 128, cada onda
parece emerger del fondo como una curva sinusoidal.
.......................................................................
/ / Manejadores de mensaje de CShockWaveView
void CShockWaveView::OnSize(UINT nlype, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
utiliza más adelante estas dimensionespara asegurar que el modelo de onda rellena la ventana.
La función OnLButtonDown registra las coordenadas de un clic de ratón dentro del área de
cliente, que determina el centro del siguiente modelo de onda. La función selecciona también
al azar un color nuevo de los seis disponibles en la matriz rgb e invoca Invalidate de modo que
la ventana se vuelve a pintar con el nuevo modelo de onda.
Asegúrese de que la barra de herramientas Build muestra Win32 Debug como la configu-
ración de programa actual:
Pulse la tecla F ~ oO haga clic en el botón de la herramienta Step Over para ejecutar el
código de prólogo de la función. El puntero se detiene en la línea siguiente de la función
PreCreate Window :
La ventana Variables ahora incluye el valor actual de hCur, pero como la instrucción
no se ha ejecutado todavía, el valor que se muestra en la ventana no tiene significado.
Ejecute la instrucción pasando por encima de ella, dándole a hCur el valor devuelto por la
función API LoadCursor. El valor nuevo aparece en rojo para indicar que la última ins-
trucción ha cambiado el valor de hCur. La codificación de color es un bonito rasgo de
algunas ventanas del depurador, que le permite ver rápidamente cuáles de los valores de
variables enumerados ha cambiado la instrucción.
Pulse Fio otra vez para ejecutar la invocación para AfxRegisterWndClass:
La ejecución continúa brevemente hasta que el flujo del programa alcanza el siguiente
punto de ruptura, que colocamos anteriormente en la función OnDraw. Al llegar a este
punto del programa, probablemente vio la ventana ShockWave dar una señal de vida y a
continuación desaparecer. La ventana ShockWave todavía existe, pero al volver a obtener
el control, las ventanas del depurador la han anulado. Puede mostrar la ventana de Shock-
Wave minimizando Visual C++. Tenga en cuenta que ShockWave está completamente
inactivo; ni siquiera tiene todavía una barra de menú. El control en este punto pertenece al
depurador.
Vuelva al depurador y pulse ~ i repetidamente
o para dar un solo paso sobre las declara-
ciones de datos a esta sección de código:
para indicar un problema. Sería sorprendente que estas funciones fallasen, así que no
garantizan el restablecimiento del programa con código adicional para comprobar los va-
lores de retorno. Aunque ShockWave no almacena los valores de retorno de la función,
puede visualizar los valores en la ventana Variables para asegurarse de que las funciones
se ejecutan correctamente. Conforme pasa por cada función, la ventana visualiza un valor
de retorno como este:
Si una función devuelve un código de error cuando pasa sobre ella, el depurador puede
traducir el valor de retorno en un mensaje significativo. En la ventana Watch, haga dos
veces clic en el cuadro de la entrada punteado en la columna Name y teclee err,hr. Al
determinar el valor para esta entrada, el depurador invoca la función API GetLastError y
convierte los resultados en texto útil, como por ejemplo «The handle is invalid» (El mane-
jador no es válido).
La ventana Registers también proporciona otro modo de comprobar los valores de
retorno. En los procesadores basados en Intel, una función Win32 que devuelve un valor lo
coloca en el registro EAX justo antes de salir (los valores de retorno de 64 bits ocupan el
par de registros EDX:EAX). Para comprobar un valor de retorno de función, simplemente
eche un vistazo a EAX en la ventana Registers inmediatamente después de pasar sobre la
invocación de la función. Como la ventana Variables, la ventana Registers visualiza valo-
res nuevos en rojo, indicando qué registros ha cambiado la última instrucción. Como nin-
guna de las funciones anteriores devuelve un valor de cero, sabemos que esta sección de
código se ejecuta correctamente. En este punto, ShockWave está en el aire en el inicio de
los dos bucles que trazan el modelo de onda.
Pero falta algo. El programa no debería trazar las ondas circulares todavía, porque el
fondo del área de cliente de ShockWave todavía permanece sin pintar.
Recuerde que la función PreCreateWindow modificada en el paso 5 indica a Windows
que vuelva a pintar el fondo de ShockWave. Este era el propósito del valor del pincel
NULL dado a AfxRegisterWndClass cuando registra la clase de ventana. Así que como se
ha solicitado, Windows crea correctamente la ventana sin rellenar el área de cliente. El
problema es que ShockWave no cumple su parte del trato. Alguien tiene que volver a
pintar el fondo de la ventana; si no el sistema, el propio ShockWave deberá hacerlo. Hay
una solución para el primer error: ShockWave debe pintar el fondo de la ventana antes de
trazar el modelo de onda.
Tenemos aquí la elección de corregir el código fuente y reanudar la ejecución a través
de Edit and Continue, pero al mostrar la ventana Cal1 Stack vemos que la función OnDraw
es la última línea de las distintas invocaciones de función anidadas a través del núcleo y el
marco de trabajo de MFC. Esto significa que cualquier corrección no tendrá efecto in-
mediatamente, sino sólo cuando la función se vuelva a ejecutar. Para nuestros propósitos,
resulta igual de fácil parar el depurador y volver al editor.
454 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Esto trae a luz una situación interesante. Puede suponer que continuar con la ejecución
de ShockWave sea un modo prudente de detener el depurador. Podríamos salir de Shock-
Wave normalmente utilizando su comando Exit y el depurador se pararía, devolviéndonos
al editor de texto. Bien, inténtelo. Pulse F5 para continuar ejecutando ShockWave.
Nunca puede alcanzar el menú ShockWave de este modo, porque cada vez que pulsa la
tecla F5, la ventana de ShockWave aparece sólo de forma muy breve antes de que vuelva
de dejarla en el punto de ruptura en la función CShockWaveView::OnDraw. No es difícil
ver lo que está pasando. Cuando ShockWave vuelve a tener la atención, debe aparecer en
la parte superior de Visual C++ y cualquier otra ventana en la pantalla. Windows envía a
ShockWave un mensaje WM-PAINT indicándole que se vuelva a pintar a sí mismo. Pero
al volverse a pintar, el marco de trabajo invoca la función OnDraw de ShockWave, dispa-
rando el punto de ruptura. El depurador a continuación obtiene la atención y Visual C++ lo
visualiza justo sobre la ventana de ShockWave. Cada vez que pulsa F5 para continuar la
ejecución de ShockWave, el proceso se repite en un ciclo interminable. Puede romper el
ciclo eliminando o desactivando el punto de ruptura antes de pulsar la tecla ~ s pero, el
botón Stop Debugging en la barra de herramientas Debug (o su comando equivalente en el
menú Debug) proporciona un medio mejor de terminar el depurador:
El pintar el fondo de la ventana no requiere mucho código. En el editor de texto, añada las
líneas que se muestran en gris de modo que la función OnDraw aparezca del modo si-
guiente:
void CShockWaveView::OnDraw(CDC*pDC)
(
CPen pen;
CRect rect;
COLORREF color;
int i, j, iPeriod;
EL DEPURADOR 455
Las líneas en gris ubican un pincel con el color actual indexado por ilolor, pinte el
área de cliente con él y a continuación destruya el pincel. Con el código nuevo en su lugar,
construya una versión de depuración de ShockWave otra vez y ejecútela utilizando el
comando Execute en el menú Build. Debería aparecer correctamente esta vez, esto es, su
fondo pintado con una intensidad media de verde.
El segundo error
ShockWave todavía tiene otro error en él. Este error es más interesante que el primero,
porque demuestra cómo Visual C++ le permite encontrar los errores de programa incluso
cuando el depurador no está activo. Para ver el segundo depurador, haga clic en el ratón en
cualquier sitio en la ventana de ShockWave. Según el diseño del programa, esta acción
debería eliminar la ventana, volver a pintarla con uno de los seis colores disponibles y
volver a trazar el modelo de onda centrado en las coordenadas del clic del ratón. Puede que
tenga que hacer clic varias veces, pero con el tiempo Windows visualiza este mensaje:
Esta es una de las líneas que acabamos de añadir; crea un pincel que OnDraw utiliza
para pintar el fondo de la ventana de cliente de ShockWave. La variable iColor mantiene
un índice para la matriz rgb, que está declarada en ShockWaveView.h, como se muestra a
continuación:
#de£i n e NUM-COLORS 6
El valor actual de iColor determina de este modo el color utilizado para el pincel de
fondo. Eche un vistazo a iColor en la ventana Variables. Debería tener un valor de 6 o más,
lo que significa que el color actual para el pincel es el elemento iColor-ésimo
de la matriz
rgb, que es... 1
Ahí está el problema. Dándole a iColor un valor mayor de 5 significa que el programa
intenta acceder a un elemento de la matriz rgb que no existe, una receta segura para un
error de protección. Hemos encontrado un error, pero jcuál es la causa? La variable iColor
recibe un valor sólo en la función CShockWaveView::OnLButtonDown,que se ejecuta
cuando el sistema detecta un clic de ratón en el área de cliente:
center = point;
icolor = rand()
Invalidate( False )
1
La línea
icolor = rand ( ) ;
asigna a iColor un número aleatorio obtenido de la función rand. Esta función de tiempo
de ejecución de C devuelve un valor desde O hasta RANDMAX, que el archivo de cabe-
cera Std1ib.h define como Ox7FFF o 32.767. ¡Caramba! No se preocupe por qué iColor
termine con un valor tan elevado. Necesitamos asegurar que el valor de iColor nunca
supere el número de elementos de la matriz rgb de modo que la función OnDraw acceda
sólo a elementos de color válidos. Puede limitar el valor de iColor reemplazando la línea
defectuosa por esta:
nes con múltiples hilos y depurar cliente de ActiveX y aplicaciones de servidor, todo antes
del desayuno. El depurador también puede correr en un ordenador mientras que controla el
programa que se está depurando conforme corre en un segundo ordenador.
Excepciones de depuración
La facilidad de manejo de excepciones de C++ permite a los programas retener el control
cuando aparecen errores insospechados. Cuando una función detecta un error, lo notifica
al manejador de excepciones invocando la palabra clave throw. El manejador de excep-
ciones recibe la notificación utilizando catch. Si no existe ningún manejador catch para
una excepción, el depurador le notifica que la excepción no se capturó. Los programas C
también pueden llevar a cabo el manejo de excepciones de forma estructurada con -try y
las sentencias except en lugar de throw y catch.
El cuadro de diálogo Exceptions que se muestra en la Figura 11.14 le permite especifi-
car cómo el depurador debería manejar cada tipo de excepción. Invoque el diálogo hacien-
do clic en el comando Exceptions del menú Debug. Puede ajustar una de las dos opciones,
Stop Always o Stop If Not Handled, para cada tipo de excepción que pueda aparecer en su
programa.
Si especifica Stop If Not Handled para una excepción, el depurador escribe un mensaje
a la ventana Output cuando tiene lugar la excepción, pero no detiene el programa o le
notifica con un cuadro de diálogo a no ser que el manejador de excepciones no consiga
resolver la excepción. En ese punto, es demasiado tarde para solucionar el problema o
examinar el código fuente para ver dónde tuvo lugar la excepción, debido a que el progra-
ma ya ha desechado la excepción y está ejecutando el manejador de excepciones.
Si especifica Stop Always para una excepción, le da más control sobre el proceso de
excepciones. Cuando tiene lugar la excepción, el depurador detiene inmediatamente el
programa, actualiza la ventana fuente para mostrar la instrucción defectuosa y le notifica
antes de que la función de manejador obtenga el control. En algunos casos, puede manejar
la excepción usted mismo modificando cualquier variable errónea en la ventana Variable.
Si pulsa a continuación F5 para continuar ejecutando el programa, aparece un cuadro de
diálogo preguntándole si quiere volver a pasar la excepción a la función de manejador de
excepciones del programa. Si ha solucionado el problema, haga clic en el botón No. De lo
contrario, haga clic en el botón Yes para pasar el control al manejador de excepciones. Si
el manejador de excepciones no puede solucionar el problema, el depurador detiene el
programa y le notifica otra vez como si hubiese seleccionado Stop If Not Handled. Como
la opción Stop Always utiliza los registros de depuración del procesador, la opción no está
disponible para depurar un programa en procesadores que no tiene registros de depuración.
El cuadro de lista Exceptions que se muestra en la Figura 11.14 contiene una lista
predeterminada de las excepciones del sistema. Puede añadir o eliminar excepciones de la
lista, en cuyo caso Visual C++ guarda la nueva lista en el archivo OPT del proyecto. El
depurador trata cualquier excepción que no esté en la lista como una excepción Stop If Not
Handled. Cada excepción tiene un número único. Las excepciones de sistema están defini-
das en el archivo de cabecera Winbase.h con el prefijo EXCEPTION, como por ejemplo
EXCEPTION-ACCESS-VIOLATION.
Para añadir una excepción nueva al cuadro de lista Exceptions, invoque el diálogo
Exceptions y teclee el número de excepción en el control Number y el nombre de excep-
ción en el control Name. Haga clic en el botón de radio Stop Always o Stop If Not Handled
y a continuación haga clic en el botón Add. Para eliminar una excepción, selecciónela de
la lista Exceptions y haga clic en el botón Remove. Si cambia de idea y quiere restablecer
todas las excepciones del sistema borradas, haga clic en Reset. Si cambia una opción por
una excepción, como por ejemplo su nombre, haga clic en el botón Change para hacer el
cambio permanente.
Depuración de hilos
Un hilo es un camino de ejecución dentro de una aplicación que está ejecutándose. Cada
aplicación ejecuta al menos un hilo, conocido como el hilo principal o raíz, que puede a su
vez generar otros hilos secundarios. Cuando depure un programa con múltiples hilos, sim-
plemente seleccione qué hilo desea depurar y siga su curso de ejecución.
Puede seleccionar un hilo sólo después de que el depurador haya comenzado la ejecu-
ción. Primero inicie un punto de ruptura en la ubicación deseada. Cuando la ejecución se
para en el punto de ruptura, todos los hilos que pasan a través del punto quedan posterga-
dos. Haga clic en Threads del menú Debug para invocar el diálogo Threads, seleccione el
hilo que desea seguir de la lista de hilos y haga clic en el botón Set Focus. Conforme
continúe para ir paso a paso por el programa, el depurador sigue el hilo sobre el que ha
centrado la atención. Para impedir que otros hilos ejecuten el mismo código, elimínelos
del diálogo Threads. Puede continuar más tarde con un hilo aplazado seleccionándolo en
el mismo diálogo y haciendo clic en el botón Resume.
tanto del invocador como del DLL. El único paso extra en la depuración de una biblioteca
dinámica de enlace es identificar la aplicación a invocar de modo que el depurador pueda
ejecutarla. Muestre la pestaña Debug del diálogo Project Settings, invocada a través del
comando Settings del menú Project, y a continuación teclee o explore el camino y el
nombre de archivo de la aplicación que invoca:
Si deja sin seleccionar el cuadro Executable For Debug Session, el depurador le pide el
nombre de archivo cuando inicia el depurador de la biblioteca dinámica de enlace. La
ayuda interactiva le recomienda que también seleccione las DLL adicionales en el cuadro
Category, haga dos veces clic en el cuadro de entrada azul en la columna Local Name y
busque el archivo DLL que pretende depurar. Sin embargo, dependiendo de los ajustes de
la ruta, el sistema operativo puede fracasar en el intento de ubicar el archivo DLL cuando
empieza la depuración. Puede ahorrarse estos problemas simplemente ignorando el ajuste
de las DLL Additional y colocando una copia del archivo ejecutable del programa que
invoca en su carpeta Debug del proyecto. Al colocar el invocador y el DLL en la misma
carpeta, asegura que Windows siempre pueda cargar el DLL.
Después de ajustar puntos de ruptura en el código fuente de la biblioteca, elija el
comando Go o pulse F5 para iniciar el depurador. No cambia nada si el programa que
invoca está en forma de depuración o en forma final, pero en el último caso Visual C++
visualiza un mensaje informándole que el programa no tiene símbolo de información.
Como se está depurando el archivo DLL, no el programa que invoca, este mensaje es sólo
una formalidad, que le recuerda que no podrá devolver el flujo de la ejecución al programa
que invoca. Haga clic en el botón OK para iniciar el proceso de depuración.
operan el programa monitor remoto del depurador. Los archivos están en las subcarpetas
Common\MSDev98\Bin y VC98\Redist de su carpeta de Visual C++.
m( r dword p t r [ i V a r l , O
Con todo, ambas instrucciones escriben cero en el número entero iVar igual de bien.
En los procesadores 80486 y Pentium, las instrucciones
push 1
pop eax
suponen 3 bytes y 2 ciclos de reloj, casi la mitad del tamaño pero sólo la mitad de veloci-
dad de la instrucción equivalente:
mov e a x , 1
La mezcla combina los bucles en un solo bucle que hace el mismo trabajo, evitando la
sobrecarga del segundo bucle:
Visual C++ no reconoce una posibilidad de mezclar bucles como ésta, así que sin
intervención humana la oportunidad de optimizar se pasaría por alto.
Cuando decida si optimizar para velocidad o tamaño, debería tener en cuenta que
mientras que los ahorros en velocidad son casi siempre conmensurables, no son siempre
discemibles. Existe una amplia laguna entre lo que el reloj del ordenador puede medir y lo
que la mente humana puede distinguir. El aumento de la velocidad del programa que el
usuario final no puede detectar representa un esfuerzo inútil.
Generalmente, sólo las optimizaciones algorítmicas resultan en mejoras notables en
velocidad de ejecución. Los niveles más bajos de optimización normalmente no ahorran
los millones de ciclos de reloj necesarios para la detección humana, a no ser que se apli-
quen a bucles específicos o funciones que se ejecuten muchos cientos de veces. Por esta
razón, una escuela de práctica de programación ha evolucionado dictando los algoritmos
eficaces de escritura al nivel fuente y ajustando el compilador para optimizar el tamaño en
lugar de la velocidad. Los sistemas operativos multitarea tales como Windows invitan
especialmente a esta práctica. Un programa con una imagen de memoria más pequeña se
ejecuta con menos riesgo de incurrir en defectos de página en condiciones de memoria
saturada. Los defectos de página, en los que el sistema operativo debe volver a cargar
memoria desde el disco, son operaciones caras. Ponga los suficientes juntos y un progra-
ma, independientemente de lo que optimizase su velocidad, aparece impasible y lento.
Técnicas de optimización
Visual C++ nace de una colección de técnicas de optimización, muchas de las cuales se
han utilizado por compiladores durante décadas. La Tabla 12.1 enumera las técnicas de
optimización más importantes que Visual C++ utiliza e indica si el propósito de cada una
es reducir el tamaño de código, aumentar la velocidad de código, o ambas cosas. Como
hay tantas variables indicadas, es a veces difícil de predecir con exactitud el efecto general
de una técnica de optimización. Por lo tanto, la tabla refleja sólo las intenciones del compi-
lador, no necesariamente el resultado. Los mejores ajustes de optimización para un progra-
ma particular pueden a menudo estar determinados sólo por el tanteo.
Aquí empezamos una serie de cortas subsecciones que examinan catorce métodos de
optimización enumerados en la Tabla 12.1. Cada subsección describe cómo funciona una
optimización, cuándo se utiliza y cuáles son sus ventajas y desventajas.
466 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
En los viejos tiempos de programación C, el buen uso dictaba el uso de la palabra clave
register para «grabar» una o dos de las variables locales de una función. La clase de
almacenamiento de registro representaba una solicitud desde el programador al compila-
dor para mantener una variable local en un registro del procesador, si hubiese alguna
disponible, en lugar de en la memoria ubicada en el marco de la pila. Además de ahorrar
una pequeña cantidad de espacio de almacenamiento, al mantener una variable en un
registro se asegura el acceso posible más rápido al mismo, porque el procesador lee y
escribe en sus propios registros mucho más rápido de lo que lee y escribe en memoria. La
administración de una variable en un registro en lugar de en memoria puede también
resultar en un ligero descenso en lo concerniente a tamaño de código.
Hoy ya no se utiliza register,porque un compilador de optimización como Visual C++
maneja la tarea de forma automática (en realidad, Visual C++ ignora la palabra clave
register). Casi cualquier objeto de datos es un candidato para el registro, como por ejem-
plo las variables locales y globales, los valores constantes, los elementos de estructura y
los argumentos de función, incluyendo punteros para los argumentos pasados por referen-
cia. El compilador escanea una función para determinar cómo utiliza sus datos, asignando
a cada variable una puntuación que representa el beneficio derivado del almacenamiento
de la variable en un registro. Cuando escriba el código de objeto de función, el compilador
coloca las variables de puntuación más elevadas en los registros siempre que puede. El
resultado es el aumento de la velocidad de ejecución, que en circunstancias favorables
puede ser significativo.
Los registros son una comodidad muy escasa, y el compilador debe tomar decisiones
inteligentes para determinar cuándo utilizar un registro para almacenar una variable. El
código optimizado pasa una parte de su tiempo haciendo malabarismos con los datos entre
los registros y la memoria. El código puede liberar un registro escribiendo sus contenidos a
la dirección de memoria inicial de variable, pero el compilador de optimización debe
primero decidir si el acceso a memoria merece la pena. Al liberar un registro sólo para
volverlo a cargar más tarde otra vez con el mismo valor, puede que no merezca la pena si
deja el registro disponible sólo durante una sección breve de código.
en este:
i = nParam;
Function( nParam ) ;
i = j;
468 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Como vimos en el ejemplo anterior, la propagación de copia a menudo deja una sentencia
de asignación interminable como almacén muerto, una condición en la que un programa
escribe información a una variable sin leer de ella. Cuando reconoce una asignación de
almacén muerto, el compilador de optimización simplemente salta sobre la instrucción de
modo que no se convierte en parte de la imagen objeto. Las tres instrucciones originales en
el fragmento, por ejemplo, quedan reducidas a dos instrucciones después de que el compi-
lador elimine el almacenamiento muerto:
Cuando el compilador reconoce que una serie de subexpresiones reflejan todas el mismo
valor, calcula la subexpresión una vez y sustituye el resultado para todas las subexpresio-
nes en las series. Por ejemplo, considere un fragmento en el que aparezca la subexpresión
y * z dos veces:
Añadiendo una asignación y reemplazando las dos subexpresiones con una variable, el
compilador elimina una de las dos operaciones de multiplicación:
temp = y * z;
x = temp;
w = temp;
Dependiendo de las circunstancias y de si la subexpresión aparece lo suficientemente a
menudo, la sustitución puede reducir el tamaño de código del fragmento; sin embargo, la
eliminación de la subexpresión común resulta casi siempre en mayor velocidad.
Optimizaciones de bucle
temp = x + y ;
for (i=O; i < 10; i++)
n~rray[i] = temp;
Planificación de instrucciones
Los procesadores superescalares como la serie Pentium pueden ejecutar dos instrucciones
simultáneamente en conductos gemelos, siempre que una instrucción no dependa del re-
sultado de la otra. Una dependencia conlleva a una condición llamada detención del con-
ducto por sobrecarga. Utilizando la planificación de instrucciones, también conocida
como ordenamiento de instrucciones, el compilador evita semejantes dependencias vol-
viendo a organizar el orden de las instrucciones máquina donde sea posible. Por ejemplo,
considere las tres instrucciones etiquetadas A, B y C:
Reducción de fuerza
Los procesadores son rápidos en sumar y restar pero relativamente lentos en multiplicar y
dividir. El procesador Pentium, por ejemplo, puede sumar registros de 32 bits en un solo
ciclo de reloj, e incluso así necesita 10 ciclos para multiplicar y alrededor de 40 para
dividir. Cuando se optimiza, el compilador de Visual C++ busca oportunidades para redu-
cir la complejidad aritmética o «fuerza» de una instrucción sin afectar al resultado final del
cálculo.
Por ejemplo, la fuerza de una instrucción que multiplica o divide por un potencia de 2
se puede reducir sustituyendo una operación de desplazamiento equivalente. Suponiendo
que y es un número entero sin signo, el compilador puede expresar mejor la instrucción
así:
Expansión en línea
Hay varias razones por las que el acto de invocar una función ralentiza el progreso del
flujo de ejecución de un programa. Como el procesador salta a una nueva ubicación en el
código, la lista de instrucciones de próxima aparición almacenada en la cola de instruccio-
nes del procesador puede que ya no sea válida. Si el procesador no lleva a cabo una
predicción de rama (como lo hace el Pentium), debe detenerse mientras que la cola esté en
funcionamiento y se obtiene de memoria la primera instrucción de la función. Peor, la
invocación puede generar una serie de escrituras en memoria a medida que los parámetros
de función se desplacen hacia arriba en la pila junto con el registrado EIP del procesador
(para una descripción del registro EIP, consulte el texto tramado de la página 423 del
Capítulo 11, «El depurador»). Después de que la función termine, el procesador se detiene
de nuevo cuando la dirección de retorno se devuelve de la pila al registro EIP, se pone en
funcionamiento, si es necesario, la cola de precaptura, y se lee de la memoria la siguiente
instrucción a la que apunta. En pocas palabras, las funciones son costosas de obtener y de
abandonar.
La expansión en línea resuelve estos problemas, pero algunas veces al precio de
aumento de tamaño del código. En esta técnica de optimización, el compilador inserta el
código de función en el cuerpo del programa, reemplazando la invocación de función con
una copia de la propia función. Una instrucción máquina CALL no se genera nunca, per-
mitiendo que el procesador siga un camino secuencial de las instrucciones sin que se
desvíe hacia otro sitio. Siguiendo un camino lógico secuencial, el procesador puede pre-
capturar instrucciones con más exactitud, un ahorro que es más significativo cuando la
expansión tiene lugar dentro de un bucle.
Puede parecer sorprendente, pero la expansión en línea a menudo reduce el tamaño de
un programa. La expansión en línea (también conocida como colocación en línea) es más
efectiva cuando se aplica a funciones pequeñas, especialmente a aquellas con parámetros
que son constantes y que se pasan por referencia en vez de valor. En tales casos el compila-
dor puede suministrar secciones enteras de código que escribe valores de parámetro a la
pila. La colocación en línea de una función ahorra el gasto de una sección de prólogo y
epílogo y la creación de un puntero de pila separado. La colocación en línea también
muestra los efectos secundarios de una función -cambios a una variable global, por ejem-
472 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
plo- que de otra manera sería invisible al compilador. Esto permite una optimización más
agresiva que puede que sea posible sin colocación en línea.
Conjunto de cadenas
El compilador puede determinar cuándo un programa crea la misma cadena más de una
vez. El conjunto de cadenas es una técnica de optimización en la que el compilador reserva
espacio de datos sólo para la primera cadena y vuelve luego a apuntar las referencias a
cualquier cadena de duplicados a la primera.
La supresión de punteros marco es una optimización para sistemas Intel que ahorra el
gasto del código de prólogo y epílogo; un ahorro considerable para programas que tienen
muchas funciones. Sin supresión de punteros marco, el compilador genera código de pró-
logo para cada función que requiere puntero de pila, apuntando al registro EBP del proce-
sador en la parte superior del marco:
Utilizado de este modo, el registro EBP se llama puntero de marco. Las variables con
clase de almacenamiento automático se referencian en el puntero de pila por medio de
desplazamientos relativos a EBP. El uso de EBP como el puntero de marco es un legado
innecesario de las versiones más antiguas de Windows diseñadas para correr en el procesa-
dor Intel 80286. Cuando se activa la supresión del puntero de marco, las referencias del
compilador amontonan los datos relativos al registro ESP en lugar de al registro EBP. Un
prólogo de función se convierte en una instrucción simple que ajusta el puntero de pila
ESP para crear el marco de pila:
Como muestra la Figura 12.1, es posible que una aplicación exceda las posibilidades
de la página de guarda de la pila e intente probar suerte en la memoria reservada. Esto
puede suceder cuando una función reserva más de una página de pila para sus variables
locales:
void BigLocal ( )
{
char chArray[3*40961; / / Reserva tres paginas (12 Kb) de pila
chArray[12000] = -1; / / Esta asignación puede fallar
En esta simple ilustración, chArray consume tres páginas (12 Kb) de pila. La función
reserva espacio para los datos automáticos disminuyendo el puntero de pila del procesador
ESP mediante los 12 Kb solicitados, pero esto sólo no realiza más pila. Si el espacio
reservado para chArray empieza cerca de la parte inferior de la pila, el acceso de un
elemento elevado de chArray puede que sobrepase la página de guarda de la pila hasta la
memoria de reserva. Esto dispara una violación de acceso que el sistema soluciona termi-
nando la aplicación.
La comprobación de pila en Win32 evita este tipo de escenario. Cuando se habilita la
comprobación de pila, el compilador computa el tamaño total de los datos locales de cada
función. Las funciones con variables locales que consumen menos de una página de pila
no pueden alcanzar la página de guarda, y por ello no necesitan comprobación de pila.
Cada función con más de una página de datos automáticos va precedida, sin embargo, por
una invocación a la rutina de comprobación en la biblioteca de tiempo de ejecución de C.
La rutina de comprobación de pila simplemente alcanza las páginas secuenciales de la
pila; es decir, introduce un bucle que lee un byte en el montón a incrementos de 4.096
bytes. El ciclo comienza en la parte superior de la pila y continúa hacia abajo hasta que la
rutina de la comprobación de pila ha pasado por las páginas suficientes para cumplir las
necesidades de pila de la función.
Un vistazo a la Figura 12.1 muestra cómo la comprobación de pila soluciona el proble-
ma de la función BigLocal. Antes de que BigLocal obtenga el control, la rutina de compro-
bación de pila alcanza la pila en las páginas 1, 2, y 3 bajo la parte superior de la pila.
Suponiendo que la colocación para chArray empiece en la última página asignada de la
pila, el primer toque accede a la página de guarda. El sistema responde asignando otra
página, haciéndola la nueva página de guarda. La segunda iteración del bucle en la rutina
de comprobación de pila alcanza la página de guarda nueva, haciendo que el sistema
asigne otra página. El proceso se repite una tercera vez, añadiendo tres páginas a la pila
antes de que la rutina de comprobación de pila devuelva BigLocal y obtenga el control.
Ahora, cuando BigLocal accede a un elemento cercano al final de chArray, el acceso recae
en una página asignada de la pila y no dispara un error.
Entonces, ¿podría BigLocal resolver su propio problema sin la comprobación de pila?
Por supuesto. Simplemente accediendo a elementos tales como chArray[4000] y ch-
Array[8000] antes que chArray[12000], la función se ocupa de asignar la memoria nece-
saria y asegurar que la pila no esté saturada. La comprobación de pila añade sobrecarga a
un programa, y deshabilitándola ahorra código y mejora la velocidad para las aplicaciones
con grandes demandas de almacenamiento automático. Tales aplicaciones no necesitan
comprobación de pila siempre que se acceda a datos de pila en páginas secuenciales yendo
desde la parte superior de la pila hacia la inferior.
Superposición de pilas
Suponer no alias
Aliasing significa el uso de más de un nombre para referirse a un solo objeto de memoria.
Los punteros y uniones ofrecen al programa oportunidades infinitas para los alias, como se
muestra en este ejemplo típico en el que c y *cptr se refieren al mismo byte de memoria:
char c;
char "cptr = &c;
main ()
{
char *ptrl = chArray; / / puntos ptrl para chArray
char *ptr2 = GetPointerO; / / Igual que ptr2
1
char * GetPointer (void)
{
return chArray;
1
Es posible que se optimice una función fuera de la existencia, ya sea a través de la coloca-
ción en línea o porque el compilador haya deducido cómo compilar el programa de tal
modo que la función no se invoque nunca. Sin embargo, la función todavía debe compilar-
se e incluirse en la imagen del objeto, porque el compilador no tiene modo de determinar si
otros módulos fuente acceden a la función. Sólo el enlazador puede reconocer cuándo una
función permanece sin referenciar en un programa. Si el compilador escribe una función
no referenciada de forma «empaquetada>>,el enlazador la omite del ejecutable terminado.
El enlace a nivel de función asegura que todas las funciones en un módulo de fuente
están empaquetadas, es decir, identificadas en el código objeto por un registro COMDAT.
Los registros COMDAT están en Formato de archivo de objeto común (COFF) y contie-
nen información que permite al enlazador reconocer funciones no referenciadas y elimi-
narlas de la imagen del ejecutable, un procedimiento llamado eliminación COMDAT tran-
sitiva. Sin un registro COMDAT, una función no referenciada permanece en la imagen
después del enlace, ocupando espacio.
CAMBIOS DE OPTIMIZACIÓN
Ahora vamos a ir de lo general a lo particular. Esta sección conecta lo que hemos aprendi-
do hasta ahora sobre las optimizaciones de compilador con los cambios en Visual C++ que
controlan el proceso de optimización. Los cambios están contenidos en el diálogo Project
Settings que se muestra en la Figura 12.2, que se invoca utilizando el comando Settings del
menú Project. El diálogo Project Settings es una madriguera de cambios y opciones que
afectan al proceso de construcción y a la eficacia del ejecutable terminado. Esta sección se
concentra en la pestaña C/C++ del diálogo, que contiene todos los cambios que controlan
cómo (o si) el compilador optimiza los archivos fuente del proyecto.
Los ajustes de optimización predeterminados dependen del destino de construcción.
Visual C++ desactiva las optimizaciones cuando construye una versión de depuración,
asegurando que el programa ejecutable es una traducción literal del fuente. Para una ver-
sión final, el compilador optimiza por defecto en favor de la velocidad, incluso a expensas
de aumentar el tamaño del código. Para muchos proyectos, los ajustes de la optimización
3. Elegir la
optimización
Optimizaciones globales
Genera funciones intrínsecas en línea
Favorece código pequeño
Favorece código rápido
Supresión del puntero de marco
Deshabilita la comprobación de pila
Conjunto de cadenas
Enlace a nivel de función
480 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
La segunda elección utiliza un truco específico de Intel para evitar la cara instrucción
IMUL, resultando en una secuencia de código más larga pero más rápida:
Procesador
El cuadro Processor le permite seleccionar el nivel del procesador Intel para el que optimi-
zar. El ajuste predeterminado, llamado Blend, representa un compromiso que es parte de
un destino oscilante hoy en día. En la versión 4 de Visual C++, el ajuste Blend hizo que el
compilador optimizase principalmente para el Intel80486, añadiendo optimizaciones para
el 80386 y los procesadores Pentium que no dificultan el rendimiento en 80486. En las
versiones 5 y 6 el ajuste Blend apunta al Pentium, añadiendo optimizaciones seleccionadas
para el procesador 80486 de más bajo nivel.
Independientemente del ajuste del procesador, el compilador genera sólo instrucciones
máquina reconocibles para el 80386. Esto le asegura que un programa optimizado puede
correr en un procesador de nivel más bajo incluso si es compilado con el ajuste Pentium o
Pentium Pro. En realidad, los ajustes Pentium y Blend tienen el mismo efecto.
Convención de invocación
Alineación de estructura
El ajuste final en la categoría Code Generation especifica el límite sobre el que están
alineados la estructura y los miembros de unión. Después del primer miembro de una
estructura, cada miembro siguiente recae en el límite de memoria determinado por el
tamaño del miembro o por el ajuste de alineación, lo que sea más pequeño. Ajustar un
valor de estructura de línea a 1 le asegura que no se desperdicia memoria en huecos entre
los miembros de estructura, una técnica conocida como empaquetamiento de estructura.
El empaquetamiento se puede reducir al uso de la pila para estructuras con clase de
almacenamiento automático, o reducir el tamaño de programa cuando se aplique a las
estructuras con clase de almacenamiento estático. Sin embargo, para que el empaquetado
tenga algún efecto, una estructura debe contener al menos un elemento que abarque 1 o 2
bytes colocados ante un elemento de multibyte mayor tal como un número entero. Por
ejemplo, considere el efecto del empaquetamiento en esta estructura simple:
Tabla 12.4. Ajustes en el cuadro Use Run-Time Library que determinan cómo un
programa se adjunta a la biblioteca de tiempo de ejecución de C
Biblioteca de
Ajuste tiempo de ejecución Descripción
- -
Una alineación de valor de 4 o más desperdicia 3 bytes de memoria entre los dos
elementos, porque el compilador coloca el elemento i en un límite de palabra doble. Sin
embargo, un valor de alineación de 1 empaqueta i adyacente en la memoria para ch:
-
s.¡ (byte 1) 1 (byte 21 1
(byte 2) 1 (byte 3) 1
s.¡ (byte 7) 1 1 (byte 3)
1 (byte 4) 1
(byte 21 1 1 (byte 4 ) 1
(byte 31
(byte 4)
Categoría Customize
La categoría Customize (Fig. 12.4) controla las optimizaciones que permiten el enlace a
nivel de función y la eliminación de cadenas duplicadas (conjunto de cadenas). Ambas
optimizaciones son parte integral de la opción Maximize Speed. Si selecciona Maximize
Categoría Optimizations
La categoría Optimizations ofrece mejor control sobre los tipos de optimizaciones aplica-
dos al proyecto y también le permite especificar si el compilador debería expandir funcio-
nes en línea. La categoría visualiza el mismo ajuste de optimización seleccionado en la
categoría General. El ajuste debe ser Customize para habilitar los cuadros de comproba-
ción que se muestran en la Figura 12.5, que le permiten elegir de entre la lista de optimiza-
ciones de compilador. El ajuste Customize proporciona el único camino de activar la
optimización Assume No Aliasing.
El último cambio del cuadro de comprobación en la lista, etiquetado Full Optimiza-
tion, activa una serie de optimizaciones que incluyen la expansión en línea, funciones
intrínsecas, favorece el código rápido, no hay comprobación de pila ni optimizaciones
globales. El otro cuadro de comprobación de la lista que puede necesitar alguna explica-
ción está etiquetado como Improve Float Consistency. Este cambio es en realidad una
optimización cuando se desactiva. Al precio de más código y operaciones más lentas de
coma flotante, al activar el cambio hace que el compilador recorra los pasos siguientes
para reducir la posibilidad de errores de redondeo:
Añade instrucciones que copien datos de la memoria a los registros de coma flotante
antes de cada operación de coma flotante. Aunque esto ralentiza la operación de
forma considerable, se garantiza que el resultado del cálculo no tiene más precisión
que el tipo de datos que puede guardar.
i Inhabilita la forma intrínseca en línea de funciones de tiempo de ejecución que lleve
a cabo los cálculos de coma flotante, que se enumeran en la Tabla 12.3. El programa
utiliza las funciones de tiempo de ejecución en su lugar.
Inhabilita otras optimizaciones que puedan permitir un resultado de cálculo para
persistir en la precisión de 80 bits del procesador de coma flotante.
Estos pasos mantienen los resultados de cálculos de coma flotante en precisión de 32 o
64 bits y ayudan a asegurar que los números de coma flotante se pueden probar para una
igualdad exacta. Sin embargo, incluso si activa el cambio Improve Float Consistency, es
una buena idea permitir una pequeña tolerancia cuando compara números de tipo float o
double, como estos:
DE DEPURACIÓN A FINAL
Construir una versión final de una aplicación normalmente es poco frecuente durante el
ciclo de producción. La primera construcción final puede que llegue después de pasar
semanas o meses desarrollando la versión de depuración. Normalmente, la creación del
destino final no implica más que una construcción nueva, pero puede que surjan proble-
mas. Esta sección trata algunas de las dificultades potenciales que pueden aparecer cuando
pasa de los destinos de depuración a los de final y explica cómo evitarlos.
Para construir una versión final de un programa, haga clic en el comando Set Active
Configuration en el menú Build y selecciones Win32 Release, o seleccione el destino en la
barra de herramientas Build:
Los problemas de hilo oculto algunas veces afloran a la superficie en una construcción
final de programa. Considere el error común de dos hilos que acceden simultáneamente a
una función que escribe una variable estática. En una versión de depurador la variable
siempre permanece en memoria, así que el conflicto potencial entre los hilos nunca puede
surgir debido a las ligeras diferencias de los tiempos. En una construcción final, sin embar-
go, la posibilidad de error es más amplia, porque la variable puede registrarse durante la
duración de la ejecución de la función. Esto hace más probable que un hilo sobrescriba los
resultados del otro.
Un programa optimizado puede fallar debido a muchos otros tipos de problemas de
código fuente, algunos de los cuales se enumeran en la Tabla 12.5. Para hacer el segui-
miento de un problema, intente activar estas optimizaciones individualmente para deter-
minar bajo qué circunstancias surge el error. La segunda columna de la tabla ofrece suge-
rencias de qué buscar cuando examine su código.
Es quizás inherente a la condición humana el sospechar del optimizador cuando se
rompe la versión definitiva. Después de todo, el compilador vuelve a escribir nuestro
código de modos desconocidos. Pero el arte de la optimización de código ha alcanzado un
elevado grado de fiabilidad en el compilador de Visual C++. Microsoft concede la con-
fianza suficiente en su propio producto que los desarrolladores de Microsoft optimizan las
versiones definitivas de productos mayores escritos en C/C++, como por ejemplo Win-
dows 95, Windows NT y Microsoft Office. El hecho por sí solo debería aplacar cualquier
preocupación persistente sobre si la optimización es de algún modo insegura.
Tabla 12.5. Problemas normales de código fuente que pueden aflorar
de la optimización de código
(Continuación)
Microsoft Visual C ++ 4 Competidor más cercano
mov EBX,offset FLAT:?X@@3QAVTest@@A
rnov EDX, -034h[EBPl
rnov EAX, -038hLEBPl
rnov -020h[EBPl, EAX
sub eax, ecx rnov -OlCh[EBPl, EDX
rnov ecx, DWORD PTR ?Y@@3PAVTest@@A[edx-41 L352 :
ddd eax, 3r1023 P?R lesil rnov EDX, 4[ESIl
rnov DWORD PTR $T1455[esp+201, eax mov EAX, [ESII
lea eax, DWORD PTR [ecx+ebx+81 rnov -010h[EBP], EAX
lea ebx, DWORD PTR [edi-edif41
8 rnov -0Ch [EBP], EDX
mov edi, DWORD PTR $T1455[esp+201 rnov EDX, 4IEBXl
add eax, ebx rnov EAX, [EBXI
mov DWORD PTR [esil, edi mov -018h[EBPl, EAX
rnov DWORD PTR Lesil , edi rnov -018h[EBPl, EAX
rnov DWORD PTR [esi+4], eax rnov -014h[EBPl, EDX
cmp edx, 8000 rnov ECX, -020h[EBP]
j1 SHORT $Ll220 imul ECX, -018h[EBPl
POP edi rnov EDX, -OlCh[EBP]
POP esi imul EDX, -014h[EBPl
DOD
.
~ ~ ebx
L~
sub ECX, EDX
add esp, 8 0 rnov -030h[EBPl, ECX
ret O rnov ECX, -020h[EBP]
imul ECX, -014h[EBPl
rnov EDX, -OlCh[EBPl
imul EDX, -018h[EBP]
El uso de EBP para almacenar el cálculo intermedio ahorraría dos accesos de memoria
en cada iteración de bucle.
La versión de Visual C++ ahorra espacio de código y gana velocidad combinando
modos de direcciones en una única instrucción. Esto permite utilizar la instrucción LEA
(carga de dirección efectiva) para simple aritmética, un rasgo conocido de los procesado-
res Intel. Utilizando el desplazamiento izquierdo y la adición, la instrucción LEA puede
manipular registros de índice y de base para llevar a cabo ciertas operaciones de multipli-
cación más rápidas que las instrucciones de multiplicar MUL e IMUL del procesador. Por
ejemplo, aquí tiene cómo una única instrucción LEA puede multiplicar el valor almacena-
do en el registro EAX:
Instrucción Descripción
Aunque el uso de LEA de este modo está bien documentado por Intel, el otro compila-
dor recurrió a cuatro instrucciones IMUL, que como tienen lugar en un bucle que se itera
mil veces, son particularmente caras. La longitud del bucle producida por el otro compila-
dor es también contundente, abarcando 39 instrucciones máquina. La versión de Visual
C++ del bucle requiere sólo 18 instrucciones y no tiene instrucciones IMUL en absoluto.
494 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Para nuestra sorpresa, el otro compilador no ponía ningún cuidado en el uso de los
registros. Tenga en cuenta esta secuencia tomada desde el código en el marcador 0,en la
que un valor se escribe en la pila y a continuación se accede otra vez inmediatamente:
Como el registrador ECX está ya cargado, no es necesaria la lectura del valor otra vez
desde la memoria. La secuencia debería ser más pequeña si se compilase de este modo:
La segunda instrucción no puede alterar el registro EDX hasta que la primera instruc-
ción haya terminado de leerla. Al cambiar el orden de la segunda y tercera instrucción se
evita el paro potencial, asegurando que las instrucciones adyacentes en la secuencia se
puedan ejecutar simultáneamente:
SHORT SL1621
POP edi
POP esi
ret O
La sección de código marcada @ ofrece mayor entendimiento en los trabajos del com-
pilador de optimización. El optimizador escribe esta sección en estos tres pasos, en la que
el código lee dos números enteros de la memoria, los añade a los valores de los registros y
a continuación escribe las sumas de nuevo en memoria. Una alternativa es añadir simple-
mente los registros directamente a los números enteros en memoria, reemplazando todo el
código en el marcador @ con estas tres líneas:
EL DIÁLOGO OPTIONS
El comando Options visualiza el diálogo de pestañas que se muestra en la Tabla 13.1. El
diálogo Options contiene una colección de gran variedad de ajustes que abarca el compor-
tamiento y la apariencia de los editores de Visual C++ y el depurador, el espacio entre
tabuladores y las sangrías en el editor de texto, las ubicaciones de directorios de archivos
MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Tabla 13.1. Ajustes en el diálogo Options, invocado haciendo clic en Options del
menú Tools
(Continúa)
500 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Las pestañas Editor, Tabs y Compatibility del diálogo Options se aplican sólo al editor
de texto. La mayor parte de los ajustes en estas pestañas afectan de forma simultánea a las
visualizaciones normales y de pantalla completa del editor, así como a la ventana de texto
fuente del depurador. Esto hace posible crear tres composiciones de pantalla diferentes en
el entorno: una para editar en visualización normal, otra para visualización de pantalla
completa y una tercera composición para la depuración.
EL DIÁLOGO CUSTOMIZE
El comando Custornize visualiza el diálogo que se muestra en la Tabla 13.2, que propor-
ciona el medio para:
8 Añadir o borrar comandos de menú.
m Añadir iconos a comandos de menú.
m Activar y desactivar barras de herramientas.
Añadir o borrar botones de barra de herramientas.
m Crear comandos de pulsación de tecla con nombre.
Ya nos hemos encontrado de forma breve algunos de estos comandos en otros capítu-
los. Por ejemplo, el Capítulo 3, «El editor de texto», mostró cómo asignar las pulsaciones
de tecla para dos comandos no vinculados llamados WordUpperCase y WordLowerCase.
La pestaña Commands del diálogo Customize le permite controlar el contenido de los
menús del entorno. Mientras que el diálogo Customize está visible en la pantalla, puede
visualizar los menús, pero los comandos individuales en los menús no están activos. En su
lugar, el entorno actúa como un editor de menú en el que puede añadir o borrar comandos
de menú de datos, cambiar el orden de los comandos y añadir iconos para los comandos
existentes.
Tabla 13.2. Ajustes en el diálogo Customize, que se invoca al hacer clic en Customize
del menú Tools
(Continúa)
502 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Por ejemplo, aquí tiene cómo añadir imagen de icono al comando Page Setup en el
menú File, que normalmente no tiene icono. El primer paso es pedir prestado una imagen
adecuada y almacenarla en el Portapapeles. Un programa de captura de pantalla sirve bien
para este propósito, o puede designar su propia imagen de 16 por 16 en el editor de gráfi-
cos de Visual C++. También puede capturar una imagen de icono en el Portapapeles desde
cualquier barra de herramientas del entorno. Con la pestaña Commands visible en el diálo-
go Customize, haga clic en el botón derecho del ratón para visualizar el menú contextual
que se muestra en la Figura 13.1. El comando de menú Copy Button Image copia la
imagen del icono de botón al Portapapeles.
El propio diálogo Customize sirve como una fuente conveniente de imágenes de boto-
nes. Todas las imágenes de icono en las barras de herramientas de Visual C++ se pueden
visualizar dentro (y pedirse prestadas desde) el diálogo Customize. Primero haga clic en el
cuadro de combinación Category en la pestaña Commands para visualizar una lista desple-
gable de menús. Al seleccionar un menú en la lista, visualiza una colección de pequeños
iconos para comandos en el menú. Para nuestro ejemplo, seleccione File de la lista desple-
Figura 13.1. Cuando está visible el diálogo Customize, si hace doble clic
en un comando d e menú o botón de barra d e herramientas,
muestra este menú contextual.
gable Category y haga clic en el botón derecho del ratón sobre las imágenes de icono en la
lista que expresa de forma aproximada la idea de «ajustar página». Esto visualiza el mismo
menú contextual que se muestra en la Figura 13.1, desde el que puede elegir el comando
Copy Button Image para copiar la imagen al Portapapeles.
Una vez que tiene almacenada la imagen de 16 por 16 en el Portapapeles, transfiera la
imagen al comando Page Setup en el menú File. Con la pestaña Commands todavía visi-
ble, haga clic en File en la barra de menú para exponer el menú, a continuación haga clic
en el botón derecho del ratón sobre el comando Page Setup para visualizar el menú de
contexto que se muestra en la Figura 13.1. Elija el comando Paste Button Image para
colocar la nueva imagen de icono a la izquierda del comando Page Setup en el menú File:
Antes DespuBs
Añadir un comando nuevo a uno de los menús de entorno sólo lleva dos pasos:
1. Sacar el menú mientras que la pestaña Commands del diálogo Customize aparece
en la pantalla.
2. Seleccionar el grupo deseado de herramientas de la lista desplegable Category y
arrastrar el icono de herramienta o el nombre de comando desde el diálogo al
menú. Una barra de colocación horizontal indica la posición del menú.
Para borrar el comando nuevo del menú, haga clic en el botón derecho del ratón y elija
Delete del menú emergente. También puede añadir las entradas de menú para macros y
comandos no vinculados. Elija Al1 Commands de la lista desplegable Category, coloque el
nombre de comando en la lista, a continuación arrastre el nombre de comando de la lista a
la posición deseada en el menú. La Figura 13.2 muestra el procedimiento para añadir el
comando WordUpperCase al menú Edit.
BARRAS DE HERRAMIENTAS
El aspecto de las barras de herramientas en la pantalla depende del editor activo y de si el
editor está en modo de pantalla completa. Por defecto, las barras de herramientas permane-
cen invisibles en las visualizaciones de pantalla completa; para hacer visible una barra de
herramientas, active la visualización de pantalla completa y pulse ALT+T para mostrar el
menú Tools. Haga clic en el comando Custornize y en la pestaña Toolbars, a continuación
active el cuadro de comprobación adyacente a la barra de herramientas deseada en la lista.
También puede hacer el menú visible en modo de pantalla completa de esta forma. Como
el cambio tiene lugar en modo de pantalla completa, el aspecto de la barra de herramientas
y su posición en la pantalla se aplican sólo a la visualización de pantalla completa. Cuando
pulsa ESC para volver a la visualización normal, la barra de herramientas vuelve a su
posición original y puede que incluso no sea visible.
do no vinculado como WordUpperCase, elija Al1 Commands del cuadro Category para
visualizar una lista de nombres de comandos. Coloque el comando deseado en la lista, a
continuación arrastre la entrada desde la lista y despliéguela en una barra de herramientas.
Para copiar uno de los botones predefinidos del entorno, seleccione un menú de la lista
Category y arrastre un botón del diálogo Customize a la barra de herramientas.
No tiene que utilizar una barra de herramientas predefinida para recibir el botón de
herramienta nueva, porque el entorno ofrece dos métodos para crear una barra de herra-
mientas personalizada. El primer método es hacer clic en el botón New en la pestaña
Toolbars del diálogo Customize, a continuación dar un nombre a la barra de herramientas:
Cuando hace clic en OK, Visual C++ crea una barra de herramientas en blanco en la
que puede colocar botones tal y como se ha descrito.
El segundo método para la creación de una barra de herramientas nueva es incluso más
fácil; simplemente, arrastre un comando fuera del diálogo Customize y suéltelo en cual-
quier área de la pantalla que no esté cubierta por una barra de herramientas. Para un
comando nuevo como WordUpperCase, seleccione Al1 Commands de la lista Category y
arrastre una entrada deseada fuera de la lista Commands. El procedimiento es similar al
modo en que colocamos WordUpperCase en un menú anteriormente. Seleccione un icono de
botón en el diálogo Button Appearance, añada texto apropiado para etiquetar el botón y haga
clic en OK. Visual C++ crea automáticamente una barra nueva para mantener el botón:
Para copiar uno de los comandos predefinidos del entorno, seleccione un nombre de
menú de la lista Category y arrastre un icono fuera del diálogo Customize al área en blanco
de la pantalla como se muestra en la Figura 13.3. Este método tiene la ventaja de copiar
una imagen de icono y texto de botón en un solo paso. Para visualizar tanto imagen como
texto en el botón nuevo de la barra de herramientas, haga clic en el botón derecho en la
barra de herramientas y elija Image And Text del menú contextual.
Es fácil eliminar un botón de cualquier barra de herramientas, tanto personalizado
como predefinido. Aunque el diálogo esté visible, arrastre un botón de la barra de herra-
mientas y suéltelo en una área en blanco de la pantalla. Al mismo tiempo, puede volver a
nombrar una barra de herramientas o cualquiera de las capturas de botón. Para modificar
una captura de botón, haga clic en el botón derecho del ratón sobre el botón en su barra de
herramientas, elija el comando Button Appearance del menú emergente y a continuación
vuelva a teclear la captura de botón en el cuadro de edición en la parte inferior del diálogo
Button Appearance. Puede cambiar un icono de botón en cualquier momento cortando y
pegando en el Portapapeles.
Figura 13.3. Creación de una nueva barra de herramientas personalizada.
Figura 13.5. Adición del comando nuevo MFC Tree List al menú Tools.
Figura 13.6. El nuevo comando MFC Tree List del menú Tools y la aplicación
MfcTree3.
Si desea que un programa de utilidad reciba los mismo argumentos de línea de coman-
do cada vez que se ejecuta, el segundo método para proporcionar argumentos demuestra
ser mucho más conveniente. Deseleccione el cuadro de comprobación Prompt For Argu-
ments y teclee los argumentos de línea de comando en el cuadro Arguments que se mues-
tra en la Figura 13.5. Por lo tanto, Visual C++ pasa los argumentos al programa sin solici-
tar nada.
Macros de argumento
Visual C++ proporciona una buena característica conocida como macros de argumento,
que puede facilitar mucho las especificaciones de argumento para programas Tools. Como
se describe en la Tabla 13.3, cada macro se amplía en una cadena que describe una carac-
terística del proyecto actual o archivo.
Un ejemplo simple ilustra la flexibilidad de las macros de argumento. Ponga por caso
que desea que la herramienta Bloc de Notas abra el documento que está actualmente activo
en el editor de texto. En lugar de solicitar cada vez el nombre de archivo, es mucho más
fácil utilizar la macro $(FilePath). Esta macro se amplía en toda la especificación de archi-
vo del documento que actualmente tiene la atención de entrada. Si ningún documento tiene
la atención, la macro genera una cadena vacía. Para utilizar la macro con la herramienta
Bloc de Notas, deseleccione el cuadro de comprobación Prompt For Arguments en el
diálogo Customize y teclee $(FilePath) en el cuadro Arguments como sigue:
$(FileDir) File Directory Directorio del archivo fuente de la ventana activa, es-
presado como d:ruta\.
$(FileExt) File Extension Extensión del nombre de archivo de la ventana activa.
$(FileName) File Name Nombre de archivo del archivo fuente de la ventana
activa.
$(FilePath) File Path Especificación completa del archivo fuente en la venta-
na activa, expresado como d:\rutabzombredearchiuo.
$(TargetArgs) Target Arguments Argumentos de línea de comando pasados a la aplica-
ción proyecto.
$(TargetDir) Target Directory Ruta al proyecto ejecutable contenido en el subdirecto-
rio Debug o Release, expresado como d:\ruta\.
$(TargetExt) Target Extension Extensión del nombre de archivo del proyecto ejecuta-
ble, tal como EXE o DLL.
$(TargetName) Target Name Nombre de archivo del proyecto ejecutable (normal-
mente el nombre del proyecto).
$(TargetPath) Target Path Especificación completa del proyecto ejecutable, ex-
presada como d:\ruta\nornbredearchivo.
$(WkspDir) Workspace Directory Directorio que contiene los archivos de proyecto, ex-
presados como d:\ruta\.
$(WkspName) Workspace Name Nombre del proyecto.
t
DWORD dwLowDateTiae
DWORD dwHighDateTime
>
Tool returned code O
4. Coloque el cursor en cualquier sitio sobre uno de los nombres de función del
documento y haga clic en el comando Win32 Structure del menú Tools. La Figu-
ra 13.9 muestra que el mensaje de utilidad es como la pestaña Win32 Structure de
la ventana Output.
(Continúa)
514 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
MACROS
Visual C++ incorpora un lenguaje de macros excelente en la forma de Visual Basic Script-
ing Edition, más conocida como VBScript. Almacenando guiones de macro en archivos,
PERSONALIZACIÓNDE VISUAL C++ 515
el entorno le permite hacer una colección permanente de macros útiles que se pueden
compartir con otros. Puede crear una macro grabando una secuencia de tareas o escribien-
do un guión programado. Y como veremos en esta sección, VBScript proporciona una
biblioteca de funciones que le permite ejecutar una macro para solicitar entrada de usuario,
visualizar cuadros de mensaje, llevar a cabo cálculos matemáticos, manipular cadenas y
llevar a cabo muchas otras tareas que están más allá de las habilidades de la interfaz de
usuario.
VBScript es un subconjunto reducido de Microsoft Visual Basic for Applications
(VBA), el lenguaje de programación para programas de Microsoft tales como Access.
Diseñado como un lenguaje de guiones para documentos Web escritos en Lenguaje de
marcas de hipertexto (HTML), VBScript proporciona un modo de que las páginas de
HTML incrusten controles ActiveX y otros objetos. Pero VBScript también funciona
como un lenguaje de guiones general que puede interpretar una lista de comandos específi-
cos para una aplicación y ejecutar los comandos a través de Automatización. En otras
palabras, VBScript puede servir como un lenguaje de macros. Es en este contexto donde
Visual C++ utiliza VBScript.
Una macro representa un conjunto de instrucciones reunidas en un solo comando. Las
macros escritas en VBScript son en todos los sentidos programas para los que el guión de
macro sirve como código fuente. Al ejecutar una macro, ejecuta todas las instrucciones
contenidas en el guión. Vimos en el Capítulo 3 cómo crear una macro de VBScript regis-
trando pulsaciones de tecla y clics de ratón. La grabación le da una macro que pone a
funcionar otra vez una secuencia de comandos grabados. Sólo necesita pasar manualmente
a través de los comandos una vez para grabar una macro. Por lo tanto, Developer Studio
duplica los mismos pasos de forma automática siempre que ejecute la macro registrada.
La macro de quitar tabuladores del Capítulo 3 se creó utilizando el comando Record
Quick Macro. Aunque conveniente, el comando Record Quick Macro crea sólo una macro
temporal que se pierde para siempre la próxima vez que invoque el comando y registre
otra macro. Por el contrario, el comando Macro del menú Tools le permite registrar ma-
cros permanentes, almacenando varias macros relacionadas en un solo archivo. Como
demostración, aquí tiene cómo crear una visión permanente de la macro de eliminar tabu-
ladores y mejorarla con una macro correspondiente de colocar tabuladores. Las macros
amplían la TabifySelection y los comandos UntabifySelection para colocar y eliminar
tabuladores en todo un documento, no sólo en el texto seleccionado. El primer paso es
crear un archivo de macro, a continuación añada guiones para las nuevas macros de colo-
car y eliminar tabuladores. Con un documento abierto en el editor de texto, elija el coman-
do Macro del menú Tools para abrir el diálogo Macro, haga clic en el botón Options y a
continuación haga clic en el botón New File. Introduzca un nombre de archivo y descrip-
ción para el archivo de macro, como se muestra a continuación:
516 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
1. En el menú Edit, haga clic en el comando Select Al1 para seleccionar el documen-
to entero.
2. Elija Advanced del menú Edit y haga clic en el comando Untabify Selection.
Haga clic en el botón Stop Recording en la barra de herramientas Record para terminar
la grabación, en ese momento el editor de texto abre automáticamente el nuevo archivo de
macro VBScript, llamado Tabs.dsm, en caso de que desee editar la macro (la extensión de
archivo significa la macro de Developer Studio). Para volver al documento de texto origi-
nal, haga clic en el nombre de archivo en el menú Window.
Cuando visualiza el diálogo Macro otra vez, el nombre de archivo Tabs aparece en el
cuadro de combinación Macro File. Para añadir una macro que coloque tabuladores, siga
los mismos pasos de antes, excepto que esta vez no haga clic en los botones Options y New
File, porque está añadiendo al archivo de macro, no creando uno nuevo. Simplemente
teclee TabifiAll para nombrar la segunda macro, haga clic en el botón Record, e introduz-
ca una descripción para la macro nueva, como por ejemplo Convertir espacios en tabula-
dores. A continuación siga los pasos anteriores para crear la segunda macro, esta vez
utilizando el comando Tabify Selection. El archivo de macro Tabs contiene ahora dos
macros, enumeradas en el diálogo Macro como UntabifyAll y TabifyAll. Para ejecutar una
macro, selecciónela de la lista en el diálogo y haga clic en el botón Run. El efecto es el
mismo que volver a teclear las pulsaciones de tecla grabadas manualmente.
Durante la grabación, Visual C++ compila una lista de cada comando que ejecuta y los
escribe en código VBScript al archivo DSM. Puede trabajar en cualquier editor mientras
que graba, e incluso cambiar de editor. Haciendo clic en el segundo botón en la barra de
herramientas Record detiene la grabación, permitiéndole realizar operaciones que no están
incluidas dentro de la macro terminada. Para reanudar la grabación, haga clic en el mismo
botón otra vez. Normalmente, antes de correr una macro, debería primero desplazar el
cursor a la ubicación del documento donde desea que las pulsaciones de tecla grabadas se
vuelvan a poner en funcionamiento, aunque este paso no es necesario para las macros
TabifyAll y UntabifyAll.
La mayor parte de las necesidades de su macro pueden completarse grabando una
secuencia de comandos de este modo. Pero puede que haya veces que desee que una macro
realice tareas que no se pueden grabar. Para crear semejante macro, debe escribir un guión
y guardarlo como un archivo DSM. La sección siguiente muestra cómo hacerlo.
Ejemplo: Una macro para búsqueda y reemplazo en columnas
Si prefiere teclear la macro usted mismo, empiece con el comando New y haga dos
veces clic en el icono MacroFile de la pestaña Files. Teclee el guión como se muestra en el
Listado 13.2, a continuación guarde el archivo como Replace.dsm en la carpeta Com-
mon\MsDev98\Macros. Developer Studio todavía no reconoce el archivo de macro nuevo,
así que debe seleccionar su cuadro de comprobación en el diálogo Customize como se
explica en el párrafo anterior.
La carpeta Capitulo. 13 del CD que se acompaña contiene un archivo de texto llamado
Columna.txt que proporciona una área de prueba simple para mostrar la macro Colum-
narReplace (puede utilizar cualquier documento de texto que desee para experimenta-
ción). Para cambiar una columna de palabras en el texto, seleccione un bloque de colum-
nas arrastrando el cursor del ratón como se muestra en la Figura 13.11 mientras que pulsa
la tecla ALT. Normalmente, puede marcar un bloque arrastrando en la dirección opuesta
desde el ángulo inferior derecho al ángulo superior izquierdo, pero la macro ColumnarRe-
place presupone que el cursor del ratón se desplaza de la parte superior izquierda a la
inferior derecha.
A continuación, haga clic en el comando Macro en el menú Tools, seleccione Replace
de la lista desplegable Macro File si es necesario y haga dos veces clic en ColumnarRepla-
ce en el cuadro que se muestra en la Figura 13.10. A medida que se ejecuta la macro,
consulta tanto la cadena de búsqueda como la cadena de reemplazo. Teclee palabra en la
primera consulta:
Haciendo dos veces clic en el icono, visualiza un mensaje de confirmación para termi-
nar la macro. Como la macro continúa ejecutándose mientras que se visualiza el mensaje,
debe responder al mensaje rápidamente.
Cada iteración del bucle principal en ColumnarReplace reemplaza texto en una línea
del bloque seleccionado. El bucle empieza en la línea superior ( y l ) del bloque y a conti-
nuación desciende a lo largo del documento una línea cada vez hasta que alcanza la línea
inferior (y2) del bloque:
Do While y1 <= y2
El resultado es una banda de texto seleccionado que abarca el bloque original, como se
muestra aquí:
Una vez que se ha seleccionado una banda en una línea, la macro invoca ReplaceText.
El método Developer Studio reemplaza cada ejemplo de cadena de búsqueda strFind con
la cadena de reemplazo strReplace. La búsqueda no es sensible a las mayúsculas, aunque
ColumnarReplace podría modificarse para ajustar búsquedas sensibles a las mayúsculas.
La clave de toda la macro es que ReplaceText actúa sólo en la banda de texto seleccionada,
no en la línea entera. Cuando se itera el bucle principal, el proceso se repite para la línea
siguiente a lo largo de la última fila del bloque de columna.
a J Y A T L COM AppW~zard
Cluster Resource Type Wizard
,!& Custom AppWrard
vos DSAddIn.cpp. El objeto Commands contiene todos los métodos que implementan
cualquier comando que el añadido proporciona. El objeto DSAddIn contiene dos métodos
llamados OnConnection y OnDisconnection.
Developer Studio invoca el primer método cuando carga (o «conecta») el añadido, e
invoca el segundo método cuando se descarga el añadido. Cuando el añadido inicia la
ejecución, el método OnConnection obtiene el control e invoca la función AddCommand
de Developer Studio. La invocación de AddCommand añade un comando nuevo al entorno
y proporciona toda la información sobre el comando que Developer Studio necesita pre-
sentar al usuario. El comando nuevo lo sirve la biblioteca de enlace dinámico añadido,
pero para el usuario aparece como cualquier otro comando en el conjunto de comandos
internos de Developer Studio. Los parámetros para AddCommand especifican información
tal como el nombre del comando, el texto que aparece en la barra de estado de Developer
Studio cuando el comando está seleccionado en un menú, texto para las herramientas de
consejo y botón de barra de herramientas de comando, y el nombre del método exportado
por la biblioteca de enlace dinámico añadido que Developer Studio debería invocar cuan-
do el usuario invoca el comando.
Developer Studio exporta otros dos métodos que le permiten al añadido hacer el co-
mando más inmediatamente accesible al usuario. AddCommandBarButton le enseña a De-
veloper Studio cómo crear un botón de barra de herramientas para un comando exportado
por el añadido. AddKeyBinding asigna una combinación de teclas a un comando. Aunque
crear una tecla para una pulsación de tecla le ahorra al usuario el problema de asignar una
tecla, la invocación de AddKeyBinding no se recomienda, porque corre el riesgo de anular
la pulsación de tecla existente, causando potencialmente una confusión al usuario. Gene-
ralmente es mejor dejar un comando de añadido no vinculado y dejar que el usuario asigne
una pulsación de tecla en el diálogo Customize después de que el añadido comience. Un
añadido no puede de ningún modo solicitar a Developer Studio una lista de combinaciones
de tecla actuales.
Developer Studio proporciona un conjunto de objetos que representan aspectos del
entorno, como por ejemplo información de instrucción y configuración de información,
apertura de documentos, el depurador, ventanas y mucho más (el Apéndice C explica
cómo las macros de Visual C++ pueden utilizar estos mismos objetos). A través de las
propiedades y métodos de un objeto, un añadido puede obtener o establecer información
detallada que pertenece al entorno. Por ejemplo, el objeto principal de Developer Studio
(llamado Application) contiene la colección extendida de propiedades y métodos enume-
rados en la Tabla 13.4 y en la Tabla 13.5. Puede familiarizarse con las cadenas de propie-
dad y otros valores visualizándolos en un guión de macro simple. Por ejemplo, ejecutando
esta línea en una macro:
Propiedad Descripción
Propiedad Descripción
Método Descripción
Tabla A.1. Conjunto de caracteres del ASCll Alto, valores entre 128 y 255
Dec Hex Car Dec Hex Car Dec Hex Car Dec Hex Car
los caracteres ASCII altos, los caracteres aparecen como algo distinto en el editor de textos
de Visual C++.
Técnicamente, cualquier archivo consiste en caracteres ASCII (o ANSII). Pero en los
formatos ASCII o ANSI, cada carácter se toma por su valor de apariencia. Z, é y '1,
significan «Z», «é» y «'/,». Los caracteres se representan sólo a sí mismos, sin códigos,
instrucciones ni ninguna otra cosa. La única excepción a esta regla son los caracteres de
tabulación y retorno de carro. Un tabulador es un carácter simple (valor ASCII 9) que
representa un número variable de espacios. La forma del carácter retorno de carro, que
marca el final de una línea de texto, depende del editor y del sistema operativo. En el
mundo del DOS y Windows, un retorno consiste en un par de caracteres con los valores
ASCII 10 y 13. El ASCII 13, llamado retorno de carro, señala el movimiento del cursor al
principio de la línea, mientras que el ASCII 10, llamado salto de línea, indica el movi-
miento hacia abajo a la siguiente fila. El orden de los caracteres es importante; el ASCII 13
debe preceder al ASCII 10, o el retorno no será reconocido con normalidad. En el sistema
operativo UNIX, el salto de línea sólo sirve como la marca de final de línea, que implica
tanto una nueva línea como un retorno de carro. El editor de textos de Visual C++ recono-
ce ambos estilos de retorno, tanto el par ASCII 13-10 como el solitario ASCII 10.
La Tabla A.2 enumera los 256 caracteres del conjunto ANSI de EE.UU., algunos de
los cuales no visualiza Windows. Estos caracteres ANSI no visualizables, que normalmen-
te aparecen en la pantalla como un cuadrado o espacio en blanco, aparecen el la Tabla A.2
como un cuadro genérico como este . La tabla enumera valores octales más que hexade-
cimales para cada carácter. Conocer el valor del carácter en base octal le permite incluir el
carácter en una cadena de recursos o en un cuadro de texto estático tecleando la barra
invertida seguida del valor octal del carácter. Por ejemplo, para incluir el carácter ANSI '1,
en una cadena, teclee \275. Observe que Windows puede visualizar algunos caracteres, tal
como el símbolo de marca registrada TM, solamente en fuentes TrueType.
Dec Octal Car Dec Octal Car Dec Octal Car Dec Octal Car
Dec Octal Car Dec Octal Car Dec Octal Car Dec Octal Car
204 314 i 217 331 U 230 346 ¿e 243 363 ó
205 315 Í 218 332 Ú 231 347 c 244 364 O
206 316 i 219 333 0 232 350 8 245 365 O
207 317 1 220 334 U 233 351 é 246 366 o
208 320 D 221 335 Y 234 352 2 247 367 +
209 321 Ñ 222 336 P 235 353 e 248 370 0
210 322 0 223 337 B 236 354 i 249 371 U
211 323 Ó 224 340 A 237 355 í 250 372 ú
212 324 0 225 341 á 238 356 i 251 373 U
213 325 0 226 342 2 239 357 i 252 374 ü
214 326 O 227 343 a 240 360 a 253 375 9
215 327 x 228 344 a 241 361 ñ 254 376
216 330 0 229 345 6 242 362 O 255 377 y
No todas las clase MFC soportan opciones de Automatización, en cuyo caso los bo-
tones radiales se desactivan en el cuadro de diálogo New Class. La columna Soporte de la
Tabla B-1 incluye un código A o 1 para indicar qué clases soportan Automatización o
identificación de tipos. El código D en la columna Soporte señala clases basadas en cua-
dros de diálogo, tal como CDialog, que requiere un recurso diálogo.
La primera mitad de este apéndice describe varios elementos del lenguaje VBScript,
tales como variables, sentencias de flujo de programa y procedimientos. Cada discusión se
ilustra con fragmentos de código comentado. Los comentarios en VBScript comienzan
con un carácter de cornilla simple y continúan hasta el final de la línea. VBScript también
reconoce la vieja sentencia Rem del lenguaje BASIC, pero Rem se utiliza rara vez:
Esto es un documento
Rem Y esto también
VARIABLES
VBScript reconoce un tipo de datos, llamado Variant, que contiene cualquier información
numérica o de texto, dependiendo del contexto. Si asigna datos numéricos a una variable
en VBScript, la variable toma un tipo de datos numérico adecuado al dato. La asignación
de texto a una variable Variant la cambia a cadena. VBScript proporciona varias fun-
ciones para convertir un tipo de datos interno en otro. La Tabla C. 1 enumera algunos de los
tipos de datos que Variant puede imitar.
Un nombre de variable debería dar una indicación de su subtipo interno. Utilice la
notación Húngara o una convención similar cuando dé nombre a una variable para indicar
el tipo de datos que contiene la variable. Nombres como bFlag, iNumber y strstring se
autodocumentan, haciendo fácil reconocer variables que contienen BOOL, int y datos
texto.
Dirn x
x = 3
y = x ' Esta sentencia es legal, incluso aunque no se haya declarado y
Para ayudar a capturar los errores tipográficos, puede exigir la declaración de variables
obligatoria en una macro mediante la inclusión de la sentencia Option Explicit. Realizada
esta sentencia, utilizar una variable es legal solamente si el guión ha declarado la variable
previamente con la sentencia Dim. Coloque la sentencia Option Explicit al principio del
archivo de macro como se muestra aquí:
Option Explicit
Dim x
x = 3 ' Legal
y = x ' No legal, por que y no se ha declarado con anterioridad
La palabra clave Const tiene el mismo efecto en VBScript que en el lenguaje C. Para
una variable dirigida a contener sólo datos incambiables, utilice Const en la asignación
inicial de la variable. Después de eso, el intérprete VBScript no permite otra asignación a
esa variable:
Const x = 3
x = 5 ' Esta sentencia provoca un error
Matrices
Una macro debe específicamente declarar una matriz utilizando la sentencia Dim:
Una matriz asignada sin una lista de subíndices es dinámica, lo que significa que la
macro puede redimensionar la matriz en tiempo de ejecución. Utilice la sentencia ReDim
para redimensionar una matriz dinámica, como esta:
ReDim puede redimensionar un array para hacerlo o bien más grande o bien más
pequeño. El array iArray del fragmento inicial tiene 101 elementos enteros, pero es con-
secutivamente reducido en tamaño hasta 51 elementos. La palabra clave Preserve asegura
que reducir el array mantiene los valores de los primeros 51 elementos, aunque los ele-
mentos iArray(51) hasta iArray(100) se pierden cuando el array es redimensionado. Sin la
palabra clave Preserve, redimensionar un array para hacerlo más grande o más pequeño
borra los contenidos originales. VBScript no impone un límite en el número de veces que
una macro puede redimensionar un array. Sin embargo, si el código declara un array con
un subguión en la sentencia Dim, el array no puede ser redimensionado con una sentencia
ReDim. El intento de hacerlo causa un error.
Cadenas
Asigne valores a variables de cadenas como lo haría en C, encerrando el texto entre comi-
llas dobles:
StrName = "Juan J. García"
OPERADORES
Todos los operadores de VBScript, excepto uno, se ajustan a tres categorías, llamadas
aritmética, comparación y lógica. Cuando operadores de categorías diferentes aparecen en
la misma expresión, los operadores aritméticos tienen la precedencia más alta, esto es,
VBScript evalúa los operadores aritméticos tal como suma y multiplicación antes que los
operadores de otras categorías. Los operadores de comparación son los siguientes, segui-
dos por los operadores lógicos. Como en el lenguaje C, los operadores entre paréntesis son
evaluados antes que los operadores fuera de los paréntesis, a pesar de la categoría. Estas
dos líneas demuestran cómo los paréntesis pueden cambiar el orden en el que VBScript
evalúa los operadores de una expresión:
posición del guión a otra. Existe un método llamado GoToLine, pero GoToLine sirve sólo
para mover el cursor de intercalación en un documento y no tiene nada que ver con el
control del flujo de programa en la ejecución de una macro.
Saltos condicionales
El grupo de sentencias If...Then...Else es funcionalmente equivalente a las sentencias C
if...else, excepto en que VBScript no utiliza llaves { } para encerrar bloques de código:
If ActiveDocument.Selection = " " Then str = "No selection"
Else str = ActiveDocument.Selection
Una condición que abarque dos o más líneas debe finalizar con la sentencia End If:
If x < y Then
iLine = ActiveDocurnent.Se1ection.CurrentLine
iCol = ActiveDocument.Se1ection.CurrentColumn
End if
Select Case 1
Case 1
' Viene aquí cuando i = 1
Case 2
' Viene aquí cuando i = 2
VBScript no proporciona una palabra clave break, por lo que cada sentencia Case
implica el final del bloque Case precedente. Después de que un bloque finalice, el flujo del
programa continúa en la siguiente sentencia que sigue a la sentencia End Select con la que
termina la sección Select Case. La sentencia Case Else realiza la misma función que la
palabra clave de C default, que marca un bloque de código que se ejecuta si el control no
salta a ninguna de las sentencias Case:
Select Case strColor
Case "red" strHiLite = "magenta"
Case "blue" strHiLite = "cyan"
Case "brown" strHiLite = "yellow"
Case Else strHiLite = "undefined"
End Select
VBScript reconoce varias construcciones de bucles que no ofrecen sorpresas al programa-
dor C/C++:
... ...
Do While Loop o Do Loop While. Itera mientras una condición es cierta.
Do Until...Loop o Do...Loop Until. Itera hasta que una condición sea cierta.
For...Next. Itera mientras lo indique la variable contador del bucle.
La palabra clave Do comienza un bloque repetitivo de código que finaliza con una
sentencia Loop. Las palabras clave While y Until pueden aparecer en cualquiera de las
dos, en la línea Do al principio del bloque o en la línea Loop al final del bloque, de-
pendiendo de si quiere que el intérprete de VBScript examine la condición antes o después
de ejecutar el bucle. Unos pocos fragmentos de código ilustran los fonnatos correctos de
bucles en VBScript:
Do While x < y
' No se entra en el bucle a no ser que x sea menor que y
LOOP
DO
u El bucle se ejecuta al menos una vez y se repite s61o si i
' es menor que j
Loop While i < j
Do Until x = 10
' No se entra en el bucle si x es igual a 10
' Cuando x alcanza un valor de 10, existe el bucle
LOOP
Do
' El bucle se ejecuta al menos una vez y se repite sólo
' hasta que i no es igual a j
Loop Until i <> j
For i = 1 To 10
Este bucle se itera 10 veces, incrementando i de 1 a 10
Next
...
Utilice la palabra clave Step en un bucle For Next para especificar un valor del
incremento diferente para el contador del bucle:
For i = 1 To 10 Step 2
' Este bucle itera cinco veces
Next
For i = 10 To 2 Ctep -2
Este bucle también itera cinco veces
Next
550 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
El primer bucle For...Next del fragmento itera cinco veces. Inicializa el contador del
bucle i con un valor de 1, luego lo incrementa en las sucesivas iteraciones a los valores 3,
5, 7 y 9. Durante la pasada final del bucle, i tiene un valor de 9; el bucle acaba cuando i
...
alcanza un valor de 11. El segundo bucle For Next también itera cinco veces, pero i se
decrementa en vez de incrementarse en las repeticiones del bucle, porque el valor Step es
negativo. Inicializado con un valor de 10, i se decrementa en cada iteración del bucle a los
valores 8, 6, 4 y 2. Cuando el bucle acaba, i tiene un valor de O.
Procedimientos
Además de su propia librería de funciones construidas, VBScript reconoce dos tipos de
procedimientos en un guión macro, etiquetadas Sub y Function. Ambos tipos aceptan
argumentos, pero solamente Function puede devolver un valor. Además de esto, hay poca
diferencia entre los dos tipos.
Cada guión macro tiene un procedimiento Sub, el nombre del cual determina el nom-
bre de la macro que aparece en el diálogo Macro de Visual C++. El procedimiento princi-
pal Sub aparece primero en el guión macro y no toma argumentos. Los procedimientos
que se pueden llamar desde el guión siguen al procedimiento principal en orden arbitrario.
Como se muestra aquí, cada procedimiento Sub (incluido el primero) finaliza con una
sentencia End Sub:
Sub MacroName () Procedimiento principal
End Sub
Un procedimiento Sub que se puede invocar, esto es, cualquier procedimiento Sub
salvo el primero, puede invocarse o bien a través de una sentencia Call o bien a través del
nombre de procedimiento como una sentencia de programa sola. Esto imita la forma en
que se invocan a las funciones en C/C++. El formato difiere ligeramente para los dos
métodos. La sentencia Call requiere los argumentos del procedimiento Sub sean ente-
rrados entre paréntesis después del nombre del procedimiento. Sin la sentencia Call, los
argumentos siguen al nombre del procedimiento separados por comas sin paréntesis. Estas
dos líneas tienen, de este modo, el mismo efecto:
Call AnySub( paraml, param2 )
AnySub paraml, param2
Como un procedimiento Function devuelve datos, puede servir como valor óptimo de
la misma forma que una función C. Para devolver un valor, un procedimiento Function
debe contener una variable que tiene el mismo nombre que el procedimiento mismo. El
intérprete devuelve el valor de esta variable al invocador cuando el procedimiento acaba.
Como todos los valores son del tipo Variant, el intérprete no realiza comprobación de
tipos para valores devueltos. A diferencia de los procedimientos Sub, la lista de argumen-
tos de un procedimiento Function siempre aparece encerrada entre paréntesis. Si un pro-
cedimiento Function no tiene argumentos, debe incluir un conjunto vacío de paréntesis.
Aquí tenemos un ejemplo simple que calcula el área de un círculo a partir de su radio:
Sub Main ( )
iRadius = InputBox("Introduzca el radio del círculo:" )
MsgBox( "El área es un cuadrado de " &Area( iRadius ) & " unidades" )
End Sub
Un procedimiento no puede mantener más de 127 variables; una matriz cuenta como
una variable simple.
OBJETOS
Varios aspectos de Developer Studio pueden aparecer en una macro en ejecución como
una colección de 17 objetos diferentes. Por ejemplo, el depurador puede representarse
como un objeto, como pueden ser los editores de Visual C++, las ventanas, y cosas así.
Cada objeto soporta propiedades y métodos a través de los cuales una macro conoce o
ajusta el estado actual del objeto. El principal objeto de Developer Studio se llama Ap-
plication, cuyas propiedades, métodos y eventos se enumeran en las Tablas 12.4, 12.5
y 12.6. Application es el objeto por defecto de un guión macro, por lo que puede utilizar
elementos de Application sin nombrar específicamente el nombre del objeto. Por ejemplo,
esta orden visualiza la configuración activa del proyecto actual, cualquier versión defi-
nitiva o de d6puración de Win32, sin referirse al objeto Application:
MsgBox( "La Configuración es " & Activeconfiguration )
Para darle una visión de los objetos, esta sección trata el objeto TextSelection, que
representa texto seleccionado en una ventana de documento abierto en el editor de textos.
Una macro determina el texto seleccionado en el documento activo utilizando la propiedad
ActiveDocument.Selection. Encontramos esta propiedad en la macro ColumnarReplace
del Capítulo 13 (véase el Listado 13.2, pág. 520). Las Tablas C.3 y C.4 enumeran las
propiedades y métodos de TextSelection, que una macro puede utilizar para manipular
texto seleccionado, mover el signo de intercalación, desplazar y realizar muchas otras
tareas. Cada propiedad y método debe aparecer en el guión macro adjuntado a la propie-
dad ActiveDocument.Selection con un operador de punto. Por ejemplo, esta línea utiliza el
método Copy para copiar texto seleccionado al Portapapeles:
Propiedad Descripción
Método Descripción
Replace Text Busca y reemplaza el texto de la selección. Para ver un ejemplo de cómo
se utiliza ReplaceText, véase la macro ColumnarReplace descrita en el
Capítulo 13.
Selecciona el documento completo.
Selecciona la línea que contiene el cursor.
Establece una marca sin nombre para la línea que contiene el cursor.
Formatea el texto seleccionado de acuerdo a la configuración de formateo
actual.
Mueve el cursor al comienzo del documento. Véase además EndOf-
Docurnent.
Mueve el cursor al comienzo de la línea actual. Véase además EndOfiine.
Inserta un tabulador a la selección. Para más información sobra la orden
Tabify de Developer Studio, véase la página 78 del Capítulo 3, «El editor
de texto».
Unindent Elimina un nivel de indentación de todas las líneas de una selección. Esto
tiene el mismo efecto que pulsar MAYÚS+TAB.
Untabify Elimina una tabulación a la selección.
WordLeft Mueve el cursor a la izquierda un número de palabras especificado.
WordRight Mueve el cursor a la derecha un número de palabras especificado.
Para obtener más información sobre el Script Debugger, diríjase al artículo de ayuda
en línea titulado script debugging. Puede además descargar una copia de la página Web de
VBScript de Microsoft citada al principio de este apéndice. El paquete Script Debugger
incluye documentación.
FUNCIONES DE LIBRER~A
El resto de este apéndice está dedicado a las descripciones de las funciones de la librería
VBScript disponibles para un guión macro de Visual C++. La Tabla C.5 organiza las
funciones de librería en varios grupos, permitiéndole determinar qué funciones pertenecen
a una necesidad particular de programación. La Tabla C.6 enumera las funciones en orden
alfabético y proporciona una breve descripción y fragmentos de códigos de ejemplo. Bus-
que la función que necesita en la Tabla C.5, y luego consulte la Tabla C.6 o la ayuda en
línea de Visual C++ para obtener una descripción de la función. Las funciones de librería
se contienen en el archivo VBScnpt.dl1, que está normalmente ubicado en la carpeta Sis-
tema o System32 de Windows.
Por convenio, los nombres de las funciones aparecen en un guión macro como una
combinación de mayúsculas y minúsculas. El intérprete VBScript no considera el caso, sin
embargo, y reconoce adecuadamente los nombres de las funciones, a pesar del caso. Un
prefijo «C» identifica las funciones de conversión, tal como las funciones CByte y CDate.
Asc Devuelve el código de carácter ANSI de la primera letra de una cadena. Una
función similar llamada AscB devuelve el primer byte de una cadena. La función
relacionada AscWdevuelve el byte y el código de carácter Unicode (amplio) del
primer carácter de una cadena, por ello se evita la conversión de Unicode a
ANSI.
Devuelve el arcotangente (en radianes) de un valor. El rango del resultado es
-pi/2 y pi12 radianes. Para convertir grados a radianes, multiplique el número de
grados por pi1180. Atn es la función inversa trigonométrica de Tan, que toma un
ángulo como argumento y devuelve el cociente de los dos lados de un triángulo
rectángulo.
Función Descripción
( Continúa)
558 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Función Descripción
Dateserial Devuelve una cadena que contiene un año, mes y día especificados. Dateserial
permite a la macro calcular una fecha absoluta a partir de una expansión serie de
tiempo. Por ejemplo, la siguiente invocación a DateSerial devuelve la fecha de
100 días a partir de la fecha actual:
iYear = DatePart( "m", Date )
iMonth = DatePart ( " m " , Date )
iDay = Datepartí "d", Date )
str = Dateserial( iYear, iMonth, iDay + 100 )
~sg~ox "100días
( apartirdeahoraserá " & s t r )
str(0) = "stringo"
str(1) = "stringl"
str(2) = "string2"
str(3) = 'string3"
x = Filter( str, "2" )
MsgBox( x(0) )
En este caso, intentar acceder a otro elemento tal como x(I) resulta erróneo.
Fix Devuelve la parte entera de un número en coma flotante. Tanto la función Int
como la función Fix truncan la parte decimal de un número y devuelven la parte
entera. Considere la variable x como ejemplo. Si x es positiva, ambas funciones
tienen el mismo efecto. Si x es negativa, Int devuelve el primer entero negativo
menor o igual a x, mientras que Fix devuelve el primer entero negativo mayor o
igual a x. Si x es -5,6, por ejemplo, Int devuelve -6 y Fix devuelve -5.
(Continúa)
560 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Función Descripción
-
FormatCurrency Devuelve una expresión con el formateo adecuado de moneda utilizando la con-
figuración regional establecida en el Panel de control del sistema.
FormatDateTime Devuelve una expresión formateada como una fecha u hora que esté conforme
con la configuración regional actual.
FormatNumber Devuelve un número expresado como una cadena. La cadena está formateada de
acuerdo a la configuración regional, por lo que, por ejemplo, los valores de
1.000 se formatean con comas en Estados Unidos y con puntos en Europa.
FormatPercent Devuelve una expresión formateada como un porcentaje (multiplicado por 100)
con un carácter % de final.
Hex Devuelve una cadena que representa un número en forma hexadecimal. Una
macro puede expresar un número hexadecimal añadiendo el prefijo &H al nú-
mero. Véase además la descripción de Oct.
Hour Devuelve un número entre O y 23, que representa la hora del instante dado. Entre
las 6:00 y las 7:00 de la tarde, por ejemplo, el siguiente fragmento devuelve el
número 18:
hr = Hour ( Time )
Msgbox( "La hora actual es " & hr )
LTrim Corta una cadena dejando espacios. Véase las descripciones de RTrim y Trim.
Mid Extrae una subcadena de una cadena dada.
Minute Devuelve un número entero entre O y 59 para el minuto de una hora dada. Para
las 6:47:53, por ejemplo, el siguiente fragmento devuelve el número 47:
Min = Minute( Time )
MsgBox( "El minuto actual es " & min )
mnth(l1) = "Diciembre"
m = Month( Date )
~ s g B o x ("El mes actual es " & mnthím-1) )
MonthName Devuelve una cadena que contiene el mes especificado por un número entre 1
y 12. Utilizando la función MonthName, el fragmento del ejemplo anterior pue-
de rescribirse de este modo:
m = Month( Date )
MsgBox( "El mes actual es " & MonthName(m) )
(Continúa)
562 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Función Descripción
MsgBox Visualiza un cuadro de mensaje estándar de Windows con los botones de las
opciones Aceptar, Cancelar, Abortar, Reintentar, Ignorar, Sí y No. MsgBox de-
vuelve uno de los siguientes valores para indicar qué botón utilizó el usuario
para cerrar el cuadro de mensaje:
vbOK 1 Aceptar
vbCancel 2 Cancelar
vbAbort 3 Abortar
vbRetry 4 Reintentar
vbIgnore 5 Ignorar
vbYes 6 Sí
vbNo 7 No
Función Descripción
StrComp Compara dos cadenas y devuelve un valor que indica si las cadenas difieren. El
uso y sintaxis de StrComp es similar a la función strcmp de la librería en tiempo
de ejecución de C:
If StrComp( stringl, string2 ) = O Then
MsgBox( 'Las cadenas son iguales" )
End If
Devuelve una cadena que contiene una hora formateada. Como la función Date-
Value, TimeValue reconoce varios formatos, dependiendo de la confinuración
regional. Para la configuración de España, por ejemplo, estas líneas devuelven
todas la cadena « 18:47:53»:
Función Descripción
Trim Reduce los espacios de cabecera y de final de una cadena. Véase también RTrim
y LTrim.
TypeName Toma una variable como parámetro y devuelve uno de los siguientes valores que
indican el subtipo de la variable:
Valor Byte
Valor Integer
Entero largo
Coma flotante de simple precisión
Coma flotante de doble precisión
Cadena de moneda
Valor decimal
Cadena de fecha y hora
Cadena de caracteres
Valor booleano
No inicializado
Datos no válidos
Devuelve el límite más grande disponible para la dimensión de una matriz. Por
ejemplo:
Dim A(100, 3, 4)
x = Ubound(A, 1) 'x = 99
y = Ubound(A, 2) 'y = 2
z = Ubound(A, 3) 'z = 3
Ucase Convierte todas las letras de una cadena a mayúsculas. Véase además la descrip-
ción de la función LCase.
VarType Toma una variable como parámetro y devuelve uno de los siguientes valores
enteros que indican el subtipo de la variable. Observe que esta función es similar
a la función TypeName.
( Continúa)
566 MICROSOFT VISUAL C++ 6.0. MANUAL DEL PROGRAMADOR
Función Descripción
vbEmpty O No inicializada
vbNull 1 Datos no válidos
vbInteger 2 Entero
vbLong 3 Entero largo
vbSingle 4 Coma flotante de simple precisión
vbDouble 5 Coma flotante de doble precisión
vbcurrency 6 Cadena de moneda
vbDate 7 Cadena de fecha y hora
vbString 8 Cadena de caracteres
vbBoolean 11 Valor booleano
vbvariant 12 Una matriz de tipo Variant
vbByte 17 Valor byte
vbArray 8192 Matriz. La función VarType devuelve la
suma del valor de la matriz (8192) más
el valor del subtipo de la matriz que Ile-
na la matriz.
Weekday Devuelve un entero entre 1 y 7, que representa el día de la semana, que comien-
za con el 1 para el domingo. Véase el fragmento de ejemplo de la función Week-
DayName.
WeekDayName Devuelve la cadena que indica el día especificado de la semana:
D = Weekday( Date )
MsgBox( "Hoy es " & WeekdayName(d1 )
Year Extrae el año de una fecha dada y lo devuelve como un valor entero.
M S ~ B O X ("El año es " & Year ( Date ) 1