A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Tutoriais e Programação

Menu malandro (masm)

Qui

16

Abr

2009


20:57

(2 votos, média 5.00 de 5) 


Nível intermediário

Trabalhando com variáveis do sistema. Brinque com o usuário. Resolução de tela, coordenadas de janelas, data e hora do sistema são algumas das novidades usadas para criar uma janela que "foge" do cursor do mouse.

Neste tutorial vamos fazer uma brincadeira com o usuário. Aproveitando o código fonte do tutorial anterior, o Criando menus em Assembly, vamos obter algumas informações do contexto (as assim chamadas variáveis do sistema) e fazer com que a janela "fuja do cursor do mouse". Veremos como obter a resoluçãoo de tela, o tamanho de uma janela, hora e data do sistema e a posição de uma janela na tela. Veremos também como interagir com estes dados para obter os resultados desejados.

O tipo da janela

O código fonte base é o mesmo escrito para o tutorial "Criando menus em Assembly", assim como o arquivo de recursos. O arquivo de recursos não precisa ser alterado. Fazendo algumas pequenas modificações no código fonte, podemos fazer uma brincadeira com o usuário - escolhendo determinados itens do menu, a janela "foge". A primeira pequena modificação será feita na função CreateWindowEx: fixaremos o tamanho da janela em 300 x 200 pixels e tiraremos os elementos do canto superior direito da janela que permitem maximizar ou minimizar a janela.

... .DATA ... largJanela dd 300 altJanela dd 200 .CODE inicio: ... gerenteJanela proc mInst:HINSTANCE, mInstAnt:HINSTANCE, linhaCmd:LPSTR, Mostra:DWORD ... INVOKE CreateWindowEx, NULL, ADDR NomeClasse, ADDR TituloJanela,\ WS_POPUPWINDOW or WS_CAPTION, CW_USEDEFAULT,\ CW_USEDEFAULT, largJanela, altJanela, NULL, NULL,\ mInst, NULL ... end inicio

Trocando o texto

Primeiramente vamos trocar o texto que será mostrado ao usuário quando escolher algum item do menu. Use a imaginação e divirta-se. Minha sugestão não está "aquelas coisas", mas serve como exemplo:

... .DATA NomeClasse db "MenuPadrao",0 TituloJanela db "Menu Malandro",0 NomeMenu db "lanchonete",0 Texto_sopa db "SOPA de Letrinhas - Só para adultos",0 Texto_salada db "SALADA Brejeira - Temperada com besteira",0 Texto_peixe db "PEIXE Ecológico - Direto do brejão",0 Texto_frango db "FRANGO Sadio - Goleiro sarado",0 Texto_piranha db "Nananinanão... hehehe",0 Texto_sobremesa db "SOBREMESA Especial - Bala perdida",0 Texto_fim db "Até outra hora...",0

Trocando caixas de mensagens por texto na tela

Ao invés de utilizar a função MessageBox para criar caixas de mensagem para indicar o item de menu escolhido, vamos "pintar" o texto referente a cada item do menu na área cliente da janela. O procedimento já foi descrito em detalhes no tutorial Pintando Texto. A lógica é a seguinte:

  • Criamos uma variável global que recebe o endereço do texto referente ao item de menu selecionado. Este valor é obtido quando interceptamos a mensagem WM_COMMAND.
  • No final da rotina do WM_COMMAND chamamos a função InvalidateRect, a qual força uma repintura da área cliente da janela, ou seja, invalida a área e envia uma mensagem WM_PAINT.
  • Interceptamos a mensagem WM_PAINT e fazemos a pintura do texto indicado pelo endereço da variável global.

A coisa fica com esta cara:

... .DATA? mInstancia HINSTANCE ? endTexto WPARAM ? ... gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL mCM:HDC LOCAL ps:PAINTSTRUCT LOCAL eRet: RECT ... .ELSEIF uMsg == WM_COMMAND mov eax, wParam .IF ax == mnID_fim invoke MessageBox, NULL, ADDR Texto_fim, OFFSET TituloJanela, MB_OK invoke DestroyWindow, hWnd .ELSEIF ax == mnID_sopa mov endTexto, OFFSET Texto_sopa .ELSEIF ax == mnID_salada mov endTexto, OFFSET Texto_salada .ELSEIF ax == mnID_peixe mov endTexto, OFFSET Texto_peixe .ELSEIF ax == mnID_frango mov endTexto, OFFSET Texto_frango .ELSEIF ax == mnID_sobremesa movendTexto, OFFSET Texto_sobremesa .ELSEIF ax == mnID_piranha mov endTexto, OFFSET Texto_piranha ... .ELSEIF ax == mnID_amendoim mov endTexto, OFFSET Texto_piranha ... .ENDIF invoke InvalidateRect, hWnd, NULL, TRUE .ELSEIF uMsg == WM_PAINT invoke BeginPaint, hWnd, ADDR ps mov mCM, eax invoke GetClientRect, hWnd, ADDR eRet invoke DrawText, mCM, endTexto, -1, ADDR eRet,\ DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint, hWnd, ADDR ps .ELSE ...

