Está en la página 1de 17

El API win32

23 de febrero de 2004 Indice


1. Introduccin o 2. Ejemplos de uso del API 2.1. CreateProcess . . . . . . . . . . . . . 2.2. Variables de entorno . . . . . . . . . 2.3. Pipes . . . . . . . . . . . . . . . . . . 2.4. Manejo estructurado de Excepciones 2.5. Manejo de cheros . . . . . . . . . . 2.6. Directorios . . . . . . . . . . . . . . 2.7. Seales de consola . . . . . . . . . . n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 5 6 8 10 13 15

1.

Introduccin o

Este documento describe la API de programacin de win32. Esta API es comn (con ciertas difeo u rencias m nimas) en todos los sistemas operativos Windows de 32 bits (Windows 98/NT/2000/XP), e incluye un gran nmero de funciones, desde manejo de cheros y procesos hasta funciones grcas. u a En este documento no podemos tratar todas esas funciones, y nos centramos en las funciones ms ina teresantes de programacin a nivel de sistema (manejo de cheros y directorios, procesos, variables de o entorno, tuber entre procesos, manejo de excepciones, etc.) as Hay una serie de consideraciones generales acerca de la programacin con la API de win32 que o conviene puntualizar: Todo programa Windows normalmente incluye el chero de cabecera ((windows.h)). Los programas win32 usan su propio sistema de tipos para ser portable entre distintos sistemas Windows y tambin para permitir una mejor programacin de bajo nivel. As por ejemplo: e o Casi cualquier recurso del sistema representa a un objeto del ncleo, y se maneja con el tipo u genrico HANDLE. e El tipo DWORD especica una variable que puede guardar una doble palabra de 16 bits 1 (32 bits), el tipo BOOL, tambin de 32 bits representa valores booleanos 2 , el tipo LPSTR un e puntero a una cadena de caracteres, LPCSTR un puntero a una cadena de caracteres constante, etc.
1 Se

considera que una palabra es de 16 bits aunque los procesadores actuales utilizan palabras de al menos 32 bits por cuestiones de compatibilidad con versiones anteriores. 2 Se puede pensar que utilizar un valor de 32 bits para representar slamente un bit es un desperdicio. Sin embargo, o actualmente los ordenadores son capaces de manejar mucho ms rpido un valor de 32 o 64 bits (gracias a la alineacin a a o de memoria) que una parte de 1 bit de un registro, con lo que se gana en velocidad. Adems, la cantidad de memoria a normalmente ya no representa el principal cuello de botella de los programas.

Normalmente se utiliza la llamada notacin polaca inversa para nombrar las variables. As por o ejemplo, un HANDLE para un chero se nombrar como hFichero, un puntero a una cadena a ASCIIZ, como lpszNombre (long pointer to String-z), etc. Aunque win32 tambin permite cierto tipo de seales (muy sencillas y orientadas a la consola, e n como se ver), Windows introduce el concepto de excepcin. Las excepciones permiten un trataa o miento estructurado de los errores en puntos comunes que adems permiten liberar los recursos a temporalmente utilizados que ya no son vlidos debido al error. Veremos ejemplos de este tipo de a tratamiento de errores en los programas de la siguiente seccin. o Para el manejo de memoria seguiremos utilizando las funciones estndar de C (malloc()/free()) a en vez de las ofrecidas por la API win32. Esto se debe a que su uso ya es conocido de la prctica a de Linux, adems de que estas funciones pertenecen al estndar de C y por lo tanto cualquier a a compilador debe incluirlas. Windows permite trabajar con cadenas en formato UNICODE o en ASCII. En la primera opcin o se soportan la mayor de los lenguajes del mundo, incluyendo chino, hind, japons, etc., y los a u e caracteres ya no ocupan un slo byte, sino varios (2 o 4). La API win32 ofrece macros que permiten o hacer programas que usan indistintamente ambas codicaciones. Sin embargo en nuestros ejemplos usaremos slo ASCII por ser ms sencillo (cada carcter ocupa un byte) y por continuidad con o a a las prcticas anteriores. a El Registro de Windows es una base de datos que relaciona cadenas de caracteres (claves) con valores (que pueden ser numricos, cadenas de caracteres, etc.). Es parecido a un conjunto de e variables de entorno organizadas jerrquicamente. Mientras que las variables de entorno son vlia a das para una sesin, el Registro es persistente. Los programas de Windows y el propio sistema o operativo utilizan el Registro para almacenar datos estructurados de forma persistente. La API win32 ofrece funciones para acceder al registro similares al acceso a las variables de entorno que veremos despus. Debido a su tamao y complejidad, el tratamiento de los valores contenidos en e n el Registro queda fuera de este documento.

