Linguagem C - A primeira janela

Transcrição

Linguagem C - A primeira janela
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
O primeiro projeto ficou com gosto de "quero mais"? Se foi este o seu caso, o quero mais
geralmente se refere a um programa Windows. Acontece que estamos apenas no terceiro
módulo do tutorial e não é aconselhável queimar etapas. Então, o jeito é partir para a janelinha
mais simples que existe: uma caixa de diálogo.
Apesar de simples, muitos conceitos serão abordados. Analise-os com atenção, pois são
essenciais para o sistema operacional Windows e vamos precisar deles daqui pra frente. É
importante entender todas as etapas e o mecanismo usado por este sistema operacional.
Um programa para o Windows
Hoje em dia a maioria dos aplicativos são controlados por uma GUI (Graphical User Interface).
Com o uso de janelas, que têm controles como campos de edição, botões, menus, etc, a
interatividade do usuário torna-se mais fácil e intuitiva.
Você viu na Introdução que a organização de um programa C exige SEMPRE uma função
principal, geralmente denominada de main, por onde o programa é iniciado. No caso de um
programa para o Windows, esta função é a WinMain (poderia ser diferente?). As funções
utilizadas num programa Windows podem ser de dois tipos: aquelas que você programar (suas
funções) e aquelas que fazem parte do sistema operacional Windows e estão prontinhas para
serem usadas.
Todas as funções "usáveis" do Windows estão agrupadas por tipo em arquivos com a extensão
.DLL (dynamic-linked libraries). O conjunto destas DLLs é chamado de API (Application
Programming Interface). As principais DLLs são kernel32.dll, user32.dll e gdi32.dll. A
kernel32.dll contém funções API que lidam com a memória e com a administração de
processos, a user32.dll possui funções que controlam a aparência da interface do usuário e a
gdi32.dll tem funções responsáveis por operações gráficas. São milhares de funções que,
quando chamadas com os parâmetros corretos, criam as janelas e os controles da GUI (Ainda
bem, já pensou ter que programar cada risquinho???).
O primeiro programa GUI para Windows
Crie um novo projeto clicando em |Project/Create|. Dê-lhe um nome (eu o chamei de testedlg) e
indique o diretório onde o projeto deve ser colocado. Em "Options" assinale "Single user". Até
1/6
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
aqui, tudo igual ao módulo anterior. Mas, para um programa Windows, siga os passos a seguir:
1. Em "Type of project" assinale "Windows Application".
2. Clique em [Create] e no diálogo "Do you want to use the wizard to generate the
application skeleton?" (Quer usar o wizard para gerar o esqueleto do aplicativo?) clique em
[Yes].
3. Na caixa de diálogo "Application characteristics", em "Type of apllication", assinale a
opção "Dialog based" e depois clique em [Ok].
4. Clique em [OK] na caixa de mensagem avisando que o projeto foi criado.
5. Na janela "Compiler settings", clique em [Next]. Na janela "Linker settings", clique em
[Next]. Na janela "Debugger settings", clique em [Finish].
Surpresa! A janela de edição mostra nosso programa gentilmente preparado pelo lcc-win32.
Mordomia pura, pois o programa está pronto. Basta compilá-lo com |Compiler/Compile
testedlg.c| e rodá-lo com |Compiler/Execute testedlg.exe| para ver o resultado.
O programa montado pelo lcc
Não se assuste com todo este código, mesmo porque vamos cansar de vê-lo e, pode ter
certeza, a gente acaba se acostumando. Observe que o programa possui três funções:
WinMain (linha 10), InitializaApp (linha 33) e a DialogFunc (linha 38). A InitializaApp não faz
nada além de retornar 1 - serve apenas de gancho se quisermos configurar alguma coisa antes
da caixa de diálogo ser mostrada. Vou tentar explicar em detalhes as outras duas, a WinMain e
a DialogFunc.
#include #include #include #include #include "testedlgres.h" static BOOL CALLBACK
DialogFunc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); int
APIENTRY WinMain( HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int
nCmdShow) { WNDCLASS wc; INITCOMMONCONTROLSEX cc;
memset(&wc,0,sizeof(wc)); wc.lpfnWndProc = DefDlgProc; wc.cbWndExtra =
DLGWINDOWEXTRA; wc.hInstance = hinst; wc.hCursor = LoadCursor(NULL,
IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszClassName = "testedlg"; RegisterClass(&wc); memset(&cc,0,sizeof(cc));
cc.dwSize = sizeof(cc); cc.dwICC = 0xffffffff; InitCommonControlsEx(&cc);
return
DialogBox(
hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC)
DialogFunc); } static int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam) {
return 1; } static BOOL CALLBACK DialogFunc( HWND hwndDlg, UINT msg, WPARAM
wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG:
InitializeApp(hwndDlg,wParam,lParam);
return TRUE; case WM_COMMAND:
switch
2/6
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
(LOWORD(wParam)) {
case IDOK:
EndDialog(hwndDlg,1);
return 1;
case IDCANCEL:
EndDialog(hwndDlg,0);
return 1;
}
break; case
WM_CLOSE:
EndDialog(hwndDlg,0);
return TRUE; } return FALSE; }
A função WinMain
A função WinMain é o ponto de entrada do programa. É uma função da API do Windows que
pede quatro parâmetros, usando a convenção de chamada stdcall (APIENTRY especifica este
tipo de chamada), e que retorna um inteiro (int):
int APIENTRY WinMain(HINSTANCE hinst,
HINSTANCE hinstPrev,
LPSTR lpCmdLine,
int nCmdShow);
1. HINSTANCE hinst é o manipulador (handle) da instância do programa. Seu valor é
sempre 0x400000 em hexadecimal e não é usado para nada... coisas do Windows.
2. HINSTANCE hinstPrev refere-se a uma instância anterior do programa: outro
manipulador misterioso. Contém sempre zero e também nunca é usado.
3. LPSTR lpCmdLine é importante! É um ponteiro para uma string de caracteres que
contém os argumentos da linha de comando.
4. int nCmdShow contém um inteiro que indica se o programa foi chamado com a instrução
de ficar oculto ou aparecer normalmente, ou outras instruções que podem ser usadas quando a
janela principal for criada.
int APIENTRY WinMain( HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int
nCmdShow) { WNDCLASS wc; INITCOMMONCONTROLSEX cc;
memset(&wc,0,sizeof(wc)); wc.lpfnWndProc = DefDlgProc; wc.cbWndExtra =
DLGWINDOWEXTRA; wc.hInstance = hinst; wc.hCursor = LoadCursor(NULL,
IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszClassName = "testedlg"; RegisterClass(&wc); memset(&cc,0,sizeof(cc));
cc.dwSize = sizeof(cc); cc.dwICC = 0xffffffff; InitCommonControlsEx(&cc);
return
DialogBox(
hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC)
DialogFunc); }
A tarefa principal desta função é criar uma estrutura WNDCLASS, preenchê-la com dados e
depois chamar a função da API DialogBox. WNDCLASS wc; cria a estrutura e todas as linhas
com wc.algumaCoisa atribuem valores aos campos desta estrutura. Depois de pronta, esta
estrutura precisa ser registrada como uma classe do sistema operacional Windows, o que é
feito com RegisterClass(&wc);.
Uma classe, no Windows, é um conjunto de objetos de janela que compartilham um
procedimento comum. Quando alguma mensagem ou evento referente a esta janela é
detectado pelo sistema, é enviada uma mensagem à esta janela. Por exemplo, quando
movemos o cursor do mouse sobre esta janela, o sistema envia uma mensagem do tipo
WM_MOUSEMOVE para o procedimento da janela, informando-o do evento. Este sistema de
3/6
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
troca de mensagens pode ser comparado ao um serviço SAC (serviço de atendimento ao
consumidor). Não se confunda: SAC não é um acrônimo usado em informática - serve apenas
como comparação.
Quando um programa está rodando, existe um caminhão de mensagens sendo
constantemente enviadas e recebidas. Seria impossível gerenciar todas. Para nossa sorte,
apenas tratamos as mensagens que nos interessam e, as restantes, passamos para o
procedimento padrão (o SAC que se vire!).
Existem vários procedimentos padrão: para uma janela normal há o DefWindowProc; para uma
janela MDI, existe o MDIDefWindowProc; e para caixas de diálogo, como neste caso, há o
procedimento DefDlgProc. Dá para perceber que, para cada tipo de janela, podemos contar
com um SAC especializado :wink:
Sabendo disso, informamos na nossa estrutura wc que o procedimento padrão de troca de
mensagens é o DefDlgProc (wc.lpfnWndProc = DefDlgProc;), indicando ao sistema o SAC
apropriado.
Os dados mais importantes da estrutura foram explicados. Agora está na hora de registrar a
estrutura para que seja colocada à nossa disposição pelo sistema: chamamos a função da API
RegisterClass(&wc); levando como parâmetro o ponteiro para a nossa estrutura wc. O
Windows que se vire com o resto...
A última declaração da função WinMain vale uma explicação mais detalhada. Agora que temos
uma estrutura registrada, chamamos a função da API DialogBox:
DialogBox(hinst,
MAKEINTRESOURCE(IDD_MAINDIALOG),
(DLGPROC) DialogFunc);
NULL,
O parâmetro hinst, que muitas funções da API ainda exigem, é o valor que recebemos do
sistema quando chamamos a função WinMain. Depois usamos a macro MAKEINTRESOURCE
para fazer com que o compilador transforme em ponteiro o valor de IDD_MAINDIALOG. Este
valor foi definido nos recursos, que serão tema do próximo módulo. Por enquanto, deixa
quieto...
4/6
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
O terceiro parâmetro é NULL. Na verdade, deveria ser o valor do manipulador (handle) da
janela-mãe desta caixa de diálogo. As caixas de diálogo, normalmente, são acessórios do
aplicativo e têm nesse parâmetro a indicação do manipulador da janela à qual pertencem.
Acontece que estamos construindo uma caixa de diálogo autônoma, que não possui uma
janela-mãe (pobrezinha, é órfã!), portanto podemos passar um NULL (nadinha de nada) como
referência.
O quarto e último parâmetro é a indicação de que, caso existam mensagens importantes, estas
devem ser direcionadas para a função DialogFunc, que será vista a seguir.
A função DialogFunc
Esta é a central de atendimento particular da nossa janela do tipo caixa de diálogo. É para ela
que o sistema envia as mensagens importantes, todas num formato padrão: o manipulador da
janela da caixa de diálogo (como se fosse o endereço do destinatário), a mensagem e mais
dois parâmetros extras.
static BOOL CALLBACK DialogFunc( HWND hwndDlg, UINT msg, WPARAM wParam,
LPARAM lParam) { switch (msg) { case WM_INITDIALOG:
InitializeApp(hwndDlg,wParam,lParam);
return TRUE; case WM_COMMAND:
switch
(LOWORD(wParam)) {
case IDOK:
EndDialog(hwndDlg,1);
return 1;
case IDCANCEL:
EndDialog(hwndDlg,0);
return 1;
}
break; case
WM_CLOSE:
EndDialog(hwndDlg,0);
return TRUE; } return FALSE; }
Esta função, com o uso de uma declaração switch, faz a triagem das mensagens recebidas.
Apenas três tipos de mensagens são tratadas pela nossa central de atendimento, as restantes
são ignoradas:
- WM_INITDIALOG: esta mensagem é enviada depois da janela da caixa de diálogo ter
sido criada e antes de ser mostrada na tela. É aqui que se pode fazer uma chamada para a
função InitializaApp, aquela que eu disse não estar fazendo nada neste programa (retorna
apenas o valor 1). Vai ser usada mais pra frente, aguarde.
- WM_COMMAND: esta mensagem é enviada quando um dos controles (ou janela-filha, se
quisermos ser mais exatos) quiser notificar a caixa de diálogo de algum evento importante, do
tipo um botão foi clicado, um checkbox foi selecionado, o texto de uma caixa de texto foi
alterado, etc. Como a caixa de diálogo pode conter vários controles, usamos novamente uma
declaração switch para poder tratá-los individualmente.
- WM_CLOSE: esta mensagem é recebida quando o usuário clicar a opção "close" do
menu ou quando digitar Ctrl+F4 para fechar a caixa de diálogo.
5/6
Linguagem C - A primeira janela
Escrito por vovó Vicki
Dom, 23.11.2008 00:41 - Última atualização Seg, 09.03.2009 07:20
Observe que, com exceção da mensagem WM_INITDIALOG, todas as outras são respondidas
com EndDialog, ou seja, seja lá onde você clicar, a caixa de diálogo será fechada.
Mais uma coisa. Caso não tenha percebido, todas as mensagens são precedidas por WM_,
que vem de Windows Message. Como nomes são mais fáceis de memorizar (quando se sabe
Inglês :wink:) do que números, os números de identificação das mensagens padrão do
Windows foram traduzidos para WM_tipoDeMensagem.
Observações da vó
Muita areia pro seu caminhãozinho? É, só que não tem escapatória: se quisermos brincar de
aplicativo Windows, este é o caminho das pedras para chamar o sistema na chincha. E olha
que é um aplicativozinho de nada (pus aplicativozinho só para dar uma força pro pessoal da
terceira idade :smile: )
Brincadeira a parte, a coisa não é tão complicada assim. Resumindo: a função WinMain aciona
o sistema operacional para criar uma estrutura do tipo WNDCLASS. Depois colocamos os
valores necessários na estrutura, principalmente o procedimento padrão de troca de
mensagens (o SAC especializado, lembra?) e registramos tudo no sistemão do Windows.
Criamos nossa própria central de mensagens (a função DialogFunc) para poder receber as
mensagens do SAC, fazer a triagem e reagirmos apenas às que nos convierem.
Pois é, esta é a forma de fazer com que o sistema operacional trabalhe para nós e, o que é
mais importante, da forma como NÓS determinamos. Para tanto é preciso ter uma boa fonte de
referência da API do Windows. Aconselho fazer o download do win32.exe nos mesmos
endereços indicados para o download do lcc-win32 que, apesar de imenso (são 12,8 Mega), é
essencial para nos orientar.
Se você se embananou, não se preocupe. Voltaremos ao assunto mais de uma vez.
Abraço da vó :vovo:
6/6