[Tutorial] Programar para Windows

Favas

Power Member
Objectivo:
O objectivo deste tutorial é subir um escalão na tua programação em C, muitos de nós sabe C mas limita-se a programar uns printf() e scanf() para a consola de DOS. Neste tutorial vou tentar mudar isso, vou-te mostrar como fazer um programa numa janela de Windows, com botões e caixas de texto. Tudo programado à mão, sem recorrer a resources, assistentes de desenho ou libs. Não é facil programar a este nível, vou precisar muito da tua atenção e paciência.

Requesitos:
• O programa Dev-C++ que podem sacar aqui: http://prdownloads.sourceforge.net/dev-cpp/devcpp-4.9.9.2_setup.exe
É um IDE para programar C/C++ muito simpático e userfriendly, não tem muitas mariquices, é o ideal!
• Conhecimentos básicos de programação por objectos.
• Conhecimentos básicos de Windows.
Ex: o que é: uma EDIT box, um BUTTON ou LABEL?
• Conhecimentos básicos de C.
Ex: o que faz a função: "int soma(int x, int y){return (x-y);}" ?
• Motivação para aprender/explorar

Ao conseguires sem dificuldades responder a estas duas perguntas, estás pronto para seguir o tutorial sem qualquer esforço.
Se não conseguires, epá não sabes nada de C :( .. isto não é nada do outro mundo, mas terás que te esforçar um pouco mais, mas por favor, evita de postar dúvidas básicas de C, cria uma thread, há mais threads ou perguntem por PM. obrg.

Programa:
O programa será uma simples calculadora básica, vai usar um unico ficheiro *.cpp, nada de resources, libs malucas ou dlls, irá ter apenas 3 páginas A4, e nessas 3 páginas de código vou por botões dar-lhes instruções, caixas de texto, vai ter que caber lá tudo!

O aspecto final vai ser este:
prog5yl.jpg

Simples, hein?

Vais começar por:
Instalar o Dev-C++ e preparar o programa.


1º - Vais instalar o programa Dev-C++ (deverás conseguir sozinho :o )

2º – Executas o programa, e vais criar um novo projecto e escolher o destino. (Windows Application; C++ Project, [OK])
tut2kr.jpg


3º - Vais selecionar o codigo já existente no projecto criado e apagar tudo! Vamos começar do ZERO.
tut29jp.jpg


OK está tudo pronto para começares a codar a tua calculadora.
 
Última edição:
Começar a programar!

Parte 1: a tua Janela principal



Vais copiar/colar este código na parte de código que está em branco.

Código:
#include <windows.h>
#include <stdlib.h>

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

//função WinMain
int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)
{
    HWND hwnd;                                                          //handle
    MSG messages;                                                       //mensagem
    WNDCLASSEX wincl;                                                   //classe

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = "WindowsApp";
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;          
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);                     
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);                   
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);                       //cursor default
    wincl.lpszMenuName = NULL; 
    wincl.cbClsExtra = 0;  
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;                    //cor do background

    if (!RegisterClassEx (&wincl))
        return 0;

    hwnd = CreateWindowEx (                                             //criar a janela
        0,                                                              
        "WindowsApp",        
        "Calculadora",                                                  //titulo da janela
        WS_OVERLAPPEDWINDOW,                                            //estilo da janela
        350,                                                            //X em relação ao desktop
        250,                                                            //y em relação ao desktop
        300,                                                            //largura da janela
        100,                                                            //altura da janela
        HWND_DESKTOP,                                                   
        NULL,              
        hThisInstance,      
        NULL                
        );

    ShowWindow (hwnd, nFunsterStil);                                    //mostrar a janela

    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    
    return messages.wParam;
}

/*
Parte 2 do Tut
*/


/* //Parte 3 do Tut
char s_valor1[20] = "0", s_valor2[20] = "0", s_total[20] = "0";
int valor1, valor2, total; */

//função WindowProcedure
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE:
             //Parte 2 do Tut
             //DesenharObjectos(hwnd,message,wParam,lParam);
        break;    
        case WM_COMMAND:
             /*
             Parte 3 do Tut
             */
        break;
        case WM_DESTROY:
            PostQuitMessage (0);
        break;
        default:                     
            return DefWindowProc (hwnd, message, wParam, lParam);
    }//switch (message)
    return 0;
}

Faz Compile & Run (F9) para ver se o programa funciona. tks :007:

Neste programa vais usar apenas dois includes, o <windows.h> te dá o acesso ao API do Windows (sem ele não há janelinhas pra ninguém)
O <stdlib.h> está aqui apenas por lógica, vamos utilizar as funções itoa () e atoi() desse include, o compilador funciona na mesma sem ele, mas não custa nada incluir.

Se reparares, para fazer uma janela de Windows, geralmente são precisas duas funções; WinMain e WindowProcedure.

A função WinMain é o inicio, é néla que se cria a estrutura WNDCLASS, preenche-se com dados e se regista RegisterClassEx (&wincl). (ok não, percebeste? Não faz mal..)
Há muito a dizer sobre esta função, mas vamos imaginar que é um algo já adquirido “é assim que se faz” e vais usar a função tal como ela está, sem mexer em nadinha. (por agora hihi..)
O que deves saber é que a função WinMain vai criar uma janela de Windows com botões minimizar, maximizar etc, vai também torna-la visivel (duh) e quando o utilizador interagir com essa janela ela vai executar a função WindowProcedure.

Por sua vez, a função WindowProcedure é a nossa função de trabalho, é lá que vamos saber se o palhaço que está por detraz do computador (user) clicou no botão A ou B, e se ele fez resize ou clicou no [X] que fecha a janela. Tudo isto com apenas 4 variáveis que a função nos dá, hwnd, message, wParam e lParam.