2.

Ejemplos de uso del API

En esta seccin se muestra el uso de la API win32 a travs de ejemplos de programas funcionales que o e realizan ciertas tareas sencillas. Estos programas estn disponibles en la pgina Web de la asignatura a a como un Workspace de Microsoft Visual C++. Cada programa ilustra un conjunto de funciones de la API para manejar procesos, variables de entorno, cheros, directorios, etc.

2.1.

CreateProcess

En primer lugar veremos la creacin de procesos. A diferencia de UNIX, Windows no mantiene una o estructura jerrquica de procesos ni sigue el esquema fork()/exec(). Al contrario, un proceso puede a crear directamente a otro, especicando si quiere que se hereden los cheros abiertos, etc. La funcin que nos permite crear procesos es CreateProcess, que se muestra a continuacin extra o o da directamente de la denicin de los archivos de cabecera de Windows: o BOOL WINAPI CreateProcess( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,

DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); De los parmetros de la funcin, los siguientes son los ms importantes (el resto normalmente ir a a o a a NULL o 0 dependiendo de su tipo): lpCommandLine Especica la l nea de ordenes a ejecutar. Normalmente el parmetro anterior, a lpApplicationName, se deja a NULL. bInheritHandles Especica si se deben heredar los cheros abiertos. lpStartupInfo Puntero a una estructura STARTUPINFO: typedef struct _STARTUPINFO { DWORD cb; LPSTR lpReserved; LPSTR lpDesktop; LPSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; Por ahora slo nos interesan de esta estructura los ultimos tres campos, que especican los HANDLEs o para los cheros de entrada estndar, salida estndar y de error. En el ejemplo se ver cmo a a a o se puede hacer que un proceso herede los HANDLEs de cheros que se necesiten para hacer una redireccin. o lpProcessInformation Puntero a una estructura PROCESS INFORMATION: typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

Esta estructura es muy interesante, porque una vez lanzado el proceso, el programa que lo cre pueo de obtener datos como: el HANDLE al proceso, el HANDLE al hilo3 , y los identicadores de ambos (equivalentes a los PIDs, Process IDs, en UNIX). Un proceso puede obtener su propio HANDLE llamando a la funcin GetCurrentProcess y puede o obtener su identicador llamando a GetCurrentProcessId. Finalmente, un proceso puede terminar usando la llamada ExitProcess, que acepta un entero con el valor del error (equivalente a la funcin exit() de UNIX). o El siguiente ejemplo muestra el uso de CreateProcess: Ejecuta el programa (junto con los parmea tros) que se le pasa por la l nea de ordenes.
#include <stdio.h> #include <windows.h> int main(int argc, char*argv[ ]) { char* cmdline; STARTUPINFO si; PROCESS INFORMATION pi; int i, len; /* Construir la l nea de comando a parir de los parmetros */ a len = 0; cmdline = malloc(1); cmdline[0] = \0; for (i=1 ; i < argc; i++) { len += 2 + strlen(argv[i]); cmdline = realloc(cmdline, len); strcat(cmdline, argv[i]); strcat(cmdline, " "); } printf("cmdline: \" %s\"\n", cmdline);
25

10

15

20

ZeroMemory( &si, sizeof (si) ); si.cb = sizeof (si); ZeroMemory( &pi, sizeof (pi) ); /* Creamos el proceso hijo. */ if (!CreateProcess(NULL, /* No nombre de modulo (usar linea de ordenes). */ cmdline, /* Linea de ordenas a ejecutar. */ NULL, /* El manejador de proceso no se hereda. */ NULL, /* El manejador de hilo no se hereda. */ FALSE, /* Los manejadores no se heredan. */ 0, /* El proceso se crea sin ninguna opcion especial. */ NULL, /* Heredamos el bloque de entorno del padre. */ NULL, /* Usamos el directorio de inicio del padre. */ &si, /* Puntero a estructura STARTUPINFO. */ &pi)) /* Puntero a estructura PROCESS INFORMATION. */ { perror ("Imposible crear proceso"); return 1; }
30

35

40

45

printf("[Identificador de proceso: %d]\n",pi.dwProcessId); /* Si la orden se ejecuta en primer plano, debemos esperar a que termine.*/ WaitForSingleObject(pi.hProcess, INFINITE);
3 En

50

win32 cada proceso ejecuta al menos un hilo (thread).

CloseHandle(pi.hProcess); CloseHandle(pi.hThread); free(cmdline); return 0; } /* $Id: CreateProcess.c,v 1.2 2004/02/11 19:52:40 dsevilla Exp $ */
60 55

