Tutorial de win32

Dudas sobre compiladores que funcionan bajo Windows (DJGPP, OpenWatcom, Visual C++, Borland C, etc ...).
Mensaje
Autor
Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Tutorial de win32

#1 Mensaje por rir3760 » 08/11/2012 9:42 am

Lo primero: mil disculpas a untio por la misteriosa desaparición de su tema "Tutorial de win32", honestamente no se como explicarlo ...

Por razones obvias debo esgrimir a mi favor el intenso estrés de la vida cotidiana mas la complejidad que tiene moderar un foro como este, créanme que tiene sus dificultades.

Mas todavía: en incontables ocasiones los moderadores e incluso el administrador del sitio nos hemos quejado de su operación. En especial su interfaz de usuario es confusa y eso nos ha llevado una y otra vez a cometer errorcillos siendo los mas comunes los causados por el texto (ambiguo) de ciertos botones como ... Eliminar MENSAJE y ... mmm ... Eliminar T E M A ...

Vamos que cualquiera a las prisas los mira por "encimita" y PUEDE confundirse ... Lo mas natural del mundo ... A quien no le ha pasado ...

m(_ _)m

m(_ _)m

m(_ _)m

m(_ _)m

m(_ _)m

m(_ _)m

Por fortuna tenia todo el tema en el cache del navegador. En las próximas horas publico el texto completo.

Un saludo
Última edición por rir3760 el 08/11/2012 10:18 am, editado 1 vez en total.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#2 Mensaje por rir3760 » 08/11/2012 9:47 am

Primer mensaje de untio -- Mar Oct 30, 2012 8:03 am

----

Hola,

Voy a tratar de introduciros en la programación gráfica de Windows al nivel más crudo: el api win32.

Para ello voy a usar la version 10.5 de codeblocks, descargable desde:
http://www.codeblocks.org/downloads/26
Os recomiendo que descarguéis la versión que incluye mingw (el compilador).

Una vez instalado, vamos al menu File (fichero)/ New (nuevo) y seleccionamos Project (proyecto). Desplazamos hacia abajo los iconos y seleccionamos win32 gui project. Damos al botón go (ir). Clicamos en next (siguiente). Seleccionamos Frame based. Clicamos en next, seleccionamos un nombre y una carpeta para nuestro proyecto. Clicamos en next, luego en Finish y tenemos un proyecto con un fichero llamado main.cpp.

Si lo ejecutamos, vemos que nos aparece una ventana de consola y una ventana de windows.

Seleccionamos en el menu project (proyecto) la opción properties (propiedades) y clicamos en la pestaña build targets. A la izquierda tenemos seleccionado Debug y a la derecha arriba vemos un desplegable con la opción Console Application. Clicamos en la flecha de ese desplegable y lo cambiamos a GUI application. Después seleccionamos a la derecha debajo de Debug la opción Release y si en el desplegable hay Console application la cambiamos también a GUI application.

Seleccionamos en el menú build la opcion rebuild para reconstruir nuestro proyecto y ya sólo nos aparece la ventana. Ya nos hemos olvidado de la consola.

El código de main.cpp es:

Código: Seleccionar todo

#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}
Las aplicaciones win32 "normales" suelen consistir en:
1. Como necesitamos una ventana al menos, definimos una clase de ventana. No tiene nada que ver con c++. Lo que hacemos es especificar los iconos, cursor del ratón y otras opciones que se incluyen en una estructura del tipo WNDCLASSEX. Los detalles, de los que hablaré en otro post se pueden consultar en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
2. Se crea y se muestra la ventana.
3. Se entra en el bucle de mensajes. El sistema nos envía mensajes. Por ejemplo, si el usuario hace clic en nuestra ventana, el sistema nos envía un mensaje específico. Si se pulsa una tecla, nos envía otro. Si se cambia el tamaño de la ventana, se envía otro. etc.
4. Se pide al sistema que mande cada mensaje a la función de ventana. Allí se procesa cada mensaje que queremos procesar. Los que no nos interesan le pedimos al sistema que les de un procesamiento por defecto.
5. Llega un mensaje que dice que destruyamos la ventana. Pedimos al sistema que ponga en la cola de mensajes uno que hace que el bucle de mensajes acabe.
6. Salimos del programa.

Bien. Voy a comentar el principio del código que nos ha puesto codeblocks:

Código: Seleccionar todo

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
WINAPI podría ser sustituido por _stdcall (convención de llamada estándar, estándar para Microsoft). La convención de llamada indica como se pasan los parámetros a una función y como se restauran las cosas (en este caso la pila) cuando la función acaba. Las funciones de windows usan la convención de llamada estándar.

WinMain. Los programas gráficos win32 no empiezan en main. Empiezan en WinMain. WinMain tiene siempre cuatro parámetros, a saber:

HINSTANCE hThisInstance: En windows hay muchos programas funcionando. Para distinguir unos de otros, se tiene el manejador de la instancia. Algo así como un identificador. Este parámetro es el manejador de esta instancia del programa.

HINSTANCE hPrevInstance: En las versiones de 16 bits de windows, este parámetro tenía el manejador de la instancia previa del programa (si se arrancaba 2 veces). En las versiones de 32 bits, este parámetro siempre contiene NULL, es una reliquia del pasado que no se usa.