hwnd é o manipulador, a nossa janela principal.

message informa-te qual dos comandos o user activou sobre a nossa janela mãe, entre muitos comandos esquisitos, apenas te interessam três: WM_CREATE, WM_COMMAND e WM_DESTROY.
caso a “message” for
WM_CREATE - é porque a janela principal foi criada, não confudir “janela criada” com “janela visivel”
WM_COMMAND - é porque alguém interagiu com um objecto dentro da janela, botão, caixa de texto, image, scrollbar etc
WM_DESTROY - informa que a janela foi fechada.

wParam divide-se em duas partes, sendo de 32bits divíde-se em duas words de 16bits, e assim conseguimos extrair duas informações
a parte alta “HIWORD(wParam)” dá-te uma submensagem do WM_COMMAND (ex: BN_CLICKED quando alguem clicar num botão)
a parte baixa “LOWORD(wParam)” identifica o objecto usado pelo user com um numero. (ex. 2400 quando alguem clicar no botão com id=2400)

lParam é usada juntamente com o GetLParam() e não a vamos útilizar.

Já deves ter reparado que o código que te dei tem um switch a filtrar estes três comandos, o WM_CREATE e o WM_COMMAND (ignorando os comentários) estão vazios, mas o WM_DESTROY já tem uma linha a mandar “PostQuitMessage (0);”.
Eu fiz isto, porque não faz sentido quando o user clica no X para fechar a janela, o programa continuar em background a consumir memória sem fazer nada.
Por sua vez, o WM_CREATE não tem nada porque ainda não colocas-te nada dentro da janela, se quizeres por um button a dizer TechZonePw0ns é no WM_CREATE que deves colocar.
Sem haver objectos para interagir dentro da janela, também não faz sentido o WM_COMMAND ter algo, para fazer com que ao clicar no botão da TechZonePw0ns o computador rebente, este é o sitio certo. (WM_COMMAND rula =P)

A linha “return DefWindowProc (hwnd, message, wParam, lParam);” que está em default: é algo que temos sempre de fazer. Não se mexe.

E estamos prontos para a Parte: 2.

Nota. O que fazem as variáveis ou comandos tipo WM_BLABLA, BS_XUPAMUS tu não tens que saber! Um programador por muito bom que seja não tem que saber tudo de cor, há sites que explicam tudinho por A mais B, o que faz X, e como se faz Y. Por muito pequeno que seja um programa, não é possível codar sem a consulta. remember that =)
Depois do Tut dou sites referencia com muita decumentação.
 
Última edição:
Desenhar o conteudo da janela

Parte 2: Objectos

Mais uma vez vais copiar/colar este código para a parte do código onde diz /* Parte 2 do Tut */“ mesmo por cima da função WindowProcedure

Código:
#define ID_BUTTONmais 1001
#define ID_BUTTONmenos 1002
#define ID_BUTTONvezes 1003
#define ID_BUTTONdividir 1004

HINSTANCE g_inst;
HWND EditNum1,EditNum2,EditTotal,ButtonMais,ButtonMenos,ButtonVezes,ButtonDividir;

