hola.cpp
:
<windows.h>
. Por ejemplo, podemos hacer que
el mensaje "Hello, World!" tarde en aparecer tres segundos usando
la llamada al API Sleep
. Para ello, hacemos:
// hola.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> int main(int argc, char* argv[]) { Sleep(3000); printf("Hello World!\n"); return 0; }Como habréis observado, el propio entorno os recuerda el prototipo de las funciones que uséis y también os da formato automáticamente al programa (¡viva la uniformidad!). De todos modos, esto es configurable. En el menú Tools, apartado Options, se puede hacer que que el entorno no formatee automáticamente el programa. Lo que sí debéis hacer es seleccionar la opción de incluir espacios en lugar de tabuladores para que las prácticas se os impriman bien siempre y pasen bien por el sistema de correo electrónico:
main
un tanto extraña.
Es así para compatibilizar con
UNICODE. No os preocupéis.
BOOL GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode);En primer lugar, notaréis que no se escatima en el nombre de las funciones y variables. Suelen ser nombres largos y se suele poner en mayúsculas la primera letra de cada palabra que forma el nombre.
true
y false
para este tipo.
Tipo | Descripción |
BOOL, BOOLEAN |
Variable booleana (TRUE/FALSE) |
BYTE |
Byte (8 bits) |
CALLBACK |
Convenio de llamada para funciones de rellamada |
CHAR |
Carácter ANSI de 8 bits |
DWORD |
Entero sin signo de 32 bits |
FLOAT |
Variable de punto flotante |
HANDLE |
Handle, manejador o asa de un objeto |
HFILE |
Handle de un fichero |
INT |
Entero con signo |
LONG |
Entero de 32 bits con signo |
LONGLONG |
Entero de 64 bits con signo |
LPcosa |
Puntero a cosa |
LPCSTR |
Cadena de caracteres ANSI constante
acabada en \0 |
LPCWSTR |
Cadena UNICODE constante
acabada en \0 |
LPCTSTR |
Cadena UNICODE o ANSI constante según se defina |
LPCVOID |
Puntero a una constante de cualquier tipo |
LPSTR |
Cadena de caracteres ANSI
acabada en \0 |
LPWSTR |
Cadena UNICODE acabada en \0 |
LPTSTR |
Cadena UNICODE o ANSI según se defina |
Pcosa |
Puntero a cosa |
SHORT |
Entero corto |
TBYTE, TCHAR |
WCHAR ó CHAR según proceda |
UCHAR, UINT, ULONG, |
Valores sin signo de CHAR, INT, ... |
WCHAR |
Caráter UNICODE |
WINAPI |
Convenio de llamada para llamadas al API |
WORD |
Entero sin signo de 16 bits |
CreateProcess
, cuyo prototipo es:
BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bHeredarHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);Como primera diferencia con las llamadas al sistema del UNIX, observad cómo la función devuelve un valor de tipo BOOL, es decir, verdadero o falso. Devolverá falso (0) cuando falle y habrá que usar la llamada del API
GetLastError
para ver cuál fue
el error en concreto que se produjo. Si lo que deseamos saber
es el identificador o el HANDLE del proceso
recién creado, hay
que acudir a los campos hProcess
y
dwProcessId
de la estructura de tipo
PROCESS_INFORMATION
que nos devuelve la función.
Observaréis también que las
llamadas del API de Win32 suelen tener
muchos más parámetros que
sus equivalentes de UNIX.
Afortunadamente, casi todos los parámetros
pueden llevar un valor por defecto.
En el resumen
podéis ver una breve descripción
de los parámetros que podéis usar.
PROCESS_INFORMATION informaciOn; STARTUPINFO suinfo; [...] /* O bien rellenamos la estructura "a mano" */ suinfo.cb=sizeof(suinfo); suinfo.lpReserved=NULL; suinfo.lpDesktop=NULL; suinfo.lpTitle=NULL; suinfo.dwFlags=0; suinfo.cbReserved2=0; suinfo.lpReserved2=NULL; /* O usamos la llamada a la API siguiente... */ GetStartupInfo(&suinfo); [...] if (!CreateProcess(NULL,"notepad",NULL,NULL,false, CREATE_NEW_PROCESS_GROUP,NULL, NULL,&suinfo,&informaciOn)) { fprintf(stderr,"CreateProcess: error %d.\n",GetLastError()); return 1; } [...]Los errores que produce este código son numéricos. Si queremos algo parecido al
perror
de UNIX, hay
que esforzarse un poco:
PROCESS_INFORMATION informaciOn; STARTUPINFO suinfo; [...] GetStartupInfo(&suinfo); if (!CreateProcess(NULL,"notepada",NULL,NULL,false, CREATE_NEW_PROCESS_GROUP,NULL, NULL,&suinfo,&informaciOn)) { LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf,0,NULL ); fprintf(stderr,"CreateProcess: %s\n",lpMsgBuf); LocalFree( lpMsgBuf ); return 1; }No estaría mal que usárais una macro que hiciera este trabajo sucio. En esencia, se trata de que el sistema reserve memoria dinámicamente y escriba el código de error allí en el idioma por defecto del ordenador. La macro podría llamarse así en honor al UNIX, que en paz esté:
#define PERROR(a) \ { \ LPVOID lpMsgBuf; \ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ FORMAT_MESSAGE_FROM_SYSTEM | \ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, \ GetLastError(), \ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \ (LPTSTR) &lpMsgBuf,0,NULL ); \ fprintf(stderr,"%s:%s\n",a,lpMsgBuf); \ LocalFree( lpMsgBuf ); \ }Y al usarse quedaría:
if (!CreateProcess(NULL,"notepada",NULL,NULL,false, CREATE_NEW_PROCESS_GROUP,NULL, NULL,&suinfo,&informaciOn)) { PERROR("CreateProcess"); return 1; }El error que nos da ahora la aplicación es bastante más descriptivo:
CreateProcess:El sistema no ha encontrado el archivo especificado.En el resumen, podéis encontrar cómo obtener un manejador o el identificador del proceso actual, acabar con procesos u obtener el código de retorno de un proceso. Además, se habla algo acerca de la prioridad de los hilos de un proceso, que veremos posteriormente.
DWORD WINAPI funciOn(LPVOID parAmetro);El código de retorno de la función será el código de retorno del hilo y, a la función, se le podrá pasar un parámetro (un puntero). Se dejó que fuera un puntero para que así se pudiera pasar cualquier cosa a la función. Si se necesita pasar más de un parámetro, se define una estructura que los contenga y se pasa un puntero a esa estructura.
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);Un par de parámetros los dejaremos a su valor por defecto:
lpThreadAttributes
a NULL
,
dwStackSize
a 0
.
El tercer parámetro es un puntero
a la función que ejecutará el
thread, el cuarto es parámetro
que pasaremos a la función cuando
se ejecute el thread. dwCreationFlags
es un campo de
bits donde se especifican opciones
de la función (en estos momentos
bien es verdad que solo hay una opción,
pero la intención es lo
que cuenta). En el último parámetro
se nos devuelve el identificador del thread.
8 EJEMPLOS DE PLANIFICACIÓN.
8.2 Windows.
Tiene en cuenta que el sistema es multitarea, pero monousuario. Se trata de un planificador apropiativo por prioridades.
Se planifica según hilos, aunque los procesos poseen una prioridad base.
Clases de prioridad (mayor número=mayor prioridad):
Cada proceso tiene una prioridad base.
Reglas de prioridad para los hilos:
Dentro de cada prioridad, se sigue un turno rotatorio.
El ejecutor de Windows premia a los hilos de prioridad variable que tienen E/S y castiga a los que agontan su cuanto.
En sistemas multiprocesadores, se reserva siempre un procesador para los hilos de menor prioridad y hay un atributo del hilo de afinidad a un procesador en concreto.
La prioridad base del proceso vista en teoría depende de la clase de prioridad a la que pertenezca:
Clase | Prioridad base del proceso |
IDLE | 4 |
NORMAL | 8 |
HIGH | 13 |
REALTIME | 24 |
CreateProcess
. Por su parte, la prioridad base
del hilo depende de la prioridad base del proceso y se establece con
la función SetThreadPriority
.
Se usan unas macros para ello:
Macro | Prioridad base del hilo |
THREAD_PRIORITY_IDLE |
1 ó 16 |
THREAD_PRIORITY_LOWEST |
base_proceso - 2 |
THREAD_PRIORITY_BELOW_NORMAL |
base_proceso - 1 |
THREAD_PRIORITY_NORMAL |
base_proceso |
THREAD_PRIORITY_ABOVE_NORMAL |
base_proceso + 1 |
THREAD_PRIORITY_HIGHEST |
base_proceso + 2 |
THREAD_PRIORITY_TIME_CRITICAL |
15 ó 31 |
BOOL DuplicateHandle( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bHeredaHandle, DWORD dwOptions);En el resumen, podéis ver cómo usar la función.
OpenProcess(PROCESS_ALL_ACCESS,FALSE,identificador);
hijos
. El argumento
es un número entero>100.
El programa comprobará que los argumentos
pasados son correctos y, si no es así,
emitirá el correspondiente
aviso por el canal de error estándar.
El proceso creará dos hilos de ejecución
que ejecutarán una
misma función fnHilo
.
La función fnHilo
desreferenciará el
puntero que se le ha pasado, para conseguir un carácter.
A continuación, repetirá un bucle
tantas veces como el argumento
pasado al programa y, en cada iteración,
imprimirá, con
printf
el carácter que se le ha pasado. Usad
fflush
para que el carácter
se imprima inmediatamente.
El primero de los hilos creado por el padre, recibirá como
parámetro '-'. El segundo, '+'.
El propio padre llamará
(sin crear un hilo) a la función fnhilo
,
pasándole
un cero ('0'). Ejecutad la práctica
y ved cómo Windows da la CPU a
los diferentes hilos,
variando en cada ejecución el valor pasado al programa.
Si se añade un Sleep(0)
después
del fflush
, ¿cómo
afecta a la salida?
Podéis ver la salida con más comodidad si
la redirigís a un fichero desde la línea
de órdenes.
[...] caracter = '-'; lpThreadHnd[0] = CreateThread(NULL, 0, imprimir, (void*)&caracter, 0, lpThreadId); if(lpThreadHnd[0] == NULL) { PERROR("CreateThread"); return 1; } caracter = '+'; lpThreadHnd[1] = CreateThread(NULL, 0, imprimir, (void*)&caracter, 0, lpThreadId + 1); [...]Pista: Hombre de poca fe.