LPSTR lpszArgument: Es la línea de parámetros del programa. No contiene el nombre del programa. Es el conjuto de parámetros sin el nombre del programa. Por ejemplo:
Si arrancamos el programa prg.exe con la orden:
prg.exe par1 par2:
el parámetro contendría par1 par2.

int nCmdShow: Como se muestra la ventana. Cuando se muestra la ventana por primera vez, hay que pasarle a la funcion ShowWindow este parámetro, aunque queramos mostrarla de otra manera. Si es este el caso, habrá que hacer 2 llamadas. La primera deberá ser con este parámetro. Fijaos en la línea:
Código:
ShowWindow (hwnd, nCmdShow);

Si queréis saber más de ShowWindow, mirad aquí:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Bueno, creo que este post ya es bastante largo. Mejor dejo para mañana entrar en más detalle en la estructura WNDCLASSEX. Si tenéis dudas, sólo tenéis que poner vuestra pregunta.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#3 Mensaje por rir3760 » 08/11/2012 9:53 am

Segundo mensaje de untio -- Mie Oct 31, 2012 8:24 am

----

Siguiente tema: WNDCLASSEX

La parte de código que voy a comentar es:

Código: Seleccionar todo

WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;
Los detalles están explicados en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

wincl.hInstance = hThisInstance : La instancia donde está la función de ventana de esta clase. Es la función que procesa los mensajes. En nuestro caso se llama WindowProcedure y está en este mismo módulo. Por lo que asignamos hThisInstance que es el manejador de esta instancia del programa.

wincl.lpszClassName = szClassName: Una cadena de caracteres con el nombre que queremos dar a la clase de ventana (recuerdo que no tiene nada que ver con c++.

wincl.lpfnWndProc = WindowProcedure; La función que procesará los mensajes de las ventanas de esta clase.

wincl.style = CS_DBLCLKS; El estilo (que define ciertos comportamientos) de la clase de ventana. Precisamente, aquí decimos que procesará los mensajes de doble clic del ratón.

wincl.cbSize = sizeof (WNDCLASSEX): Hay muchas estructura en win32 y muchas versiones de ellas, por eso en muchas aparece el campo cbSize. Sencillamente le pasamos el tamaño de la propia estructura. Os recuerdo que sizeof(WNDCLASSEX) devuelve el número de bytes que ocupa WNDCLASSEX, del mismo modo que sizeof(int) devuelve 4 en un sistema de 32 bits (o suele ser así).

wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION): A wincl.hIcon hemos de asignarle el icono de 32x32 de nuestro programa. Como no hemos creado un icono todavía le asignamos uno de los iconos que define el sistema por defecto. A LoadIcon se le pasa el manejador de la instancia que contiene el icono y el identificador del icono. Si se le pasa NULL, usa los iconos del sistema e IDI_APPLICATION es uno de ellos. Si quieres saber más:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION): A wincl.hIconSm hemos de asignarle el icono 16x16 de nuestro programa. El 32x32 aparece cuando hay que mostrar un icono grande y éste aparece, por ejemplo, a la izquierda de la barra de título de la ventana. Se le asigna un icono por defecto de los que dispone Windows.

wincl.hCursor = LoadCursor (NULL, IDC_ARROW): Aquí se asigna el cursor. El cursor para win32 es dibujito del ratón, no lo que parpadea al poner texto. Esto último lo llaman caret. Cuando el ratón pase por encima de nuestra ventana, su cursor cambiará a éste. Ocurre algo similar a LoadIcon, si el manejador de la instancia es NULL usa los del sistmea. Concretamente IDC_ARROW es la típica flecha. Si se quiere saber más:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

wincl.lpszMenuName = NULL; El nombre del menú en el archivo de recursos. Hablaremos de los recursos bastante más adelante. Los recursos son cosas que se incluyen en el ejecutable que no son códig : iconos, cursores, bitmaps, definiciones de menú, etc. Ya lo veremos.

wincl.cbClsExtra = 0; Es el número de bytes extra que se reservan después de la estructura. Se suele dejar siempre a cero.

wincl.cbWndExtra = 0; El el número de bytes extra que se reservan después de la instancia de la ventana. También se suele dejar a cero.

wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; La brocha con la que se pinta el fondo. La brocha es como un patrón que se va repitiendo. Para este caso suele usarse un color sólido.

RegisterClassEx (&wincl): Ya hemos definido nuestra clase de ventana, pero hay que registrarla en el sistema para poder usarla. Con esta llamada la registramos.

Ahora voy a hacer unos cambios en el código original.

He hecho 2 cambios que he comentado:

Código: Seleccionar todo

#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /* Use Windows's default colour as the background of the window */
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);




    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#4 Mensaje por rir3760 » 08/11/2012 9:57 am

Tercer mensaje de untio -- Jue Nov 01, 2012 8:59 am

----

Hola de nuevo.

En cuanto a c++ o c para win32: Hay una gran parte que se puede hacer con puro c, pero hay otras partes donde se usa c++, pero tranquilo, no es a un nivel demasiado elevado.

VAMOS A CREAR UNA INSTANCIA DE NUESTRA VENTANA:

Código: Seleccionar todo

   /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Code::Blocks Template Windows App",       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);
Los detalles de CreateWindowEx se pueden consultar en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Parametros: 1. Es el estilo extendido de la ventana. En el caso de una ventana normal se deja a cero, pero a veces conviene alguno. Se pueden consultar en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