La explicacin del programa es como sigue: o L neas 1222: Construye una l nea de ordenes en cmdline a partir de las partes obtenidas en los distintos argv. L nea 31: Llama a CreateProcess con los parmetros tal y como se han explicado ms anteriora a mente. L nea 47: Se utiliza la variable dwProcessID de la estructura PROCESS INFORMATION para imprimir el identicador del proceso creado. L nea 50: La llamada WaitForSingleObject se utiliza para esperar a que el nuevo proceso termine. L nea 5354: Se liberan los HANDLEs del proceso y del hilo.

2.2.

Variables de entorno

El mecanismo y uso de las variables de entorno es muy similar al presente en UNIX, salvo que ahora las funciones para manejarlo cambian. As la funcin para obtener una variable de entorno es , o GetEnvironmentVariable: DWORD WINAPI GetEnvironmentVariable( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize ); En lpName se especica el nombre de la variable requerida. En lpBuffer se ofrece espacio para que la funcin devuelva la cadena. En nSize se especica el tamao de memoria reservado en lpBuffer; o n esto se hace para que la funcin sepa si tiene espacio suciente en el buer para meter el valor de la o variable. Si no es as devuelve el nmero de bytes necesarios para albergar el valor de la variable. , u Por su parte, la funcin SetEnvironmentVariable sirve para establecer el valor de una variable de o entorno: BOOL WINAPI SetEnvironmentVariable( LPCSTR lpName, LPCSTR lpValue ); Los parmetros son claros, salvo que ahora ambos son punteros a cadenas constantes. a Ejecutando ((set)) en un ((S mbolo del Sistema)) de Windows se pueden ver ciertas variables de entorno que son interesantes para la realizacin de la prctica. Algunas de las ms importantes: o a a

PATH Equivalente a la que se encuentra en UNIX, salvo que en este caso se separa cada uno de los directorios con ((;)). USERNAME Guarda el nombre de usuario cuya sesin se est ejecutando. o a HOMEPATH Directorio ra de los datos del usuario actual. z USERPROFILE Guarda el directorio base del perl de usuario. Este directorio se usar de base para a almacenar directorios como ((Mis Documentos)), etc. Suele coincidir con HOMEPATH.

2.3.

Pipes

win32 tambin permite establecer tuber (pipes) entre procesos. El mecanismo es muy parecido al e as de UNIX, con la salvedad de las diferencias en la creacin de procesos que se vieron anteriormente. La o creacin de tuber entre procesos es posible gracias al mecanismo introducido antes de ((herencia de o as handles)). A continuacin se muestra un programa que ejecuta dos ordenes conectadas por una tuber o a: ((cmd /c dir \ /S)) y ((more)). Como en Windows la orden dir es una orden interna, se debe llamar expl citamente al intrprete de ordenes (cmd.exe) para ejecutar la orden a travs del parmetro /c. La e e a orden lista todos los directorios a partir del directorio ra del disco actual. z
#include <stdio.h> #include <windows.h> int main(int argc, char*argv[ ]) { /* Ambas ordenes: dir \ /S | more */ char* cmd1 = "cmd /c dir \\ /S"; char* cmd2 = "more.com"; HANDLE hReadPipe, hWritePipe; SECURITY ATTRIBUTES PipeSA = /* Para los handlers que se heredan */ { sizeof (SECURITY ATTRIBUTES), NULL, TRUE }; STARTUPINFO si1, si2; PROCESS INFORMATION pi1, pi2; GetStartupInfo( &si1 ); GetStartupInfo( &si2 ); ZeroMemory( &pi1, sizeof (pi1) ); ZeroMemory( &pi2, sizeof (pi2) ); /* Creacin de la Pipe */ o CreatePipe( &hReadPipe, &hWritePipe, &PipeSA, 0); /* Establecer los valores para el primer proceso */ si1.hStdInput = GetStdHandle(STD INPUT HANDLE); si1.hStdError = GetStdHandle(STD ERROR HANDLE); si1.hStdOutput = hWritePipe; si1.dwFlags = STARTF USESTDHANDLES; /* Creamos el proceso hijo. */ if (!CreateProcess(NULL, /* No nombre de modulo (usar linea de ordenes). */ cmd1, /* Linea de ordenas a ejecutar. */ NULL, /* El manejador de proceso no se hereda. */ NULL, /* El manejador de hilo no se hereda. */ TRUE, /* Los manejadores S se heredan. */ I 0, /* El proceso se crea sin ninguna opcion especial. */ NULL, /* Heredamos el bloque de entorno del padre. */ NULL, /* Usamos el directorio de inicio del padre. */ &si1, /* Puntero a estructura STARTUPINFO. */ &pi1)) /* Puntero a estructura PROCESS INFORMATION. */ {
25