//esta função só serve para criar o conteudo dentro da janela principal (nao tem retorno)
//irá criar duas EDITs e 4 BUTTONS, e mudar os respectivos tipos de letra de cada um
void DesenharObjectos(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    EditNum1 = CreateWindowEx (
        WS_EX_CLIENTEDGE,  
        "EDIT",        
        "5",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        30, 30, 50, 20,                 
        hwnd,       
        NULL,           
        g_inst,      
        NULL                 
        );
           
    EditNum2 = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "3",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        160, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

    EditTotal = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        220, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

    ButtonMais = CreateWindowEx (
        0,  
        "BUTTON",        
        "+",       
        WS_VISIBLE|WS_CHILD, 
        80, 30, 20, 20,                
        hwnd,    
        (HMENU)ID_BUTTONmais,          
        g_inst,      
        NULL           
        );
           
    ButtonMenos = CreateWindowEx (
        0, "BUTTON", "-",       
        WS_VISIBLE|WS_CHILD, 
        100, 30, 20, 20,             
        hwnd, (HMENU)ID_BUTTONmenos, g_inst, NULL);
           
    ButtonVezes = CreateWindowEx (
        0, "BUTTON", "*",       
        WS_VISIBLE|WS_CHILD, 
        120, 30, 20, 20,             
        hwnd, (HMENU)ID_BUTTONvezes, g_inst, NULL);
           
    ButtonDividir = CreateWindowEx (
        0, "BUTTON", "/",       
        WS_VISIBLE|WS_CHILD, 
        140, 30, 20, 20,
        hwnd, (HMENU)ID_BUTTONdividir, g_inst, NULL);
    
    CreateWindowEx (
        0, 
        "STATIC", 
        "=",       
        WS_VISIBLE|WS_CHILD, 
        212, 31, 5, 20,
        hwnd, 
        NULL, 
        g_inst, 
        NULL
        );
    
    SendMessage(
        (HWND) EditNum1,
        (UINT) WM_SETFONT, 
        (WPARAM) GetStockObject(DEFAULT_GUI_FONT),
        (LPARAM) lParam
        );	  
           
    SendMessage((HWND) EditNum2,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    SendMessage((HWND) EditTotal,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    
    SendMessage(
        (HWND) ButtonMais,
        (UINT) WM_SETFONT, 
        (WPARAM) GetStockObject(DEFAULT_GUI_FONT),
        (LPARAM) lParam
        );	
           
    SendMessage((HWND) ButtonMenos,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);
    SendMessage((HWND) ButtonVezes,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    SendMessage((HWND) ButtonDividir,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);        	
}
Vais também tirar as duas barras "//" da função "DesenharObjectos(hwnd,message,wParam,lParam);" na função WindowProcedure dentro do “case WM_CREATE:” para que ela possa ser executada.
(F9 pra ver se está tudo em ordem. tks)

É tudo.

Ao executares o programa, reparas que a calculadora tem agora 4 botões, 3 edits e 1 label.
Vamos começar pelo princípio.
A janela principal foi criada pela função WinMain, esta executou a função WindowProcedure e enviou-nos a “message” de WM_CREATE que significa que a janela acabou de ser criada, tal como seria de esperar.
Tu com um switch filtraste o momento de quando ela foi criada “case WM_CREATE:” então vais aproveitar esse momento de criação para desenhar os botões, edits etc.
Como a parte de desenhar objectos é algo extensa onde 90% do código é repetido, eu decidi criar uma outra função para nao “sujar” a nossa função de trabalho que é a WindowProcedure.
A técnica que usei foi criar uma função sem retorno (procedimento em pascal) a função vai-se chamar DesenharObjectos() e é dentro dela que vais desenhar tudo, se reparas ela a função transporta as 4 variáveis que já expliquei na Parte 1 do Tut, ela faz esse transporte porque convem levares contigo as ferramentas se vais trabalhar para outro local..
Então a função “DesenharObjectos(hwnd,message,wParam,lParam);” apenas te transporta para outro lado ("lá pra cima" neste caso).

Vais então analizar o novo código com atenção.

Antes da função DesenharObjectos() à sempre que declarar as variáveis, neste caso vou declarar os objectos com que vou trabalhar:
Aqui estão os nomes dos teus 3 edits e dos 4 butoes
“HWND EditNum1,EditNum2,EditTotal,ButtonMais,ButtonMenos,ButtonVezes,ButtonDividir;”
nomes2ko.jpg


Vais passar agora para dentro da função DesenharObjectos()
(sim eu sei, saltei a instrução “HINSTANCE g_inst;” de prepósito)
Código:
void DesenharObjectos(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
logo no principio da função tens isto

Código:
    EditNum1 = CreateWindowEx (
           WS_EX_CLIENTEDGE,  
           "EDIT",        
           "5",       
           WS_VISIBLE|WS_CHILD|WS_BORDER, 
           30, 30, 50, 20,                 
           hwnd,       
           NULL,           
           g_inst,      
           NULL                 
           );
Este pequeno código vai-te criar uma editbox, o processo de criação é igual para quase todos os objectos, já te mostro de perto.

Código:
EditNum1 = CreateWindowEx(
esta linha diz que EditNum1 vai ser igual a um objecto criado pela função CreateWindowEx()
A função CreateWindowEx para ser executada precisa sempre de 12 parametros que são os seguintes (por ordem).

CreateWindowEx(
1º - !Aparencia do objecto! neste caso é “WS_EX_CLIENTEDGE” que dá o efeito 3d ao edit, experimenta colocar um 0 em vez do “WS_EX_CLIENTEDGE” e corre o programa, ficou flat nao foi? (depois de modificares convem colocar como estava para tudo funcionar como pretendo)

2º - !Classe do objecto! Neste caso é “EDIT” porque queremos uma editbox, se quizessemos um botão teriamos que por “BUTTON", experimenta colocar “BUTTON” como 2 parametro. Éra de esperar, transformou-se num botão.

3º - !Texto default do Objecto! Neste caso o default é “5” experimenta por algo do tipo “rotfl”. Compile & Run

4º - !Aparencia especifica do objecto! Neste caso usa 3 constantes, WS_VISIBLE|WS_CHILD|WS_BORDER, WS_VISIBLE faz com que o objecto seja visivel, WS_BORDER faz que tenha uma borda/moldura neste caso 3d, WS_CHILD informa que o objecto está dentro de uma janela. Todas estas constantes sao separadas por uma barra “|” para poder funcionar. Experimenta adicionar ES_RIGHT e vais reparar que ele alinhou o texto à direita. Lembra-te que podes adicionar o que quiseres, 2, 5 ou 10 aparencias malucas, tem é de ter sempre a barra a separar!

5º - !Distancia da esquerda! esquerda ou direita, se tiveres habituado a programação por objectos, fazes isto com alguma facilidade.

6º - !Distancia do topo! Estas coordenadas são sempre em pixels, se alterares os valores vais fazer com que o edit se mova pra cima ou para baixo

7º - !Largura!

8º - !Altura!

9º - !Janela em que vai ser criado o objecto! Na Parte 1 do tutorial já te mostrei que hwnd era o ponteiro da nossa janela, como tu vais querer colocar o objecto dentro da tua janela mãe, só tens que indicar o apontador aqui “hwnd” (não há brincadeiras com este parametro =P)

10º - !Numero de identificação! Ora bem neste caso é Null, ou seja, não foi atribuido nenhum numero ID a esta edit, isto porque não é necessário. Fica assim.

11º - !instancia! lembras-te de eu saltar o “HINSTANCE g_inst;” na secção das variaveis? É aqui que devemos por o “g_inst” que é o manipulador da instancia associada à nossa janela (não percebeste? Eu tb nunca percebi mt bem lol...)

12º - !lparam! tb nao sei mas é sempre NULL.
);

É assim que se cria um objecto, agora segue um pouco mais o código com atenção.

Código:
    EditNum2 = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "3",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        160, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

    EditTotal = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        220, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

Os objectos EditNum2 e EditTotal são editboxs iguaizinhas a esta que acabamos de criar, só muda o nome e as coordenadas onde vao ser desenhadas (porque senão ficavam todas umas em cima das outras, sim, tambem muda o texto default (3º parametro) que já me tava a esquecer..)
O aspecto diferente do código deve-se a eu ter compactado vários parametros na mesma linha (para não ocupar tanto) hàs-de reparar que estão lá todos os 12 parametros separados por virgulas ",".

Vais seguir again o código até encontrares “ButtonMais = CreateWindowEx (“

Código:
    ButtonMais = CreateWindowEx (
        0,  
        "BUTTON",        
        "+",       
        WS_VISIBLE|WS_CHILD, 
        80, 30, 20, 20,                
        hwnd,    
        (HMENU)ID_BUTTONmais,          
        g_inst,      
        NULL           
        );

As anteriores eram “EDIT”, estes agora são “BUTTON”, o processo de criação é igual, mas neste caso o botão não precisa de WS_BORDER no 4º parametro, nem "WS_EX_CLIENTEDGE" no 1º como as editboxs, MAS tem um Número de Identificação no 10º!
Vou-te explicar o que é o Número de Identificação (ID).
O ID é um número que se atribui a um objecto, a atribuição é feita da seguinte maneira “(HMENU) NumeroAqui” neste caso defeni que seria 1001. (se reparares eu no inicio do programa defeni que “ID_BUTTONmais” seria igual a “1001”;“#define ID_BUTTONmais 1001”)

Costuma-se usar “#define QualquerCoisa ID” porque é mais facil para programar com nomes do que com números. Este é um programa pequeno, sei que há 4 butoes com os IDs 1001 para o ButtonMaiso, 1002 para o ButtonMenos etc,etc, mas num programa grande convem sempre usar o #define para facilitar.
Código:
#define ID_BUTTONmais 1001
#define ID_BUTTONmenos 1002
#define ID_BUTTONvezes 1003
#define ID_BUTTONdividir 1004
ids6fh.png


O número ID é muito bonito mas para que serve? Perguntas tu...
Quando o botão ButtonMais é clicado o Windows envia-nos as informações BN_CLICKED na variavel HIWORD(wParam) e o ID na LOWORD(wParam), BN_CLICKED é quando o user clica num botão, e se ele enviar também 1001 é porque o ButtonMais foi clicado, 1002 foi o ButtonMenos, 1003 foi o ButtonVezes etc.
Resumindo: se ((HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == 1001)) é porque alguém clicou no botão de mais.

Se continuares a seguir o código, reparas que só mudam os nomes das variáveis, os IDs e o texto default (claro também mudam as coordenadas)

Segue então mais uma vez no código, e pára na “CreateWindowEx (“ que não foi compactada por mim). Esta diz "STATIC", no 2º parametro não é?
Código:
    CreateWindowEx (
        0, 
        "STATIC", 
        "=",       
        WS_VISIBLE|WS_CHILD, 
        212, 31, 5, 20,
        hwnd, 
        NULL, 
        g_inst, 
        NULL
        );
Sabes que “BUTTON” faz botões, sabes que “EDIT” faz editboxs, mas “STATIC” é novo!
“STATIC” é conhecido por Label, na tua calculadora esta label é o sinal de igual “=”, é um objecto que não serve para nada, apenas aparece para fazer sentido ao interface da tua calculadora. Como este “STATIC” não vai fazer nada, não temos que atribuir variável alguma, nem tanto um ID (é serve para o interface ficar bonito).

Toca a seguir mais no código.

Código:
    SendMessage(
        (HWND) EditNum1,
        (UINT) WM_SETFONT, 
        (WPARAM) GetStockObject(DEFAULT_GUI_FONT),
        (LPARAM) lParam
        );

Antes de saber para que serve este pequeno código temos que ter a noção que:
o Windows comunica com o nosso programa básicamente atravez de mensagens, enviam-se mensagens, recebe-se mensagens, a função SendMessage() é composto por 4 parametros:

SendMessage(
1º - !Objecto destino! Este é o objecto a que é destinado a mensagem, no código a cima podemos ver que é o EditNum1

2º - !Mensagem enviada! Conforme a mensagem enviada, faz o objecto reagir de formas diferentes, no nosso caso a mensagem é WM_SETFONT, ou seja
o objecto vai trocar o seu tipo de letra, e o novo tipo de letra será enviado atravez do 3º parametro (wParam)

3º - !wParam! Estamos no 3º parametro, e tu deves tar a perguntar “HEI como é que sabes que o tipo de letra tem de ir como 3º parametro e como sabes que WM_SETFONT muda de letra??” é simples, MSDN mais própriamente aqui -> http://msdn.microsoft.com/library/d...windowreference/windowmessages/wm_setfont.asp
Como já disse em Nota: não há programas sem consulta, deves conhecer bem os locais a consultar e ganhar hábito em consultas, só um doido é que sabe de cor como reage um objecto a cada mensagem diferente!

4º - !lParam! este parametro não serve para nada segundo diz o MSDN, por isso vamos reenviar o próprio lParam.
);

Pois é, esta SendMessage() faz mudar o tipo de letra da caixa de texto EditNum1, a nova letra que vais usar é o tipo de letra DEFAULT_GUI_FONT (tipo default do windows).
Sim, é preciso dizer ao Windows que o tipo de letra que queremos é o default do Windows, experimenta tirar o “(WPARAM) GetStockObject(DEFAULT_GUI_FONT),” e colocar “(WPARAM) 0,”, vês logo porque é que eu troquei de letra, pois o Windows por defeito usa FONTS HORRIVEIS!!!!!

A função GetStockObject() é uma função muito simpática que existe, ela retorna as coisas mais usadas do Windows. Por exemplo
GetStockObject(DEFAULT_GUI_FONT) como já sabes, é a font default do windows
GetStockObject(GRAY_BRUSH) é cinzento.
GetStockObject(DEFAULT_PALETTE) é a palete default do windows
entre outras coisas.
Se existisse esta função GetStockObject() terias que criar uma variável do tipo fontl, e atribuir-lhe uma fonte;
Código:
HFONT Minhaletra;
Minhaletra = CreateFont(
           24,
           0,
           0,
           0,
           FW_BOLD,
           FALSE,
           FALSE,
           FALSE,
           ANSI_CHARSET,
           OUT_CHARACTER_PRECIS,
           CLIP_CHARACTER_PRECIS,
           PROOF_QUALITY,
           VARIABLE_PITCH | FF_DONTCARE,
           "Comic Sans MS"
           );

Por isso! Um bem haja ao GetStockObject() que nos livra de tal trabalheira!
(podes consultar GetStockObject() no MSDN, alias o MSDN tem tudo! é preciso é saber procurar)

O resto do código até ao terminar da função DesenharObjector() apenas muda os tipos de letras aos restantes objectos da nossa janela, não deves ter dificuldades em decifrar.

Parte 2: Terminada
 
Última edição:
Capturar clicks, guardar e enviar strings para Edits

Parte 3: Processar os Objectos


Como é habitual vou dar mais umas linhas de código (ultimas linhas de código!!!)

Vais copiar/colar onde está escrito "/* Parte 3 do Tut */" dentro do "case WM_COMMAND:" na WindowProcedure():

Código:
            if ((HIWORD(wParam) == BN_CLICKED))
            {   
                
                SendMessage(
                    (HWND) EditNum1,
	                (UINT) EM_GETLINE,
	                (WPARAM) 1, 
	                (LPARAM) &s_valor1 
                    ); 
                     
                SendMessage((HWND)EditNum2,(UINT)EM_GETLINE,(WPARAM)1,(LPARAM) &s_valor2);
                 
               	valor1 = atoi(s_valor1);
                valor2 = atoi(s_valor2); 
                 
                switch (LOWORD(wParam))
                {
                    case ID_BUTTONmais:  
                        total = valor1+valor2;
                    break;
                    case ID_BUTTONmenos:        
                        total = valor1-valor2;                     
                    break;
                    case ID_BUTTONvezes:        
                       total = valor1*valor2;                     
                    break;
                    case ID_BUTTONdividir:        
                        total = valor1 / valor2;                     
                    break;
                }    //switch (LOWORD(wParam))
                
                itoa (total,s_total,10);
                     
                SendMessage(
	                (HWND) EditTotal,
	                (UINT) WM_SETTEXT, 
	                (WPARAM) 0,
	                (LPARAM) &s_total
                    );
                    
            }    //if ((HIWORD(wParam) == BN_CLICKED))

Vais também tirar as barras de comentários "/* */" nas variáveis antes da WindowProcedure() para que possam ser executadas. E é tudo!
Podes agora Compilar & Run para ver se está tudo OK. Calculadora terminada e a fazer contas!

Pois é, vamos passar a analizar o código a ver o que ele realmente faz.

O que tens feito até agora foi:
Na Parte: 1, criaste a tua janela mãe.
Na Parte: 2, criaste o conteúdo dentro da tua janela mãe (edits, botões e a tal label)
Na Parte: 3, vais fazer com que os teus objectos reajam conforme os clicks do user que está ao computador.

A calculadora é simples e de fácil interpretação, mas aqui vai: o user vai coloca o primeiro valor na EditNum1, vai também colocar um segundo valor na EditNum2 e ao clicar no botão da operação que quer, o resultado irá aparecer na EditNum3! Bué básico!

calculadora8bb.png


Como a calculadora é super simples, só há 4 coisas que ela tem de fazer, que são: somar EditNum1 + EditNum2, subtrair EditNum1 – EditNum2, multiplicar EditNum1 * EditNum2, e dividir EditNum1 / EditNum2. Só!

Antes de analizar o código quero sintetizar umas coisas.
O user ao clicar num dos quatro botões, o nosso programa vai executar a tua função de trabalho WindowProcedure() e ao executar essa função vai-te informar do seguinte:
Se a variável “message” for WM_COMMAND é porque o user fez qualquer coisa a um dos objecto dentro da tua janela mãe.
Se a HIWORD(wParam) for igual a BN_CLICKED é porque essa “qualquer coisa” que o user fez, foi clickar num dos botões.
Se a LOWORD(wParam) for igual a 1002 é porque o botão clicado foi o ButtonMenos.

Agora vais atacar o problema comigo, seguindo o código dentro da função WindowProcedure()

Vais parar já nesse “switch (message)”, aqui só te interessa quando a “message” é igual a WM_COMMAND, por isso passas já para dentro da “case: WM_COMMAND” e começas a analizar as novas linhas de código que te dei nesta Parte 3 do Tut.

Neste momento já sabes que o user fez “qualquer coisa” a um dos objecto dentro da tua janelinha, agora só tens que identificar o que é que o gajo fez a esse objecto, e a variável HIWORD(wParam) informa-te isso.
HIWORD(wParam) pode ser muita coisa! HIWORD(wParam) pode ser igual a BN_PUSHED, BN_PAINT, BN_UNPUSHED, e BN_CLICKED, mas o que te interessa é quando ela é igual a “BN_CLICKED” ou seja, quando o user clica num objecto.
Então: if ((HIWORD(wParam) == BN_CLICKED)) vai-te filtrar os clickes todinhos.

Mais uma etapa concluida, todo o código que escreveres dentro deste “if” só vai ser executado se o user clicar (HIWORD(wParam)==BN_CLICKED) num objecto dentro da tua janela mãe (message==WM_COMMAND).
Ainda não sabes qual dos objetos (qual dos botões) foi clicado, mas já podes fazer uma coisa, que é guardar o que está dentro do EditNum1 e do EditNum2 para dentro de variáveis.
Então vamos a isso.
Todo o texto que está dentro de uma EditBox é considerado pelo Windows como uma string mesmo que sejam números é sempre uma string, por isso tens que criar uma variável string para guardar o texto da EditNum1 (primeiro valor), essa variável vai-se chamar s_valor1 do tipo string.
Já que estás com a mão na massa, e como são duas EditBoxs podes criar já duas variáveis do tipo string.

Vais agora guardar o que está dentro das EditBoxs para as variáveis.
O que vais fazer é enviar uma mensagem ao Windows atravez do SendMessage() com os seguintes parametros (lembras-te que são preciso 4 parametros, como já te expliquei na Parte 2 do Tut, ao enviar um novo tipo de letra para os objectos)

SendMessage(
1º !Objecto destino! Esta é facil, o objecto que queremos é a EditNum1, então o primeiro parametro é “(HWND) EditNum1,

2º !Mensagem enviada! Como queremos receber o texto dentro dessa EditBox, se pesquisares no MSDN encontras que a mensagem tem de ser EM_GETLINE. Então que “(UINT) EM_GETLINE,” e 2º parametro completo! http://msdn.microsoft.com/library/d...lreference/editcontrolmessages/em_getline.asp

3º !Linha a ser enviada! Aqui tens que especificar qual linha é que queres. Ora uma EditBox só tem uma linha, mas nem sempre, as EditBoxs podem ser também multiline. Sendo a tua EditBox monoline a linha é sempre igual a 1. Por isso, o 3º parametro é “(WPARAM) 1,”

4º !Ponteiro para a variável! Aqui oferecemos o ponteiro para a tua variável onde queres que seja guardado o texto, s_valor1, como é ponteiro convem por o “&” antes. O que ele vai fazer é guardar o texto da linha nº 1, do objecto EditNum1 para a variável s_valor1. Então, 4º parametro é “(LPARAM) &s_valor1
);

o SendMessage() seguinte faz o mesmo que este, mas guardar o que está dentro do EditNum2 para a variável s_valor2 também do tipo string.

Já está! O que estava dentro das EditBoxs estão agora dentro das tuas variáveis s_valor1 e s_valor2.
Agora só tens que identificar qual dos botões foi clicado e fazer a respectiva operação logica “+” “-“ “*” ou “/”.
Podemos fazer uma coisa antes! Que é converter as strings em integers para que o compilador nos deixe somar, subtrair, multiplicar ou dividir.
Criei duas variáveis do tipo integer para guardar este novos integers.
Isto é algo básico de C, mas aqui vai: o include “stdlib.h” dá-te a função atoi() que te converte strings em integers.
Mais info em http://www.cplusplus.com/ref/cstdlib/atoi.html

Código:
               valor1 = atoi(s_valor1);
               valor2 = atoi(s_valor2);

Ok, valor1 e valor2 sao agora variáveis com os valores numericos correspondentes aos que o user introduziu na EditNum1 e na EditNum2.

Vais finalmente identificar qual dos objectos o user clicou e efectuar a operação lógica usando as variáveis valor1 e valor2.
A parte baixa do wParam “LOWORD(wParam)” dá-te o ID do objecto acionado, então se o ID for 1001 foi clicado o ButtonMais, se for 1002 foi o ButtonMenos etcetc.
Vais fazer um switch para isso.
Código:
                switch (LOWORD(wParam))
                {
                    case ID_BUTTONmais:  
                 
                    break;
                    case ID_BUTTONmenos:     
   
                    break;
                    case ID_BUTTONvezes:   
     
                    break;
                    case ID_BUTTONdividir:        

                    break;
                }
(Mais uma vez lembro-te que defeni ID_BUTTONmais igual a 1001, ID_BUTTONmenos igual a 1002 etcetc.)

Agora dentro de cada caso, vais por a respectiva operação. Eu decidi utilizar uma variável chamada total também do tipo integer.
(este programa faz-se com muito menos variaveis, eu apenas usei um disparate de variáveis para fazer o codigo de facil leitura)
Vais por cada uma destas operações no seu devido lugar

Código:
total = valor1+valor2;
total = valor1-valor2;
total = valor1*valor2;
total = valor1/valor2;

e fica algo deste genero:

Código:
                switch (LOWORD(wParam))
                {
                    case ID_BUTTONmais:  
                        total = valor1+valor2;
                    break;
                    case ID_BUTTONmenos:        
                        total = valor1-valor2;                     
                    break;
                    case ID_BUTTONvezes:        
                       total = valor1*valor2;                     
                    break;
                    case ID_BUTTONdividir:        
                        total = valor1 / valor2;                     
                    break;
                }

por fim, só tens que enviar o total para o EditNum3 que é este.
total1na.jpg


Antes disso, ainda tens que voltar a converter a variável Total do tipo integer para uma do tipo String, para que o compilador te deixe enviar o resultado.
Mais uma vez, recorres a uma função da include “stdlib.h”, a função é a itoa ()
http://www.cplusplus.com/ref/cstdlib/itoa.html

Código:
itoa (total,s_total,10);

Criei “MAIS” uma variáveis chamada s_total do tipo string, para guardar o total da operação.
E é desta que vais mostrar o resultado! Tem de ser atravez de outra SendMessage() para o Windows.
Código:
                SendMessage(
	                (HWND) EditTotal,
	                (UINT) WM_SETTEXT, 
	                (WPARAM) 0,
	                (LPARAM) &s_total
                    );

aqui está,
1º a SendMessage() tem como destino a EditTotal que é a EditBox de resultado.

2º a mensagem é WM_SETTEXT, que faz com que um objecto troque o seu texto, no caso duma EditBox ela muda o texto dentro da caixa.

3º O wParam não é usado, por isso pode ser o que quizeres (eu coloquei 0)

4º O lParam é um ponteiro para uma variavel string, que neste caso é a tua variavel s_total, que é onde está o resultado que o utilizador quer.

Info sobre a WM_SETTEXT:
http://msdn.microsoft.com/library/d...windowreference/windowmessages/wm_settext.asp

Parte 3: Terminada
Tutorial: Terminado
 
Última edição:
Aqui está o codigo completo da calculadora.

Código:
#include <windows.h>
#include <stdlib.h>

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)
{
    HWND hwnd;              
    MSG messages;          
    WNDCLASSEX wincl;        

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = "WindowsApp";
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;          
    wincl.cbSize = sizeof (WNDCLASSEX);

    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL; 
    wincl.cbClsExtra = 0;  
    wincl.cbWndExtra = 0;  
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if (!RegisterClassEx (&wincl))
        return 0;

    hwnd = CreateWindowEx (
        0,                 
        "WindowsApp",        
        "Calculadora",       
        WS_OVERLAPPEDWINDOW, 
        350,      
        250,      
        300,               
        100,               
        HWND_DESKTOP,    
        NULL,              
        hThisInstance,      
        NULL                
        );

    ShowWindow (hwnd, nFunsterStil);

    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    
    return messages.wParam;
}

#define ID_BUTTONmais 1001
#define ID_BUTTONmenos 1002
#define ID_BUTTONvezes 1003
#define ID_BUTTONdividir 1004

HINSTANCE g_inst;
HWND EditNum1,EditNum2,EditTotal,ButtonMais,ButtonMenos,ButtonVezes,ButtonDividir;

void DesenharObjectos(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    EditNum1 = CreateWindowEx (
        WS_EX_CLIENTEDGE,  
        "EDIT",        
        "5",       
        WS_VISIBLE|WS_CHILD|WS_BORDER|ES_RIGHT , 
        30, 30, 50, 20,                 
        hwnd,       
        NULL,           
        g_inst,      
        NULL                 
        );
           
    EditNum2 = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "3",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        160, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

    EditTotal = CreateWindowEx (
        WS_EX_CLIENTEDGE,"EDIT", "",       
        WS_VISIBLE|WS_CHILD|WS_BORDER, 
        220, 30, 50, 20,                
        hwnd, NULL, g_inst, NULL );

    ButtonMais = CreateWindowEx (
        0,  
        "BUTTON",        
        "+",       
        WS_VISIBLE|WS_CHILD, 
        80, 30, 20, 20,                
        hwnd,    
        (HMENU)ID_BUTTONmais,          
        g_inst,      
        NULL           
        );
           
    ButtonMenos = CreateWindowEx (
        0, "BUTTON", "-",       
        WS_VISIBLE|WS_CHILD, 
        100, 30, 20, 20,             
        hwnd, (HMENU)ID_BUTTONmenos, g_inst, NULL);
           
    ButtonVezes = CreateWindowEx (
        0, "BUTTON", "*",       
        WS_VISIBLE|WS_CHILD, 
        120, 30, 20, 20,             
        hwnd, (HMENU)ID_BUTTONvezes, g_inst, NULL);
           
    ButtonDividir = CreateWindowEx (
        0, "BUTTON", "/",       
        WS_VISIBLE|WS_CHILD, 
        140, 30, 20, 20,
        hwnd, (HMENU)ID_BUTTONdividir, g_inst, NULL);
    
    CreateWindowEx (
        0, 
        "STATIC", 
        "=",       
        WS_VISIBLE|WS_CHILD, 
        212, 31, 5, 20,
        hwnd, 
        NULL, 
        g_inst, 
        NULL
        );
    
    SendMessage((HWND) EditNum1,
        (UINT) WM_SETFONT, 
        (WPARAM) GetStockObject(DEFAULT_GUI_FONT),
        (LPARAM) lParam
        );	  
           
    SendMessage((HWND) EditNum2,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    SendMessage((HWND) EditTotal,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    
    SendMessage(
        (HWND) ButtonMais,
        (UINT) WM_SETFONT, 
        (WPARAM) GetStockObject(DEFAULT_GUI_FONT),
        (LPARAM) lParam
        );	
           
    SendMessage((HWND) ButtonMenos,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);
    SendMessage((HWND) ButtonVezes,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);	
    SendMessage((HWND) ButtonDividir,(UINT) WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT),(LPARAM) lParam);        	
}

char s_valor1[20] = "0", s_valor2[20] = "0", s_total[20] = "0";
int valor1, valor2, total;

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE: 
            DesenharObjectos(hwnd,message,wParam,lParam);
        break;    
        case WM_COMMAND:
            
            if ((HIWORD(wParam) == BN_CLICKED))
            {   
                
                SendMessage(
                    (HWND) EditNum1,
	                (UINT) EM_GETLINE,
	                (WPARAM) 1, 
	                (LPARAM) &s_valor1 
                    ); 
                     
                SendMessage((HWND)EditNum2,(UINT)EM_GETLINE,(WPARAM)1,(LPARAM) &s_valor2);
                 
               	valor1 = atoi(s_valor1);
                valor2 = atoi(s_valor2); 
                 
                switch (LOWORD(wParam))
                {
                    case ID_BUTTONmais:  
                        total = valor1+valor2;
                    break;
                    case ID_BUTTONmenos:        
                        total = valor1-valor2;                     
                    break;
                    case ID_BUTTONvezes:        
                       total = valor1*valor2;                     
                    break;
                    case ID_BUTTONdividir:        
                        total = valor1 / valor2;                     
                    break;
                }    
                
                itoa (total,s_total,10);
                     
                SendMessage(
	                (HWND) EditTotal,
	                (UINT) WM_SETTEXT, 
	                (WPARAM) 0,
	                (LPARAM) &s_total
                    );
                    
            }   
            break;
        case WM_DESTROY:
            PostQuitMessage (0);
        break;
        default:                     
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
    return 0;
}

Final:
Como é obvio, hoje em dia, quase ninguem se dá ao trabalho de programar a este nivel. Quem programa C para o Windows usa resources e assistences de desenho, libs, já ninguem conta pixels para desenhar um butão ou uma caixa de texto.
Eu fiz este tutorial porque considero essencial para um BOM programador, saber codar a este nivel. Sabendo isto, sabes tambem concerteza pegar no VB, Delphi,VC++ ou noutra linguagem de "alto alto" nivel e começar a programar. E é claro, melhoras a noção de como um SO interage com o utilizador. Melhor que isto só mesmo assembly 8o !!
Lembra-te que a consulta é essencial, inflizmente, nada foi inventado no codigo que escrevi, está tudo documentado na Web, o Google é o teu melhor amigo! Vai demorar um pouco até conseguires reunir os melhores sites com a melhor documentação, daí a começares a ter um ritmo na programação e alguma facilidade em encontrar a informação que queres. (já te dou alguns uteis, mas nada como o google! experimenta BN_CLICKED no google!)

Por fim:
Gostava de feedback (quem não gosta..)
Se alguem estiver a seguir o Tut, e que encontre erros ou se me esqueci de algo, PM me pls :cool:
Estou aberto a perguntas e duvidas.
Cumps :banana: :banjump:

by Favas

Links uteis

Windows API
ftp://ftp.cs.virginia.edu/pub/lcc-win32/win32hlp.exe -> um "must have"
http://msdn.microsoft.com/ -> quem melhor que a microsoft para explicar como programar para o Windows?
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=9930&lngWId=3 -> programa para criar Fonts, muito util!
 
Última edição:
eu admira-me ainda ngn ter dito nada.
excelente tutorial, parabens pelo iniciativa :)

eu usei a winapi durante vários anos, até a MS lançar o .NET (abençoada seja!).
mas acho que este tutorial está muito bom para quem se quer iniciar.

mais um vez parabens, imagino que deve ter dado bastante trabalho (fazer os screens, explicar tintin por tin, etc).
:)
 
sapropel disse:
eu admira-me ainda ngn ter dito nada.
excelente tutorial, parabens pelo iniciativa :)

eu usei a winapi durante vários anos, até a MS lançar o .NET (abençoada seja!).
mas acho que este tutorial está muito bom para quem se quer iniciar.

mais um vez parabens, imagino que deve ter dado bastante trabalho (fazer os screens, explicar tintin por tin, etc).
:)
Foi por isso que o adicionei logo ao index de artigos/tutorial/how-tos :P
Bom trabalho, continua. :)
 
Obrigado pessoal, realmente dá algum trabalho.
Eu posso saber e entender o meu codigo, mas explicar não é nada facil, acontece muitas vezes eu estar a escrever o que faz certa instrução, e depois de reler aquilo que escrevi reparo que não faz sentido.
O meu maior medo é não ser entendido..
Optei por uma linguagem mais familiar, e menos formal, para cativar mais a atenção do ppl, espero que resulte.

tks mais uma vez
 
Favas disse:
5º - !Distancia do topo! Estas coordenadas são sempre em pixels, se alterares os valores vais fazer com que o edit se mova pra cima ou para baixo

6º - !Distancia da esquerda! esquerda ou direita, se tiveres habituado a programação por objectos, fazes isto com alguma facilidade.
Acho que estes 2 estão trocados, para mim o 5º é que é a distância da esquerda e o 6º a distância do topo. De resto até agora 5* :)
 
tentei compliar no dev c e deu estes erros,

C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x33a):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x35b):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x37c):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x39d):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x3be):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x3df):untitl~1.cpp: more undefined references to `GetStockObject@4' follow

 
oslow disse:
tentei compliar no dev c e deu estes erros,

C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x33a):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x35b):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x37c):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x39d):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x3be):untitl~1.cpp: undefined reference to `GetStockObject@4'
C:\DOCUME~1\gisela\DEFINI~1\Temp\ccgzbaaa.o(.text+0x3df):untitl~1.cpp: more undefined references to `GetStockObject@4' follow



Tenta mudar para uma pasta curta Ex: C:\projectos
 
mt bem!boa explicaçao, é uma boa ajuda para noobs!:)

ja agora, ainda nng se lembrou de criar o sudoko em C
se alguem tivesse agredecia ke postasse, em windows ou em bordland
tks :D
 
Tu estás efectivamente lá!
ha muito k tentava aprender isto mas os tutoriais eram smp um bocado confusos e por isso nunca seguia nenhum ate ao fim, parece k é desta :)
 
Back
Topo