Se você quiser, neste ponto é possível compilar um executável e testá-lo. A janela ainda não "foge", porém as mensagens relativas aos itens de menu aparecem na área cliente da janela.


Criando a função que desloca a janela

Os itens escolhidos para deslocarem a janela são "Sopa de Piranha" e "Amendoim". Quando o usuário os escolher, a janela deve se deslocar na tela para uma posição diferente. Para tanto, vamos declarar a função "foge". Esta função calcula as novas coordenadas do canto superior esquerdo da janela e fará com que assuma a nova posição.

Declarando a função foge

A primeira providência será declarar o protótipo da função e, logo a seguir, declarar a pópria função. Vamos precisar do manipulador da janela para podermos alterar algumas das suas propriedades e, por isto, o único parâmetro da função será o manipulador:

.386 .model flat,stdcall option casemap:none gerenteJanela proto :DWORD,:DWORD,:DWORD,:DWORD foge proto :DWORD ... .CODE inicio: ... foge proc hWnd:HWND ... foge endp end inicio

Calculando a resolução da tela

Tudo bem. Queremos deslocar nossa janela pela tela, porém ainda não sabemos quantos pixels poderemos deslocá-la no sentido horizontal ou vertical por que desconhecemos quantos pixels estão disponíveis. A função que pode nos fornecer todas as medidas de elementos do sistema, entre elas a resolução de tela, é a GetSystemMetrics:

int GetSystemMetrics( int nIndex // medida do sistema ou configuração desejada );

O parâmetro nIndex, entre outros, pode ser SM_CXSCREEN. Neste caso, o valor de retorno é o número de pixels da horizontal (eixo X). Usando o SM_CYSCREEN obtemos o número de pixels na vertical (eixo Y). Como usaremos as coordenadas do canto superior esquerdo da janela para movimentá-la na tela, os pixels disponíveis na horizontal serão os do eixo X MENOS o número de pixels da largura da janela. Se não descontarmos os pixels da largura da janela corremos o risco de posicioná-la fora da tela (ou mostrá-la apenas parcialmente). Da mesma forma, os pixels disponíveis na vertical serão os do eixo Y MENOS o número de pixels da altura da janela. Os valores assim obtidos serão armazenados nas variáveis locais largMax e altMax.

foge proc hWnd:HWND LOCAL largMax, altMax:DWORD invoke GetSystemMetrics, SM_CXSCREEN sub eax, largJanela mov largMax, eax invoke GetSystemMetrics, SM_CYSCREEN sub eax, altJanela mov altMax, eax ... foge endp end inicio

Determinando a posição atual da janela

A posição atual de uma janela pode ser obtida através da função GetWindowRect. Esta função precisa do manipulador da janela e de uma estrutura RECT, onde serão armazenadas as coordenadas de tela da posição da janela cujo manipulador foi fornecido:

BOOL GetWindowRect( HWND hWnd, // manipulador da janela LPRECT lpRect // endereço da estrutura RECT ); typedef struct _RECT { // rc LONG left; // esquerda LONG top; // superior LONG right; // direita LONG bottom; // inferior } RECT; foge proc hWnd:HWND LOCAL largMax, altMax:DWORD LOCAL retang:RECT invoke GetSystemMetrics, SM_CXSCREEN sub eax, largJanela mov largMax, eax invoke GetSystemMetrics, SM_CYSCREEN sub eax, altJanela mov altMax, eax invoke GetWindowRect, ADDR ... foge endp end inicio

Obtendo a hora do sistema

As posições que a janela deve ocupar são aleatórias e, para calculá-las, usaremos um expediente: a nova posição dependerá dos milésimos de segundo indicados pelo relógio do sistema, pois a possibilidade de repetição deste valor é muito remota. A função GetSystemTime não possui valor de retorno, mas preenche uma estrutura SYSTEMTIME com a hora do sistema:

VOID GetSystemTime( LPSYSTEMTIME lpSystemTime // endereço da estrutura SYSTEMTIME );

A estutura SYSTEMTIME possui os seguintes membros:

typedef struct _SYSTEMTIME { // st WORD wYear; // ano WORD wMonth; // mês WORD wDayOfWeek; // dia da semana WORD wDay; // dia WORD wHour; //hora WORD wMinute; // minutos WORD wSecond; // segundos WORD wMilliseconds; milésimos de segundo } SYSTEMTIME;

Para obter os dados desejados, precisamos declarar uma variável local do tipo SYSTEMTIME e chamar a função GetSystemTime que tenha como parâmetro o endereço da nossa variável:

foge proc hWnd:HWND LOCAL largMax, altMax:DWORD LOCAL retang:RECT LOCAL system_time:SYSTEMTIME invoke GetSystemMetrics, SM_CXSCREEN sub eax, largJanela mov largMax, eax invoke GetSystemMetrics, SM_CYSCREEN sub eax, altJanela mov altMax, eax invoke GetWindowRect, ADDR invoke GetSystemTime, ADDR system_time ... foge endp end inicio

O valor que desejamos será encontrado em system_time.wMilliseconds.

Calculando a nova posição da janela

Teremos que calcular o novo valor de X (posição horizontal na tela) e de Y (posição vertical na tela). A posição atual está no membro da estrutura retang.left e retang.top. Se somarmos o valor de retang.left (posição X atual da janela) aos milésimos de segundo que estão em system_time.wMilliseconds, podem ocorrer duas situações: o novo valor de X ou é maior ou é menor do que a largMax calculada. Caso seja maior, se movermos a janela para este ponto, ela ficará parcialmente visível ou então fora da tela. Faremos uso da sintaxe de alto nível do MASM para criar um loop: se o novo valor de X for maior que largMax, diminuímos este valor de X (novo X - largMax) tantas vezes quantas forem necessárias para que o novo valor de X seja menor que largMax. Assim que o novo valor de X for menor que largMax, saímos do loop e guardamos o valor de X na variável local novoX. O mesmo raciocínio se aplica para o cálculo do novo valor de Y.

foge proc hWnd:HWND LOCAL largMax, altMax:DWORD LOCAL retang:RECT LOCAL system_time:SYSTEMTIME LOCAL novoX, novoY:DWORD invoke GetSystemMetrics, SM_CXSCREEN sub eax, largJanela mov largMax, eax invoke GetSystemMetrics, SM_CYSCREEN sub eax, altJanela mov altMax, eax invoke GetWindowRect, ADDR invoke GetSystemTime, ADDR system_time mov ax, system_time.wMilliseconds add eax, retang.left .WHILE TRUE .BREAK .IF (eax < largMax) sub eax, largMax .ENDW mov novoX, eax mov ax, system_time.wMilliseconds add eax, retang.top .WHILE TRUE .BREAK .IF (eax < altMax) sub eax, altMax .ENDW mov novoY, eax ... foge endp end inicio

Note que transferimos para AX (e não EAX) o valor dos milisegundos. É que a estrutura system_time foi criada a partir do modelo SYSTEMTIME, cujos membros são todos do tipo WORD (16 bits). Caso você tentar transferir para EAX (32 bits) o valor de qualquer membro de system_time (16 bits), o assembler indicará o erro "instruction operands must be the same size", ou seja, os operandos da instrução precisam ser do mesmo tamanho.

Mudando a posição da janela

De posse de todos os parâmetros necessários para mudar a posição da janela, agora podemos chamar a função MoveWindow:

BOOL MoveWindow( HWND hWnd, // manipulador da janela int X, // posição horizontal int Y, // posição vertical int nWidth, // largura int nHeight, // altura BOOL bRepaint // indicador de repintura );

O parâmetro hWnd veio como parâmetro para nossa função "foge". Os parâmetros X e Y são os nossos novoX e novoY. A largura e a altura não serão modificadas, portanto usaremos as variáveis globais largJanela e altJanela. O indicador de repintura será TRUE (verdadeiro) para forçar o sistema a enviar uma mensagem WM_PAINT, devidamente interceptada e onde faremos nossa pintura personalizada. Depois disto, basta um ret para terminarmos a função foge:

foge proc hWnd:HWND LOCAL largMax, altMax:DWORD LOCAL retang:RECT LOCAL system_time:SYSTEMTIME LOCAL novoX, novoY:DWORD invoke GetSystemMetrics, SM_CXSCREEN sub eax, largJanela mov largMax, eax invoke GetSystemMetrics, SM_CYSCREEN sub eax, altJanela mov altMax, eax invoke GetWindowRect, ADDR invoke GetSystemTime, ADDR system_time mov ax, system_time.wMilliseconds add eax, retang.left .WHILE TRUE .BREAK .IF (eax < largMax) sub eax, largMax .ENDW mov novoX, eax mov ax, system_time.wMilliseconds add eax, retang.top .WHILE TRUE .BREAK .IF (eax < altMax) sub eax, altMax .ENDW mov novoY, eax invoke MoveWindow, hWnd, novoX, novoY, largJanela, altJanela, TRUE ret foge endp end inicio

Tutorial para download

Este tutorial, juntamente com o código fonte, imagens e o executável está na seção de Downloads / Tutoriais / Assembly Numaboa, mas você também pode baixá-lo aqui.

Вадим Логофет Sberbankникас адресаооо полигон плюсqlik biпогода в харькове на синоптикадреса никас отзывы класс

Informações adicionais