10

15

20

30

35

40

perror ("Imposible crear primer proceso"); return 1; }


45

/* Ya no estamos interesados en la escritura de la tuber */ a CloseHandle(hWritePipe); CloseHandle(pi1.hThread); /* Establecer los valores para el primer proceso */ si2.hStdInput = hReadPipe; si2.hStdError = GetStdHandle(STD ERROR HANDLE); si2.hStdOutput = GetStdHandle(STD OUTPUT HANDLE); si2.dwFlags = STARTF USESTDHANDLES; /* Creamos el proceso hijo. */ if (!CreateProcess(NULL, /* No nombre de modulo (usar linea de ordenes). */ cmd2, /* Linea de ordenas a ejecutar. */ NULL, /* El manejador de proceso no se hereda. */ NULL, /* El manejador de hilo no se hereda. */ TRUE, /* Los manejadores S se heredan. */ I 0, /* El proceso se crea sin ninguna opcion especial. */ NULL, /* Heredamos el bloque de entorno del padre. */ NULL, /* Usamos el directorio de inicio del padre. */ &si2, /* Puntero a estructura STARTUPINFO. */ &pi2)) /* Puntero a estructura PROCESS INFORMATION. */ { perror ("Imposible crear segundo proceso"); return 1; } /* No estamos interesados en la lectura de la tuber */ a CloseHandle(hReadPipe); CloseHandle(pi2.hThread); /* Esperar a ambos procesos */ WaitForSingleObject(pi1.hProcess, INFINITE); CloseHandle(pi1.hProcess); WaitForSingleObject(pi2.hProcess, INFINITE); CloseHandle(pi2.hProcess); return 0; } /* $Id: pipe.c,v 1.4 2004/02/23 09:40:48 dsevilla Exp $ */

50

55

60

65

70

75

80

85

La explicacin del programa es como sigue: o L nea 11: Se denen ambos HANDLEs para la lectura y la escritura de la tuber a. L nea 1213: Se rellena una estructura SECURITY ATTRIBUTES con los datos que se muestran. Esta estructura es necesaria para que los HANDLEs se puedan heredar entre procesos. L nea 23: Se crea la tuber La funcin tiene la siguiente denicin: a. o o BOOL WINAPI CreatePipe( PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize

); El ultimo parmetro especica el tamao del buer interno. Si su valor es 0, se utilizar el valor a n a por defecto. La funcin acepta punteros a ambos HANDLEs, que se establecern como la lectura y o a la escritura de la tuber respectivamente, as como un puntero a una estructura de seguridad a, SECURITY ATTRIBUTES: typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; En nuestro caso, el descriptor no se utiliza. La ultima variable hInheritHandle est puesta a un a valor TRUE (l nea 13). L neas 2629: La estructura STARTUP INFO del primer proceso se rellena estableciendo los HANDLEs espec cos. As la salida estndar se conecta con la escritura en la tuber Una cosa , a a. importante es que para que realmente los cheros se hereden, la variable dwFlags se debe poner a STARTF USESTDHANDLES. L neas 4950: Se cierra el HANDLE de escritura en la tuber y el del hilo. Ya no se van a necesitar a ms. a L neas 5276: Se realiza el proceso anlogo para el otro proceso. Esta vez se utiliza hReadPipe. a L neas 79nal: Se espera a ambos procesos con la llamada WaitForSingleObject. La ejecucin de este programa muestra el resultado esperado: un listado de todos los cheros y o directorios recursivamente, paginados a travs de la orden more. e

2.4.

Manejo estructurado de Excepciones

La API win32 incluye macros y funciones que permiten trabajar tratando los errores como excepciones. Las excepciones son ms convenientes que las meras comprobaciones de error porque bsicamente a a permiten dos cosas: Tener localizados en un unico punto todos los manejadores de errores de un cdigo, pudiendo o separar cada manejador para cada tipo de error, y la posibilidad de ejecutar un cdigo de nalizacin independientemente de que se haya producido o o una excepcin. Este cdigo de nalizacin es el encargado de liberar la memoria y otros recursos o o o que ya no se necesitan debido al error. As es una ayuda al programador, evitando que ste olvide , e liberar recursos ante la aparicin de un error. o La sintaxis del manejo de excepciones es la siguiente: Un bloque precedido de try en el que se ejecuta cierto cdigo que puede lanzar excepciones, y una parte de tratamiento de excepciones precedida o por except: __try{ ... /* Cdigo que puede generar una excepcin (fallo) */ o o } __except (...) { ... /* Cdigo de tratamiento de la excepcin */ o o }

Alternativamente, en vez de except, el bloque try puede terminar con un ciones del finally siempre se ejecutarn, se produzcan o no excepciones: a __try{ ... /* Cdigo que puede generar una excepcin (fallo) */ o o } __finally { ... /* Cdigo de finalizacin (siempre se ejecuta) */ o o }

finally. Las instruc-

El siguiente ejemplo muestra un uso sencillo del mecanismo estructurado de excepciones. Los siguientes programas introducen tcnicas algo ms sosticadas que explicaremos despus. El programa e a e utiliza una variable sin inicializar (p) para albergar el resultado de una copia de una cadena. Como es normal, este programa causar una excepcin de violacin de segmento. a o o
#include <windows.h> #include <stdio.h> int main(int argc, char*argv[ ]) { char *p = NULL; char *q = "Hola"; try { /* Realizar algn procesamiento */ u /* Desastre! p es nulo! */ strcpy(p,q); } except (EXCEPTION EXECUTE HANDLER) { /* Obtener el cdigo de excepcin */ o o DWORD ex = GetExceptionCode(); if (ex == EXCEPTION ACCESS VIOLATION) { printf("Violacin de segmento.\n"); o } } printf("Finalizando el programa.\n");
30

10

15

20

25

return 0; } /* $Id: exception.c,v 1.3 2004/02/19 01:36:04 dsevilla Exp $ */

L nea 10: El bloque try comienza. L nea 15: El programa realiza un acceso no vlido. Inmediatamente salta a la parte except. a Existen varios mecanismos para ltrar excepciones, pero por ahora slo nos interesa el valor o EXCEPTION EXECUTE HANDLER, que hace que se ejecute lo que hay dentro del bucle de except. L nea 21: Se utiliza la funcin GetExceptionCode para averiguar de qu excepcin se trata o e o concretamente. Existen varias excepciones que se pueden tratar. Algunas de las ms importantes a son (aparte de la ya vista EXCEPTION_ACCESS_VIOLATION): EXCEPTION_DATATYPE_MISALIGNMENT Un acceso a memoria no alineado (en arquitecturas que fuerzan a que todos los accesos estn alineados). e

EXCEPTION_FLT_DIVIDE_BY_ZERO Error de divisin por cero (en punto otante). o EXCEPTION_ILLEGAL_INSTRUCTION Se ha ejecutado una instruccin invlida. o a EXCEPTION_STACK_OVERFLOW Desbordamiento de pila. EXCEPTION_INVALID_HANDLE Se us un HANDLE invlido en alguna operacin de cheros. o a o etc. L nea 23: En el caso de una violacin de segmento, imprimir un mensaje. El programa naliza o normalmente siendo consciente de su error.

2.5.

Manejo de cheros

El manejo de cheros en win32 es muy similar al de UNIX, salvo el nombre de las funciones. La losof es la misma. El siguiente ejemplo muestra una copia de cheros. Adems, se introduce un a a mecanismo ms sosticado de tratamiento de errores como excepciones, que se explicar a continuacin: a a o
/* Copia argv[1] en argv[2]. El primero debe existir y el segundo no */ #include <windows.h> #include <stdio.h> /* Para printf */ /* Excepciones denidas por el usuario */ #dene EXC MEM (1 << 0) #dene EXC DISK (1 << 1) /* Leer el chero en bloques de 8K */ #dene BLOQUE 8192 /* Funcin para lanzar una excepcin del usuario */ o o void ReportException(LPCSTR mensaje, DWORD dwCodigo) { /* Imprimir el error */ perror(mensaje); /* Lanzar la excepcin */ o if (dwCodigo != 0) RaiseException ((0x0FFFFFFF & dwCodigo) | 0xE0000000, 0, 0, NULL); } int main(int argc, char*argv[ ]) { HANDLE inF = INVALID HANDLE VALUE, outF = INVALID HANDLE VALUE; char* pBuer = NULL; DWORD bytes; BOOL nuevo = FALSE; /* El chero destino exist */ a? try { /* Tratamiento de excepciones */ try { /* Limpiar handlers y buers */ /* Reservar memoria para el buer */ pBuer = (char*)malloc(BLOQUE); if (pBuer == NULL) ReportException("No hay suficiente memoria", EXC MEM); /* Abrir el chero de lectura */ inF = CreateFile(argv[1], o GENERIC READ /* Slo lectura */, 0 /* No se comparte */, NULL /* No se especica seguridad adicional */, o OPEN EXISTING /* Abrir slo si existe */, FILE ATTRIBUTE NORMAL, NULL /* No se utiliza ningn otro chero de ejemplo */); u
5

10

15

20

25

30

35

40

45

10

if (inF == INVALID HANDLE VALUE) ReportException("No se puede abrir el fichero de entrada", EXC DISK);
50

/* Abrir el chero de escritura */ outF = CreateFile(argv[2], GENERIC WRITE /* Slo escritura */, o 0 /* No se comparte */, NULL /* No se especica seguridad adicional */, CREATE NEW /* Crear slo si no existe */, o FILE ATTRIBUTE NORMAL, NULL /* No se utiliza ningn otro chero de ejemplo */); u if (outF == INVALID HANDLE VALUE) ReportException("No se puede abrir el fichero de salida", EXC DISK); /* El chero se ha creado */ nuevo = TRUE;

55

60

65

/* Bucle de copia */ bytes = BLOQUE; do { DWORD bytesEscritos; if (!ReadFile(inF, pBuer, bytes, &bytes, NULL)) ReportException("Error de lectura", EXC DISK); if (!WriteFile(outF, pBuer, bytes, &bytesEscritos, NULL) | | (bytesEscritos != bytes)) ReportException("Error de escritura", EXC DISK); } while (bytes == BLOQUE); } nally { /* Eliminar memoria y handlers */ if (pBuer != NULL) free(pBuer); pBuer = NULL; if (inF != INVALID HANDLE VALUE) CloseHandle(inF); inF = INVALID HANDLE VALUE; if (outF != INVALID HANDLE VALUE) CloseHandle(outF); outF = INVALID HANDLE VALUE;

70

75

80

85

} except (EXCEPTION EXECUTE HANDLER) { /* Borrar el chero creado */ perror("Error procesando ficheros"); if (!nuevo) DeleteFile (argv[2]); }

90

95

printf("Programa finalizado.\n"); return 0; } /* $Id: copyle.c,v 1.6 2004/02/23 17:54:57 dsevilla Exp $ */
100

L neas 56: Se denen las constantes EXC MEM y EXC DISK para identicar el tipo de error que se ha producido en nuestro programa. L neas 1321: Se declara la funcin ReportException, que imprime un mensaje de error utilio zando perror() y que adems, si el error es serio (dwCodigo != 0) lanza una excepcin utilizando a o la llamada win32 RaiseException. Esta funcin es util para lanzar excepciones en el momento o

11

que se produzcan errores inesperados en nuestra aplicacin, como veremos ms adelante. El valor o a 0xE0000000 es usado para especicar que la excepcin es realmente un error grave. o Lineas 31 y 32: Comienzan dos bloques try; esta construccin se realiza para que el bloque try o ms interno pueda tener un bloque finally (l a nea 80) que permita liberar los recursos utilizados (ya se produzca error o no), mientras que el try externo es el que trata las correspondientes excepciones generadas con except (l nea 90). L nea 37: Ejemplo de uso de la funcin ReportException. o L nea 40: Se abre el chero de lectura. Ntese que aunque no se est creando ningn chero, la o a u funcin que se debe utilizar es CreateFile, con los parmetros de abrir para slo lectura: o a o HANDLE WINAPI CreateFile( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); Los argumentos son como siguen: lpFileName El nombre del chero a abrir o crear. dwDesiredAccess Puede ser bien GENERIC READ, GENERIC WRITE o ambos unidos por un ((or)) (|). dwShareMode Por ahora siempre estar a 0. Especica qu grado de comparticin tiene ese chero a e o con el resto de aplicaciones que quieran abrirlo. Por ahora lo abrimos exclusivamente. lpSecurityAttributes Atributos de seguridad. Por ahora los dejamos a NULL. dwCreationDisposition Especica si se quiere crear el chero o si por el contrario se quiere abrir un chero que ya existe. Los posibles valores son: CREATE NEW, CREATE ALWAYS, OPEN EXISTING, OPEN ALWAYS, TRUNCATE EXISTING. El hecho de especicar que se quiere abrir un chero que ya exista hace que el sistema falle si no existe el chero, como se ve en el cdigo (l o nea 46). dwFlagsAndAttributes Especica los atributos del chero. Existen varios, pero los ms importantes son: FILE ATRIBUTE NORMAL (atributos normales del chero) y a o FILE ATTRIBUTE READONLY (para crear archivos de slo lectura). Como se ve, la funcin devuelve un HANDLE, que puede ser vlido o no, indicando si se ha podido o a abrir o no el chero. Para saberlo se comprueba con INVALID HANDLE VALUE. L nea 52: Se crea el archivo destino. Ntese los valores adecuados de los campos de la funcin o o CreateFile. L neas 6778: Bucle de copia. Las funciones utilizadas son ReadFile y WriteFile, que son anlogas a read() y write() de UNIX. La denicin de ReadFile es como sigue: a o BOOL WINAPI ReadFile(

12

HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ); Como se ve, acepta un HANDLE, un buer y el nmero de bytes a leer. En lpNumberOfBytesRead u devuelve el nmero de bytes le u dos. El ultimo parmetro se deja a NULL. La funcin devuelve TRUE a o si todo ha ido bien, y FALSE en otro caso, lo cual servir para lanzar una excepcin (l a o neas 73 y 77). L neas 8089: Se utiliza el bloque finally para liberar todos los recursos utilizados por el sistema (cheros, memoria, etc.). Ntese que antes no se han cerrado los HANDLEs, sino que siempre o se cierran en el bloque finally. L neas 9096: Finalmente, el bloque except se utiliza para borrar el chero destino (si existe) en el caso de que se haya producido alguna excepcin. o Como se puede ver, esta forma de tratar las excepciones en un unico punto evita el dejar recursos sin liberar (memoria reservada, cheros abiertos, etc.), que pueden nalmente hacer que el sistema se quede sin recursos. Por ultimo, decir que en la API win32 hay una funcin espec o ca para copiar cheros (CopyFile y CopyFileEx), aunque aqu se ha implementado con operaciones de bajo nivel para demostrar su uso.