2. El nombre de la clase de la ventana. Es el mismo nombre que habíamos puesto en un miembro de WNDCLASSEX. El sistema tiene varias clases predefinidas, por ejemplo, hay una clase de ventana que se llama "button".

3. Texto que ira en la ventana. Si es una ventana normal, el texto aparecerá en la barra de título, si es un botón, aparecerá sobre él.

4. Estilo de la ventana. El estilo empieza por WS_. El estilo extendido (que va en el primer parámetro, empieza por WS_EX_. No confundir.
Los diferentes estilos se pueden consultar en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

5. Posición x donde se colocará la esquina superior de la ventana. Relativa a la esquina superior izquierda. Si se escribe: CW_USEDEFAULT, el sistema da un valor por defecto.

6. Posición y donde se colocará la esquina superior de la ventana.
Relativa a la esquina superior izquierda. Si se escribe CW_USEDEFAULT, el sistema da un valor por defecto.

7. Ancho de la ventana. Si se escribe CW_USEDEFAULT, el sistema da un valor por defecto.

8. Alto de la ventana. Si se escribe CW_USEDEFAULT, el sistema da un valor por defecto.

9. Ventana madre. Si deseamos añadir un botón u otro control a nuestra ventana, pondremos aquí su manejador. En este caso, nuestra ventana no tiene ventana madre. Se puede escribir HWND_DESKTOP o también NULL.

10. Manejador del menú de la ventana. También se usa para asignar un identificador (un número) en ventanas como botones, etc.

11. Manejador de la instancia del módulo asociado a esta ventana. Recuerdo que es un parámetro de WinMain.

12. Es un puntero, que recibirá la función de ventana (la que procesa los mensajes) a través del mensaje WM_CREATE, que se recibe cuando se crea la ventana. Si no es necesario, se deja en NULL.

Esta función devuelve un valor del tipo HWND manejador de ventana. Es un identificador único que identifica nuestra ventana. Ya sabéis que en windows hay muchas ventanas, por eso cada una se identifica con un manejador.

Si la función tiene éxito, el sistema pondrá en la cola de mensajes el mensaje WM_CREATE, en él se hacen cosas como añadir controles (botones, cajas de texto, etc) y todo tipo de inicialización. Aunque los mensajes los veremos más adelante.

ShowWindow (hwnd, nCmdShow) Ya tenemos nuestra ventana. Ahora hay que ponerla bien a la vista. El primer parámetro es el manejador de la ventana y el segundo el modo como queremos que se vea. La primera vez que se llama a esta función hay que pasarle el cuarto parámetro de WinMain. Si queremos mostrarla de otro modo, hay que llamarla 2 veces.

Los detalles se pueden consultar en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Creo que ya me he enrollado bastante por hoy. Mañana más.

Un saludo.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#5 Mensaje por rir3760 » 08/11/2012 10:01 am

Cuarto mensaje de untio -- Vie Nov 02, 2012 10:38 am

----

Hola de nuevo.

VAMOS AL BUCLE DE MENSAJES:

Código: Seleccionar todo

/* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}
while (GetMessage (&messages, NULL, 0, 0)). GetMessage obtiene un mensaje de la cola de mensajes. Va siguiendo el orden de llegada. El primero en llegar es el primero que obtiene GetMessage.
Los detalles se pueden leer en:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
Los parámetros ni los comento porque los 3 últimos siempre se escriben así. NULL, 0, 0.
Cuando GetMessage obtiene el mensaje WM_QUIT, el bucle acaba y también el programa poco después.

TranslateMessage(&messages): según podéis leer aquí:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
traduce los mensajes de teclas virtuales en mensajes WM_CHAR.

DispatchMessage(&messages). Como podéis leer aquí:
http://msdn.microsoft.com/es-es/library ... 85%29.aspx
Envía el mensaje a la función de ventana.

Cuando llega el mensaje WM_QUIT, el valor de retorno se encuentra en el campo messages.wParam en nuestro caso. Hacemos un return con él y el programa termina.


AHORA EMPIEZA LO BUENO: LA FUNCIÓN DE VENTANA:

Código: Seleccionar todo

/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

Lo de LRESULT indica que el retorno ha de ser de un número de bytes igual al número de bytes que ocupa un puntero a long. Concretamente es un typedef de LONG_PTR (puntero a long). Mejor no calentarse la cabeza con esto. No importa.

CALLBACK indica que esta función no la debemos llamar desde nuestro programa. Será el sistema quien la llame. Después de llamar a DispatchMessage el sistema llama a esta función con ese mensaje.

Hay que tener en cuenta que un mensaje se procesa cuando se ha retornado de esta función después de procesar el mensaje anterior. Si el proceso es muy largo, el programa no responderá mientras dure.

El primer parámetro es el manejador de la ventana que ha recibido el mensaje.

El segundo parámetro es el mensaje. No es más que un entero sin signo que identifica el mensaje.

Los dos últimos parámetros contienen información acerca del mensaje. Dependiendo del mensaje contendrán una cosa u otra. Será necesario consultar su contenido.

El proceso de los mensajes suele ser un switch con el mensaje y un mensaje en cada case.

Hemos de procesar el mensaje WM_DESTROY. En su proceso mandamos el mensaje WM_QUIT (PostQuitMessage(0)). El parámetro a esta función será el valor de retorno de WinMain.

return DefWindowProc (hwnd, message, wParam, lParam)
Nuestra ventana ha de procesar todos los mensajes que recibe, por eso,si no procesamos un mensaje, le pedimos al sistema que le dé un procesamiento por defecto. DefWindowProc se encarga de ello. Le pasamos como parámetros los mismos que hemos recibido.

Creo que ya me he enrollado bastante por hoy. A partir del próximo post, ya pondré código mío. Ahora empieza lo bueno.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#6 Mensaje por rir3760 » 08/11/2012 10:04 am

Quinto mensaje de untio -- Sab Nov 03, 2012 8:30 am

----

Hola de nuevo,

He modificado la plantilla de codeblocks que ha quedado así:

Código: Seleccionar todo

#include <windows.h>

LRESULT OnClose(HWND hw, WPARAM wp, LPARAM lp)
{
    if(MessageBox(hw, "Realmente deseas terminar el programa",
                  "Te pido que lo confirmes", MB_YESNO | MB_ICONQUESTION) == IDYES)
        DestroyWindow(hw);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_CLOSE: return OnClose(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
Un cambio es que cada mensaje se procesa en una función diferente. Los entendidos dicen que una función no debería tener ni menos de 4 líneas ni más de 30 para ir bien. Si hay menos de 4, nos podemos ahorrar la llamada. Si hay más de 30, se hace muy difícil de seguir.

Ya os comenté que cuando se recibe el mensaje WM_DESTROY se ha de llamar a PostQuitMessage. La pregunta es: y si no quiero salir del programa. Pues para eso está el mensaje WM_CLOSE. Si nos interesa acabar el programa llamamos a DestroyWindow, que pone un mensaje WM_DESTROY en la cola de mensajes. Si no nos interesa acabar, pues no hacemos nada.

Otra cosa es que en cada mensaje que procesamos hemos de devolver un valor determinado, en la mayoría es cero, pero no siempre.

He usado la función MessageBox, que se puede consultar aquí:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
para preguntar al usuario si desea salir. El primer parámetro es el manejador de la ventana que estamos usando, el segundo el texto que aparece dentro, el tercero, el que aparece en la barra de título. El cuarto es una serie de constantes que definen qué botones yqué iconos aparecen en él.
Las constantes que representan los valores de retorno también se pueden consultar en ese sitio.

He cambiado la línea:
wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
Para que la ventana tenga un fondo más decente. La otra constante era el color de fondo del escritorio (ya estaba harto del azul ese).

Y he añadido una comprobación por si GetMessage devuelve -1, que es un código de error.

En la función de ventana (la que se llama WindowProc, aunque la podemos llamar como queramos) he retocado un poco para hacerla más compacta. Ya os digo. Hay tendencia a hacer la función de ventana eterna, pero no es un buen sistema. Lo suyo es que las funciones no sean demasiado largas porque así es más fácil no cometer errores.

En fin, creo que por hoy ya está bien. Mañana más.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#7 Mensaje por rir3760 » 08/11/2012 10:06 am

Sexto mensaje de untio -- Dom Nov 04, 2012 7:00 am

----

Hola,

He cambiado un poco el código. Ahí va:

Código: Seleccionar todo

#include <windows.h>
#include <stdio.h>

#define ID_EDIT1 10000

HWND hew;

LRESULT OnCreate(HWND hw, WPARAM wp, LPARAM lp)
{
    RECT rec;
    GetClientRect(hw, &rec);
    hew = CreateWindowEx(0, "edit", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                                    ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
                         rec.left, rec.top, rec.right, rec.bottom,
                         hw,
                         (HMENU) ID_EDIT1,
                         GetModuleHandle(NULL),
                         NULL);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnClose(HWND hw, WPARAM wp, LPARAM lp)
{
    int leng;
    char * datos;
    FILE * fich;
    if((leng = SendMessage(hew, WM_GETTEXTLENGTH, 0, 0)) > 0)
    {
        if(MessageBox(hw, "Deseas guardar el texto?",
                  "Responde esta pregunta:", MB_YESNO | MB_ICONQUESTION) == IDYES)
        {
            datos = (char *) malloc(leng + 1);
            SendMessage(hew, WM_GETTEXT, leng + 1, (LPARAM) datos);
            fich = fopen("mifichero.txt", "wb");
            fwrite(datos, 1, leng, fich);
            fclose(fich);
            free(datos);
        }
    }
    DestroyWindow(hw);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_CREATE: return OnCreate(hw, wp, lp);
        case WM_CLOSE: return OnClose(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           400,
           400,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

En el mensaje WM_CREATE es donde se añaden los controles (o ventanas hijas). He añadido un control edit.

La estructura RECT tiene cuatro campos left, top, width y bottom (izquierda, arriba, ancho y alto. En ella guardo las dimensiones del área cliente de la ventana (el interior una vez descontados los bordes y la barra de título). Después creo el control edit (si os fijáis, la clase de ventana que uso es "edit" (que es una clase predeterminada en el sistema). Como ventana madre pongo la nuestra (ahora no es el escritorio).

Como veis en el estilo de ventana, para cada tipo de control hay unos estilos que sólo se pueden aplicar a ese tipo (además de los comunes a todas las ventanas).

También se puede hacer consultas a los controles mandándoles mensajes (a través de SendMessage). En el mensaje WM_CLOSE le envío un mensaje a nuestro control edit preguntándole el número de caracteres que tiene. Si es más de cero y el usuario quiere guardar el texto, se reserva espacio en memoria para guardarlo y después se obtiene enviando otro mensaje al control edit. Luego se guarda en un fichero.

También veréis que he cambiado el estilo de la ventana madre para que no se pueda cambiar el tamaño. Lo he hecho para hacer el programa más sencillo.

Otra cosa: si llamáis a GetModuleHanle pasandole NULL, os devolverá el manejador de la instancia del programa desde el que la llamáis.

En fin, mejor poquito a poquito. Mañana más.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#8 Mensaje por rir3760 » 08/11/2012 10:09 am

Séptimo mensaje de untio -- Lun Nov 05, 2012 8:17 am

----

Hola de nuevo,

He vuelto a modificar un poco el código:

Código: Seleccionar todo

#include <windows.h>
#include <stdio.h>

#define ID_EDIT1 10000
#define CMD_GUARDAR 10001

HWND hew;

void crearmenu(HWND hw)
{
    HMENU hm, hmarc;
    hm = CreateMenu();
    hmarc = CreateMenu();
    AppendMenu(hmarc, MF_STRING, CMD_GUARDAR, "Guardar");
    AppendMenu(hm, MF_STRING | MF_POPUP, (UINT_PTR) hmarc, "Archivo");
    SetMenu(hw, hm);
}
/////////////////////////////////////////////////////
void guardartexto(HWND hw)
{
    int leng;
    char * datos;
    FILE * fich;
    if((leng = SendMessage(hew, WM_GETTEXTLENGTH, 0, 0)) > 0)
    {
        datos = (char *) malloc(leng + 1);
        SendMessage(hew, WM_GETTEXT, leng + 1, (LPARAM) datos);
        fich = fopen("mifichero.txt", "wb");
        fwrite(datos, 1, leng, fich);
        fclose(fich);
        free(datos);
    }
    else
    {
        MessageBox(hw, "Lo siento", "No hay texto que guardar", MB_OK |  MB_ICONINFORMATION);
    }
}
////////////////////////////////////////////////////////////////
LRESULT OnCommand(HWND hw, WPARAM wp, LPARAM lp)
{
    if(LOWORD(wp) == CMD_GUARDAR)
    {
        guardartexto(hw);
        return 0;
    }
    return 1;
}
///////////////////////////////////////////////////////////////
LRESULT OnSize(HWND hw, WPARAM wp, LPARAM lp)
{
    MoveWindow(hew, 0, 0, LOWORD(lp), HIWORD(lp), TRUE);
    return 0;
}
////////////////////////////////////////////////////////////////
LRESULT OnCreate(HWND hw, WPARAM wp, LPARAM lp)
{
    hew = CreateWindowEx(0, "edit", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                                    ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
                         0,0,0,0,
                         hw,
                         (HMENU) ID_EDIT1,
                         GetModuleHandle(NULL),
                         NULL);
    crearmenu(hw);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnClose(HWND hw, WPARAM wp, LPARAM lp)
{
    if(SendMessage(hew, WM_GETTEXTLENGTH, 0, 0) > 0)
    {
        if(MessageBox(hw, "Deseas guardar el texto?",
                  "Responde esta pregunta:", MB_YESNO | MB_ICONQUESTION) == IDYES)
        {
            guardartexto(hw);
        }
    }
    DestroyWindow(hw);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_COMMAND: return OnCommand(hw, wp, lp);
        case WM_SIZE: return OnSize(hw, wp, lp);
        case WM_CREATE: return OnCreate(hw, wp, lp);
        case WM_CLOSE: return OnClose(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           400,
           400,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
He cambiado el estilo de la ventana. Ahora se puede cambiar su tamaño. Cuando cambiamos el tamaño, la ventana recibe el mensaje WM_SIZE. La descripción la podéis ver en :
http://msdn.microsoft.com/es-es/library ... 85%29.aspx
Aunque, ahora que lo pienso, no voy a poner más referencias a msdn. Para leer los detalles sólo tenéis que buscar el mensaje o la función en Google.

En el mensaje size el parámetro LPARAM contiene en la palabra de menor peso el nuevo ancho y en la de mayor peso el alto. Se obtienen con las macros: LOWORD (la de menor peso) y HIWORD(la de mayor peso).

Que qué es eso de mayor o menor peso. Muy sencillo. Imaginad el númer decimal 1234. 1 tiene mayor peso que los demás (reprensenta 1000) y 4 menor peso que los demás. En los pc una palabra son 2 bytes. En el número hexadecimal 0xFFFFAAAA, la palabra de mayor peso es 0xFFFF y la de menor peso 0xAAAA.

Lo único que hago en ese mensaje es cambiar el tamaño del control edit.

Además, creo un menú. Hay muchas maneras pero esta es de las más cortas. Se define un manejador de menú: HMENU, se llama a CreateMenu y sólo es cuestión de añadir elementos con AppendMenu. El primer parámetro es el manejador que acabamos de obtener, el segundo son unas banderas (unas constantes que indican qué queremos). En el primer caso le digo que el cuarto parámetro es una cadena STRING y en el segundo que el cuarto parámetro es una cadena y el tercero un manejador de menu, que será deplegable a partir de éste POPUP.
El tercer parámetro, para un elemento de menú normal es el identificador que identifica a este elemento y si es para un submenú es su manejador. El último parámetro es la cadena que se verá en el menú.

Después, añadimos el menú a la ventana con SetMenu.

Habréis visto que respondemos también al mensaje WM_COMMAND. Este mensaje es muy socorrido. El sistema lo envía, entre otras ocasiones, cuando se clica en un elemento de menú. En este caso en la palabra de menor peso de WPARAM está el identificador del menú (el tercer parámetro que habíamos pasado a AppendMenu.

Bueno, creo que ya he escrito suficiente hoy.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#9 Mensaje por rir3760 » 08/11/2012 10:10 am

Octavo mensaje de untio -- Mar Nov 06, 2012 6:59 am

----

Hola de nuevo,

He vuelto a cambiar un poquito el código. Ahí va:

Código: Seleccionar todo

#include <windows.h>
#include <stdio.h>

#define ID_TIMER1 10000

LRESULT OnTimer(HWND hw, WPARAM wp, LPARAM lp)
{
    InvalidateRect(hw, NULL, TRUE);
    return 0;
}
//////////////////////////////////////////////////
LRESULT OnPaint(HWND hw, WPARAM wp, LPARAM lp)
{
    HDC hdc;
    PAINTSTRUCT ps;
    SYSTEMTIME st;
    RECT re;
    char cadenahora[200];
    hdc = BeginPaint(hw, &ps);
    GetLocalTime(&st);
    sprintf(cadenahora, "%d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
    GetClientRect(hw, &re);
    DrawText(hdc, cadenahora, strlen(cadenahora), &re, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    EndPaint(hw, &ps);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnCreate(HWND hw, WPARAM wp, LPARAM lp)
{
    SetTimer(hw, ID_TIMER1, 1000, NULL);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    KillTimer(hw, ID_TIMER1);
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_TIMER: return OnTimer(hw, wp, lp);
        case WM_PAINT: return OnPaint(hw, wp, lp);
        case WM_CREATE: return OnCreate(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           400,
           300,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
He añadido un timer con SetTimer. El sistema va a llamar a la ventana (primer parámetro) cada 1000 milisegundos (tercer parámetro), como segundo parámetro pongo un número que identifica el timer y como cuarto parámetro no pongo nada. Este parámetro es por si queremos que se llame a una función en lugar de la ventana. A mí con la ventana ya me va bien.
En el mensaje WM_DESTROY llamo a KillTimer para quitar el timer.

Dije en otro post que escribiríamos y dibujaríamos en la zona cliente de la ventana. Pues para eso respondo al mensaje WM_PAINT.

Cuando el sistema cree que se debe de actualizar el contenido de la zona cliente de la ventana, manda un mensaje WM_PAINT. En muchos programas, es el único mensaje en el que escribiremos o dibujaremos dentro de la ventana.

Pero cómo se hace. Bien, lo primero es obtener el HDC (Handle Device Context) (manejador de contexto de dispositivo). Realmente, todo lo que haremos será con destino al hdc. El sistema se encarga de dibujar en la ventana si el hdc es de una ventana, o en la impresora si el hdc es de una impresora. Es un modo de abstraer el dibujado o escritura. Para dibujar en un dispositivo, sólo hay que obtener el hdc.

Pero eso sí. No os lieis a hacer experimentos con la impresora, porque aún falta un poco.

Es tan común obtener el hdc en el mensaje WM_PAINT que tiene un modo propio de hacerlo. Con BeginPaint. Cuando acabamos de pintar, llamamos a EndPaint. Se les pasa el manejador de ventana y un puntero a una estructura. La estructura no la necesitamos para dibujar, pero se ha de pasar su puntero.

Después obtengo la hora local, la formateo con sprintf y dibujo la cadena centrada en la ventana (fijaos en las banderas que paso a DrawText (hay dos que acaban en CENTER. La fuente que usamos es la fuente por defecto en ese hdc. Hay maneras de cambiarla, pero eso ya lo veremos.

En fin, creo que por hoy es suficiente.

Saludos.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#10 Mensaje por rir3760 » 08/11/2012 10:13 am

Noveno mensaje de untio -- Mie Nov 07, 2012 7:41 am

----

Hola,

Os pido perdón, pero voy a estar sin publicar más código hasta el viernes. Es porque voy a ir muy atareado.

Gracias por vuestra comprensión.

Un saludo.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

#11 Mensaje por rir3760 » 08/11/2012 10:17 am

Listo, esos son todos los mensajes del tutorial.

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
untio
Mensajes: 389
Registrado: 17/09/2008 9:35 am
Ubicación: Provincia de Almería
Contactar:

#12 Mensaje por untio » 09/11/2012 5:44 am

Hola de nuevo,

Bueno, aquí va el código de hoy:

Código: Seleccionar todo

#include <windows.h>
#include <stdio.h>

//////////////////////////////////////////////////
LRESULT OnPaint(HWND hw, WPARAM wp, LPARAM lp)
{
    HDC hdc;
    PAINTSTRUCT ps;
    HBRUSH hbn, hbv;
    HPEN hpn, hpv;
    RECT re;
    hdc = BeginPaint(hw, &ps);
    GetClientRect(hw, &re);
    hbn = CreateSolidBrush(RGB(255, 0, 0));
    hpn = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 40, 40, re.right - 40, re.bottom - 40);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);


    hbn = CreateSolidBrush(RGB(0, 255, 0));
    hpn = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 80, 80, re.right - 80, re.bottom - 80);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);


    hbn = CreateSolidBrush(RGB(0, 0, 255));
    hpn = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 120, 120, re.right - 120, re.bottom - 120);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);



    EndPaint(hw, &ps);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_PAINT: return OnPaint(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           400,
           300,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
Bueno. En el post anterior usábamos la fuente que por defecto tiene el hdc. Ahora cambiamos dos propiedades del hdc. Concretamente cambiamos el brush (se traduciría como brocha) que es el patrón para rellenar el fondo. Además cambiamos el pen (bolígrafo sería una traducción).

Para crear un nuevo brush llamamos a CreateSolidBrush(RGB(255, 0, 0)). Crea un patrón con un color único. En este caso el color rojo.

Para crear el pen usamos CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); El primer parámetro indica que es una línea continua, el segundo que su grosor es 1 punto y el tercero que su color es el rojo.

Después llamamos a SelectObject para cambiar el brush y el pen que viene por defecto en el hdc.

No obstante, una vez hayamos terminado el dibujado, debemos dejar el hdc como estaba antes de empezar. SelectObject nos devuelve el objeto que estaba seleccionado previamente. Guardamos el original y al final lo volvemos a poner como estaba.

Os pido disculpas, la función se podría mejorar poniendo una llamada a una función auxiliar y llamándola 3 veces, pero a veces no puedo resistir la tentación del Copy/Paste.

Uso la función Rectangle. Dibuja la línea con el pen seleccionado y lo rellena con el brush. Hay otras funciones como Ellipse y otras más. Es sólo cuestión de ver qué figura nos interesa.

Bueno, mejor poquito a poquito. Mañana más.

Un saludo.
Última edición por untio el 14/11/2012 8:11 am, editado 1 vez en total.

Avatar de Usuario
untio
Mensajes: 389
Registrado: 17/09/2008 9:35 am
Ubicación: Provincia de Almería
Contactar:

#13 Mensaje por untio » 10/11/2012 4:52 am

Hola de nuevo,

He cambiado algo el código y, además, he añadido un nuevo fichero:

El fichero cpp:

Código: Seleccionar todo

#include <windows.h>
#include <stdio.h>

//////////////////////////////////////////////////
LRESULT OnPaint(HWND hw, WPARAM wp, LPARAM lp)
{
    HDC hdc;
    PAINTSTRUCT ps;
    HBRUSH hbn, hbv;
    HPEN hpn, hpv;
    RECT re;
    hdc = BeginPaint(hw, &ps);
    GetClientRect(hw, &re);
    hbn = CreateSolidBrush(RGB(255, 0, 0));
    hpn = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 40, 40, re.right - 40, re.bottom - 40);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);


    hbn = CreateSolidBrush(RGB(0, 255, 0));
    hpn = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 80, 80, re.right - 80, re.bottom - 80);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);


    hbn = CreateSolidBrush(RGB(0, 0, 255));
    hpn = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
    hbv = (HBRUSH) SelectObject(hdc, hbn);
    hpv = (HPEN) SelectObject(hdc, hpn);
    Rectangle(hdc, 120, 120, re.right - 120, re.bottom - 120);
    SelectObject(hdc, hbv);
    SelectObject(hdc, hpv);

    //Reeditado. Hay que liberar los recursos usados:
    DeleteObject(hbn);
    DeleteObject(hpn);



    EndPaint(hw, &ps);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT OnDestroy(HWND hw, WPARAM wp, LPARAM lp)
{
    PostQuitMessage(0);
    return 0;
}
/////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProcedure (HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_PAINT: return OnPaint(hw, wp, lp);
        case WM_DESTROY: return OnDestroy(hw, wp, lp);
        default: return DefWindowProc (hw, msg, wp, lp);
    }
}
//////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASSEX wincl;
    CHAR clasname[] = "EjemploRinconDelC";
    BOOL ret;
    /* Para no andar asignando valores del tipo
       NULL, FALSE o 0, lo que se suele hacer con
       las estructuras es poner todos sus campos
       a cero con la macro ZeroMemory.
       Se le pasa la dirección de la estructura y
       su tamaño.*/
    ZeroMemory(&wincl, sizeof(WNDCLASSEX));
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = clasname;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (hThisInstance, "ICO32");
    wincl.hIconSm = LoadIcon (hThisInstance,    "ICO16");
    wincl.hCursor = LoadCursor (hThisInstance, "MICURSOR");
    /*Si leéis la definición de hbrBackground veréis que para
      usar uno de los colores predefinidos hay que sumarle 1
    */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    if (!RegisterClassEx (&wincl))
        return 0;
    hwnd = CreateWindowEx (
           0,
           clasname,
           "Ejemplo de rincon del C",
           WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           400,
           300,
           HWND_DESKTOP,
           NULL,
           hThisInstance,
           NULL
           );
    if(!hwnd)
        return 0;
    ShowWindow (hwnd, nCmdShow);
    while ((ret = GetMessage (&msg, NULL, 0, 0)))
    {
        if(ret == -1)
            return 0;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
Y el nuevo fichero que tiene la extensión .rc:

Código: Seleccionar todo

#include <windows.h>


ICO16 ICON "16.ico"
ICO32 ICON "32.ico"

MICURSOR CURSOR "cur.cur"
Sí, vamos a cambiar los iconos y el cursor de la ventana. Para ello vamos a descargar un editor de iconos. Puede hacerse con cualquiera. Yo he descargado el que he usado (freeware naturalmente) desde:

http://www.x2studios.com/OSX_Win_Products.html

Sólo tenéis que hacer scroll hacia abajo y clicar en el enlace que dice: LiquidIcon XP v1.0.4 build #2616

Una vez descargado, lo ejecutáis y seleccionáis File/New icon/16x16 y dibujáis. Lo guardáis en la misma carpeta del proyecto de codeblocks (donde está el fichero main.cpp). Después lo mismo con un icono de 32x32 y lo volvéis a guardar. En tercer lugar hacéis otro de 32x32, pero no lo guardáis como icono, en su lugar seleccionáis Save as Cursor (guardar como cursor). Se os presenta una ventana que os dice que digáis cuál es el punto que equivale a la punta de la flecha. Clicáis en él y ya tenemos los tres ficheros de imagen en carpeta del proyecto.

En CodeBlocks seleccionamos File/New/Empty file y guardamos el nuevo fichero con la extensión .rc, fichero de recursos. En ese fichero añadimos:
#include <windows.h>
Para las futuras referencias.
ICO16 ICON "16.ico"
ICO16 es el nombre que le he dado a ese icono. Puede ser cualquiera.
ICON le indico al compilador de recursos que es un icono.
"16.ico" es la ruta al icono. En micaso está en la misma carpeta del proyecto y se llama 16.ico

ICO32 ICON "32.ico" Lo mismo pero con el icono de 32x32 píxeles.

MICURSOR CURSOR "cur.cur" Aquí he llamado MICURSOR al cursor que he definido.
CURSOR indico que voy a incluir un cursor.
"cur.cur" Mi fichero se llama cur.cur y está en la misma carpeta del proyecto.

Además, he hecho unos cambios en el fichero cpp dentro de WinMain:

Código: Seleccionar todo

    wincl.hIcon = LoadIcon (hThisInstance, "ICO32");
    wincl.hIconSm = LoadIcon (hThisInstance,    "ICO16");
    wincl.hCursor = LoadCursor (hThisInstance, "MICURSOR");
A LoadIcon le paso la instancia del ejecutable que tiene el icono. El icono se añadirá a nuestro ejecutable al enlazar. Le paso la instancia que recibimos como parámetro de WinMain. Además, le indico (como cadena de caracteres) el nombre que le hemos dado en el fichero de recursos.

Tenéis que tener en cuenta que en un ejecutable de windows suele haber más que sólo código y datos.

Lo mismo con LoadCursor.

Nuestro ejecutable tiene su propio icono y nuestra ventana también. Además, hemos cambiado el cursor del ratón.

En fin. Por hoy creo que es suficiente.

Saludos.
Última edición por untio el 14/11/2012 8:10 am, editado 1 vez en total.

Avatar de Usuario
leosan
Mensajes: 730
Registrado: 19/04/2012 8:35 am
Ubicación: GRAN CANARIA

#14 Mensaje por leosan » 10/11/2012 5:12 am

Aunque ya te di las gracias por la dedicación, al borrarse el mensaje las repito: GRACIAS.
¿El nuevo fichero de extensión .rc cómo se llama?.
Muy emocionante el código de poder escribir en la ventana. Por cierto, al modificar un poco los comandos, me dí cuenta que era una ventana dentro de otra. Sólo queda poder tener la opción "guardar como", pero supongo que ya lo veremos.
En este último post el código me salta con multitud de errores ¿¿???:

C:\Users\Administrador\Documents\C\TEMP\Untitled14.c|4|warning: C++ style comments are not allowed in ISO C90|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.c|4|warning: (this will be reported only once per input file)|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreateSolidBrush@4'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreatePen@12'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `Rectangle@20'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreateSolidBrush@4'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreatePen@12'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `Rectangle@20'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreateSolidBrush@4'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `CreatePen@12'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `Rectangle@20'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
C:\Users\Administrador\Documents\C\TEMP\Untitled14.o:Untitled14.c|| undefined reference to `SelectObject@8'|
||=== Build finished: 21 errors, 2 warnings ===|

Saludos!. ... y seguimos aquí siguiendo con sumo interes tus "lecciones". Sólo espero que más "lectores" se apunten añadiendo más ideas o dudas.

Avatar de Usuario
untio
Mensajes: 389
Registrado: 17/09/2008 9:35 am
Ubicación: Provincia de Almería
Contactar:

#15 Mensaje por untio » 10/11/2012 8:49 am

Hola,

Voy escribiendo el tutorial con CodeBlocks. El nombre del fichero con la extensión .rc no importa, aunque ha de ser añadido al proyecto.

Los errores que te suelta el linker vienen dados porque te falta incluir la librería gdi32.lib ó libgdi32.a para mingw. A través de gdi (Graphics Device Interface) es como se hace el dibujado en la zona cliente.

En cuanto al fichero de recursos, su nombre no importa, lo que sí ha de ser correcto es la ruta a los iconos y el cursor. El compilador de recursos creará un fichero con la extensión .res (resources (recursos)) que contendrá los iconos, bitmaps, definiciones de cuadros de diálogo, etc.

Es mejor seguir este tutorial con CodeBlocks porque si lo haces, por ejemplo, con Visual C++, cuando se traten cadenas de caracteres muy probablemente falle la compilación porque ese entorno por defecto trabaja con unicode.

Pero recuerda que lo que te pasa es que has de añadir la librería gdi32 al enlazador. Puede tener un nombre como Gdi32.lib ó libgdi32.a según tu entorno.

Saludos.

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado