Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links
INTRODUCCION A LA PROGRAMACION BAJO WINDOWS (parte
2) TIPOS DE DATOS MAS FRECUENTES EN WINDOWS
LAS FUNCIONES REGISTERCLASSEX() //definimos la estructura de la ventana WNDCLASSEX wcx; // estructura de la ventana //determinamos los valores de los campos wcx.style = CS_HREDRAW | CS_VREDRAW; // valores más usuales //registramos la clase de la ventana ya registrada if( !RegisterClassEx( &wcx ) ) return( FALSE ); // en caso de error, salir La variable que se le pasa a RegisterClassEx() es una estructura de tipo WNDCLASSEX, la cual tiene la siguiente forma en los archivos de cabecera de Windows: typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX; LOAICON(), LOADCURSOR(), Y GETSTOCKOBJECT() HICON LoadIcon( HINSTANCE hInstance, // handle de la instancia LPCTSTR lpIconName // puntero a la cadena con un nombre // o un identificador de recursos ); El parámetro lpIconName constituye un identificador de recursos, de los cuales los mas habituales son los que pueden verse a continuacion: IDI_APPLICATION Default application icon. IDI_ASTERISK Same as IDI_INFORMATION. IDI_ERROR Hand-shaped icon. IDI_EXCLAMATION Same as IDI_WARNING. IDI_HAND Same as IDI_ERROR. IDI_INFORMATION Asterisk icon. IDI_QUESTION Question mark icon. IDI_WARNING Exclamation point icon. IDI_WINLOGO Windows logo icon LoadCursor(): Sirve para cargar un cursor para el ratón, devolviendo el handle identificador del mismo. De la misma manera lo usamos en WNDCLASSEX para definir el aspecto del cursor cuando nuestra aplicación se este ejecutando (aunque tambien podemos cambiarlo en cualquier momento): HCURSOR LoadCursor( HINSTANCE hInstance, // handle de la instancia LPCTSTR lpCursorName // puntero a la cadena con un nombre ); los valores mas habituales de IpCursorName son los siguientes: IDC_APPSTARTING Standard arrow and small hourglass IDC_ARROW Standard arrow IDC_CROSS Crosshair IDC_HAND Windows NT 5.0 and later: Hand IDC_HELP Arrow and question mark IDC_IBEAM I-beam IDC_ICON Obsolete for applications marked version 4.0 or later. IDC_NO Slashed circle IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL. IDC_SIZEALL Four-pointed arrow pointing north, south, east, and west IDC_SIZENESW Double-pointed arrow pointing northeast and southwest IDC_SIZENS Double-pointed arrow pointing north and south IDC_SIZENWSE Double-pointed arrow pointing northwest and southeast IDC_SIZEWE Double-pointed arrow pointing west and east IDC_UPARROW Vertical arrow IDC_WAIT Hourglass GetStockObject(): Es utilizado para extraer un objeto grafico del stock predefinido del GDI de Windows, permitiéndonos utilizar recursos incluidos dentro del propio Windows, como la brocha(brush), paleta (palette), fuentes(font), plumas(pen), etc. HGDIOBJ GetStockObject( int fnObject // entero con el valor del objeto ); En nuestro caso la hemos utilizado para extraer la brocha blanca con la que rellenar el fondo de nuestra ventana (indicándola en wcx.hbrBackGround()). LA FUNCION CREATEWINDOWEX() HWND CreateWindowEx( DWORD dwExStyle, // estilo extendido de la ventana LPCTSTR lpClassName, // puntero al nombre de la clase LPCTSTR lpWindowName, // puntero al titulo de la ventana DWORD dwStyle, // estilo de la ventana int x, // posicion orizontal de la ventana int y, // posicion vertical de la ventana int nWidth, // ancho de la ventana int nHeight, // alto de la ventana HWND hWndParent, // handle a la ventana padre HMENU hMenu, // handle al menu (NULL=no menu) HINSTANCE hInstance, // handle a la instancia LPVOID lpParam // puntero a los datos de creacion ); De esta declaración podemos deducir fácilmente el significado de (x,
y) y (nWidth, nHeight) como la posición y el
tamaño de la ventana en pixels (referido a la esquina (0, 0) del escritorio
(o pantalla)). El parámetro lpClassname es el nombre de la clase
con la que registramos la ventana y hInstance es un handle a
la instancia actual de la aplicación (cuando ejecutamos varias veces un
mismo programa, cada ventana es una nueva instancia del mismo). Mucho
de esos parámetros se ponen a NULL, como por ejemplo al no disponer de
ventana padre. CUADROS DE DIALOGO int MessageBox( HWND hWnd, // handle ventana LPCTSTR lpText, // frase informacion LPCTSTR lpCaption, // titulo UINT uType // estilo del cuadro ); De los cuatro parámetros, el primero es el handle a la ventana padre (que puede
ser NULL si no queremos que pertenezca a ninguna ventana en concreto),
el segundo (lpText) un puntero al texto que queremos que aparezca
en el (Windows partirá la línea si es necesario) pudiendo además incluir
secuencias de escape como \n, etc. El tercero constituye el titulo de
la ventana y el ultimo (y mas importante) el estilo del dialogo, es decir,
los botones y el tipo de dialogo deseado. Podemos usar usar varios de
estos indicadores simultáneamente empleando el operador OR Como resultado,
MessageBox nos devuelve el botón que haya sido pulsado (ya sea
como ratón o teclado) , o cero en caso de error. //Listado 1 #include <windows.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int opcion; opcion = MessageBox( NULL, "Texto de información.", "Título del MessageBox.", MB_OKCANCEL | MB_ICONASTERISK ); if( opcion == IDOK ) MessageBox(NULL, "Ha pulsado ACEPTAR", "Salir", MB_OK ); else MessageBox(NULL, "Ha pulsado CANCELAR", "Salir", MB_OK ); return(0); } LAS MACROS LOWORD E HWORD int Anchi, Alto; // ... codigo ... case WM_SIZE Ancho=LOWORD(lParam); Alto=HWORD(lParam); Cuando el mensaje recibido es WM_SIZE , IParam se obtiene el nuevo tamaño del área cliente de la ventana , estando en la parte alta de IParam la anchura y en el WORD bajo, la altura. LA FUNCIÓN TEXTOUT(): TEXTO EN NUESTRA APLICACIÓN BOOL TextOut( HDC hdc, // handle al DC int nXStart, // coordenada x int nYStart, // coordenada y LPCTSTR lpString, // puntero al texto int cbString // longitud de la cadena ); Los parámetros a pasarle a la función son un handle de contexto de dispositivo
(que podemos obtener mediante BeginPaint() y bien, como veremos en el próximo tutorial mediante GedDC), las coordenadas (en pixels) donde imprimir la cadena , la propia cadena, y
la longitud en caracteres pues esta función no busca el carácter END OF STRING (0)
al final de la misma. //----------- // Listado 2 //----------- #include <windows.h> //--- Declaración de funciones del programa ----------- int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ); LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); void MenuProc( HWND, UINT, WPARAM, LPARAM ); //--- Declaración de variables del programa ----------- char WindowName[] = "Default Window"; char WindowTitle[] = "Windows95 application"; //=== Función principal WinMain() ===================== int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { HWND hwnd; MSG msg; WNDCLASSEX wcx; // Definimos la estructura de clase: wcx.cbSize = sizeof( WNDCLASSEX ); wcx.style = CS_HREDRAW | CS_VREDRAW; wcx.lpfnWndProc = WndProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = hInstance; // icono, cursor, fondo e icono pequeño: wcx.hIcon = LoadIcon(NULL, IDI_WINLOGO); wcx.hCursor = LoadCursor(NULL, IDC_ARROW); wcx.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ); wcx.hIconSm = LoadIcon(NULL, IDI_WINLOGO); wcx.lpszClassName = WindowName; wcx.lpszMenuName = NULL; // Registramos la clase de ventana ya preparada: if( !RegisterClassEx( &wcx ) ) return( FALSE ); // Creamos la ventana con CreateWindowEx(): hwnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW, WindowName, WindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL ); // Comprobamos la creación de la ventana: if( !hwnd ) return( FALSE ); // Hacemos visible la ventana y la actualizamos: ShowWindow( hwnd, nCmdShow ); UpdateWindow( hwnd ); // Bucle de mensajes, env¡a los mensajes hacia WndProc while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } // devolvemos el valor recibido por PostQuitMessage(). return( msg.wParam ); } //=== Función del procedimiento de ventana WndProc() ==== LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { HDC hdc; PAINTSTRUCT ps; TEXTMETRIC tm; RECT rect; int loop; static long cxChar, cyChar; switch( message ) { // mensaje producido en la creación de la ventana case WM_CREATE: hdc = GetDC( hwnd ); GetTextMetrics( hdc, &tm ); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight + tm.tmExternalLeading; ReleaseDC( hwnd, hdc ); break; case WM_PAINT: hdc = BeginPaint( hwnd, &ps ); GetClientRect( hwnd, &rect ); for( loop=0; loop<10; loop++ ) TextOut( hdc, 0, loop*cyChar, "ESTO ES UNA PRUEBA", 18 ); EndPaint( hwnd, &ps ); break; // mensaje producido al aumentar el tamaño de la ventana case WM_SIZE: // aqui habriamos de coger de nuevo el tamaño. break; // mensaje producido al cerrar la ventana case WM_DESTROY: PostQuitMessage( 0 ); break; // resto de mensajes, dar una respuesta estándar. default: return( DefWindowProc( hwnd, message, wParam, lParam ) ); } return(0); } TEXTOUT Y WSPRINTF char cadena[80]; int longitud; //... codigo ... longitud=wsprintf(cadena, "El resultado es %d", total); TextOut(hdc, 100, 100, cadena, longitud); De esta manera , en nuestra aplicación podemos imprimir tanto cadenas de texto puro como valores numéricos de cualquier tipo, aprovechando además que wsprintf() nos devuelve el tamaño de la cadena creada. EL BUCLE DE MENSAJES // Bucle de mensajes, envía los mensajes hacia WndProc while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); // convertimos el mensaje DispatchMessage( &msg ); // devolvemos el control a w95 } // devolvemos el valor recibido por PostQuitMessage(). return( msg.wParam ); Este es un bucle que se repetirá hasta que el usuario salga de la aplicación momento en el que recibiremos un cero y el while será FALSE En casi de recibir un valor distinto de 0 (TRUE) , el bucle continua Hay que decir que Windows tiene una cola de mensajes , existiendo una continua llegada y traducción de los mismos. BOOL GetMessage( LPMSG lpMsg, // puntero a la estructura que contiene el mensaje recibido HWND hWnd, // handle a la ventana que lo recibe UINT wMsgFilterMin, // identificador del primer mensaje UINT wMsgFilterMax // identificador del ultimo mensaje ); El tipo de datos LPMSG es (vayámonos acostumbrándonos a la terminología "húngara" de Windows) un puntero largo a MSG (LP=Long Pointer): typedef struct tagMSG { // msg HWND hwnd; // handle a la ventana destinataria UINT message; //identificador del mensaje WPARAM wParam; // informacion adicional LPARAM lParam; // informacion adicional DWORD time; // hora del envio del mensaje POINT pt; // coordenadas del raton } MSG; WPARAM es el mismo tipo que UINT y LPARAM es un typedef para LONG , la estructura tipo POINT contiene las coordenadas del ratón en el momento de recibir el mensaje y esta definida como: typedef struct tagPOINT { LONG x; // coordenada x LONG y: // coordenada y }POINT; Como puede verse en la línea GetMessage(&msg, NULL, 0, 0) handle a la ventana puede especificarse como NULL, ylparam/wparam a cero con el fin de obtener todos los mensajes para nuestra aplicación, no solo par una determinada ventana ( si hubiesemos creado diferentes ventanas hijas). Continuando con el comentario del bucle del mensajes, mediante TransalateMessage() traducimos algunos de estos mensajes, y mediante DispatchMessage() los enviamos a la función de procedimientos de ventana para que realice su proceso. OTRA MANERA DE RECOGER LOS MENSAJES while(1) { HacerAlgo(); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message==WN_QUIT) return (msg.wParam); TranslateMessage(&msg); DispatchMessage(&msg); } } Mediante el bucle anterior hacemos que nuestra función vaya en busca de los mensajes al mismo tiempo que ejecutamos código propio de nuestra aplicación (llamando en el ejemplo anterior de manera ilustrativa HacerAlgo()), que podría, en el caso de un juego redibujar la pantalla, mover los sprites a distintas posiciones , con esto le estamos robando tiempo a otras aplicaciones. Códigos fuentes de los dos ejemplos de este tutorialLos ejemplos los compile con el VisualC++ 6.0 |
Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links