2.6.

Directorios

A continuacin se muestra el manejo de los directorios desde el punto de vista de la API de win32. o El esquema que sigue el listado de directorios es similar al utilizado en MS-DOS y en UNIX de ((buscar el primero y buscar el siguiente)). El siguiente programa lista todas las entradas del directorio que coinciden con el patrn dado como primer parmetro. Para cada una imprime si se trata de un chero o a o un directorio accediendo a sus atributos:
/* Lista el directorio dado en argv[1] (puede incluir comodines) */ #include <windows.h> #include <stdio.h> /* Para printf */ /* Excepciones denidas por el usuario */ #dene EXC MEM (1 << 0) #dene EXC DISK (1 << 1) /* Funcin para lanzar una excepcin del usuario */ o o void ReportException(LPCSTR mensaje, DWORD dwCodigo) { /* Imprimir el error */ perror(mensaje); /* Lanzar la excepcin */ o if (dwCodigo != 0) RaiseException ((0x0FFFFFFF & dwCodigo) | 0xE0000000, 0, 0, NULL); } int main(int argc, char*argv[ ]) { HANDLE hDirList; WIN32 FIND DATA fdData; try { /* Tratamiento de excepciones */ try { /* Limpiar handlers y buers */
20 5

10

15

25

13

if (INVALID HANDLE VALUE != (hDirList = FindFirstFile(argv[1], &fdData))) { /* Hay al menos un chero */ do { /* Imprimir el archivo le */ do printf(" %s", fdData.cFileName); if (fdData.dwFileAttributes & FILE ATTRIBUTE DIRECTORY) printf(" (dir)\n"); else printf(" (fichero)\n"); } while (FindNextFile( hDirList, &fdData)); if (GetLastError() != ERROR NO MORE FILES) ReportException("Error durante la bsqueda", EXC DISK); u } } nally { /* Eliminar memoria y handlers */ if (hDirList != INVALID HANDLE VALUE) FindClose(hDirList); hDirList = INVALID HANDLE VALUE;

30

35

40

45

50

} { }

} except (EXCEPTION EXECUTE HANDLER)


55

printf("Programa finalizado.\n"); return 0; } /* $Id: dir.c,v 1.2 2004/02/11 19:52:40 dsevilla Exp $ */
60

El programa utiliza el mismo esquema de tratamiento de errores con la funcin ReportException. o Lo ms destacado del programa es lo siguiente: a L nea 29: Se llama a la funcin FindFirstFile. Esta funcin acepta la especicacin de los o o o cheros a buscar (que puede incluir una ruta completa y comodines) y un puntero a una estructura WIN32 FIND DATA: typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; CHAR cFileName[ MAX_PATH ]; CHAR cAlternateFileName[ 14 ]; } WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA; El campo dwFileAttributes guarda los atributos del chero. Los siguientes campos datos como la fecha y el tamao4 . Finalmente, el campo cFileName guarda el nombre del chero encontrado n
4 Como

se ve, el tamao est dividido en dos partes de 32 bits. Esto es para permitir cheros de ms de 4GB. n a a

14

y cAlternateFileName un nombre compatible con MS-DOS (los t picos nombres encontrados en los listados del directorio de la forma ((FICHERO~1.EXT))). La funcin devuelve un HANDLE para continuar la bsqueda. o u L nea 37: Se comprueba si el chero es un directorio o un archivo normal a travs de los atributos e del mismo. L nea 42: Se llama a la funcin FindNextFile. Esta funcin acepta el HANDLE devuelto por o o FindFirstFile y actualiza la estructura WIN32 FIND DATA. L nea 50: Finalmente, dentro del bloque finally se llama a FindClose para terminar la bsqueu da de cheros. Ntese que este paso siempre se realiza, independientemente de que se haya produo cido error o no, evitando que se desperdicien recursos (por ejemplo, la bsqueda que ha quedado u a medio mantiene datos sobre cul fue el ultimo chero devuelto, etc.). a

2.7.

Seales de consola n

Por ultimo, se ver el mecanismo de seales de consola. Estas seales permiten tratar por ejemplo a n n la pulsacin de Ctrl+C por parte del usuario o tomar el control cuando el usuario pulsa el botn de o o ((cerrar ventana)). El ejemplo siguiente muestra un uso sencillo de un handler de que trata las seales que recibe el n programa.
#include <windows.h> #include <stdio.h> /* Funcin Handler */ o static BOOL WINAPI TrataEvento(DWORD event); /* Variable para indicar si el programa terminar */ a volatile static BOOL salida = FALSE; int main(int argc, char* argv[ ]) { /* Establecer la funcin handler */ o if (!SetConsoleCtrlHandler(TrataEvento, TRUE)) { printf("No se puede establecer el handler.\n"); return 1; } /* Bucle de espera */ while (!salida) { printf("."); ush(stdout); Sleep(1000); } printf("Programa finalizado.\n"); return 0; } BOOL WINAPI TrataEvento(DWORD event) { switch(event) { case CTRL C EVENT: printf("Recibido Ctrl-C, el programa terminar en 4 segundos.\n"); a
30 10

15

20

25

35

15

break;
40

case CTRL CLOSE EVENT: printf("Recibido evento de cierre de ventana." " Terminando en 4 segundos.\n"); break;
45

default: printf("Recibido el evento nmero %d. Quedan 4 segundos\n", event); u } /* Duerme cuatro segundos */ Sleep(4000); /* Se saldr del bucle principal */ a salida = TRUE;
55 50

/* El handler ha procesado la seal */ n return TRUE; }

El programa se puede ver l nea a l nea: L nea 5: Se dene la funcin TrataEvento. Esta funcin ser la que se llame cuando se reciba o o a una seal. La denicin de la funcin (salvo el nombre) es siempre la misma. n o o L nea 8: La variable salida se dene como booleana y volatile. Esta especicacin ayuda al o compilador a tratar la variable como una entidad que ser modicada de forma as a ncrona. Por ejemplo, se modicar dentro de la funcin TrataEvento cuando se reciba una seal. 5 a o n L nea 14: Se utiliza la funcin SetConsoleCtrlHandler para aadir la funcin que ser llamada o n o a al recibirse una seal. Tiene dos parmetros: la funcin y una variable booleana que especica si n a o aadir (TRUE) o eliminar (FALSE) el handler. Algunas consideraciones: n Se pueden aadir varios handlers, que sern llamados en orden inverso al que se aadieron a n a n SetConsoleCtrlHandler. Si el segundo parmetro es FALSE, el handler se eliminar de la lista de handlers. a a Con los parmetros (((NULL, TRUE))), el programa ignorar a partir de ese momento la pula a sacin Ctrl+C. o L neas 2126: Se inicia el bucle principal del programa. Simplemente imprime un punto y espera un segundo usando la funcin Sleep. o L nea 35: Se ejecuta un switch para discernir el evento recibido. Hay relativamente pocas seales, n y las ms importantes son las siguientes: a CTRL C EVENT El usuario ha pulsado Ctrl+C. CTRL CLOSE EVENT El usuario ha intentado cerrar la ventana pulsando el botn de cerrar o ( Alt+F4). o CTRL LOGOFF EVENT El usuario est terminando la sesin. a o Estas seales permiten obtener el control del programa cuando el usuario quiera cerrar la aplican cin, por ejemplo para preguntar si se quieren guardar los datos, etc. o L nea 54: Despus de dormir cuatro segundos, se establece a TRUE la variable salida, causando e que el programa termine el bucle en la siguiente iteracin. o
5 El

compilador no guardar esa variable en un registro, ya que puede ser modicada en el curso del bucle por otra a funcin cuando se recibe una seal. As la variable debe estar en memoria y se debe leer en cada bucle. o n ,

16

Como se ve en la ejecucin del programa, cuando se produce una seal se crea un nuevo hilo para o n tratarla. As el programa principal sigue funcionando (los puntos se imprimen cada segundo) mientras , se est tratando el evento. a $Revision: 1.15 $

17

También podría gustarte