c++ win32 tutorial para console

Upload: spielernico

Post on 28-Oct-2015

240 views

Category:

Documents


6 download

DESCRIPTION

Pequeno conjunto de tutoriais voltados àqueles que estão a iniciar os estudos de programação em C/C++ utilizando o sistema operacional Windows, mais especificamente, através do prompt de comando. São abordadas algumas questões clássicas que frequentemente aparecem em fóruns: como ler o conteúdo de um diretório; como mover o caret pelo console; como lidar com caracteres especiais; como colorir a fonte e o fundo; como lidar com eventos do mouse e do teclado, dentre outros. Trabalha muito superficialmente programação multithreaded.

TRANSCRIPT

  • C++ Win32 Tutorial Para Console

  • C++ Win32 Tutorial

    1

    Win32 Tutorial Ao invs de escrever um documento generalizado tentando abordar todos os principais conceitos (j existem muitos desses pela internet), criei vrios tutoriais acerca de tpicos especficos, sendo que a maioria dos temas so recorrentes nos fruns (ou fora, do latim, como desejar) em que participo. Os tutoriais usam as rotinas da Win32 API, ou seja, no dependem de bibliotecas como a MFC

    1 ou a VCL

    2.

    Eu encorajo o leitor a consultar a seo seguinte antes das outras partes deste documento. L esto detalhes de qual compilador usei, que verso do SO e outras informaes que podem ser teis. Existem alguns links para sites onde voc pode conseguir mais referncias ou at mesmo ajuda.

    Devo lembrar que nosso mundo evolui, portanto, no sei at quando estes tutoriais ou at mesmo os links esta-ro atualizados e/ou disponveis. Logo, como algum que quer se tornar um bom programador/profissional, ha-bitue-se a utilizar ferramentas mais modernas e, to importante quanto, nunca deixe de pesquisar novas formas de como se fazer uma tarefa. No porque seu professor recomenda, para fins didticos, o uso do j morto, sepultado e esquecido Dev-C++ da Bloodshed Software, que voc ir desenterr-lo s vezes devemos ir mais longe e ressuscitar o Turbo-C++, da Borland (muito obrigado aos mesmos pelos longos anos de servio, mas vocs j cumpriram o seu dever e agora fazem parte da histria. Por favor, descansem em paz...). E outra: se voc est programando para console, aprenda a us-lo. A vida no se resume a dois cliques...

    Ento, sem mais delongas

    Exibindo um diretrio ou pasta

    Um tutorial em trs partes cobrindo a leitura do contedo de um diretrio ou pasta no Windows.

    Parte 1 desenvolve um programa rudimentar para a tarefa

    Parte 2 refina o mesmo cobrindo diversas condies de erro comuns

    Parte 3 explora alguns outros aspectos das rotinas usadas da API

    Trabalhando com Console no Windows

    Tutoriais demonstrando caractersticas das Win32 Console Applications ou "DOS boxes".

    Parte 1 descreve consoles

    Parte 2 nomeia o console, utiliza manipuladores padro, move o cursor (ou caret, como preferir), limpa a tela e utiliza blocos de caracteres

    Parte 3 desenha linhas, caixas e grades e esconde/modifica o cursor

    Parte 4 lida com caracteres especiais

    Parte 5 colore palavras e o fundo

    Parte 6 lida com eventos do teclado e do mouse

    Parte 7 trata de problemas com o tamanho do console

    Programao Multithreaded Bsica

    Tutoriais contendo uma introduo aos processos multitarefa.

    Parte 1 constri um programa de thread nico, ento o converte a uma verso multithreaded mui-to pobre do mesmo.

    Parte 2 comea corrigindo algumas das falhas introduo sincronizao

    Parte 3 corrige as falhas restantes e comenta de forma mais generalizada sobre os problemas nos projetos multithreaded

    1 Microsoft Foundation Classes so classes fundamentais em C++ para programao Microsoft Windows; 2 Visual Component Library um framework visual de componentes baseados em orientao a objetos para desenvolvimento de aplicativos para Microsoft Windows. Foi desenvolvido pela Borland para uso nas (e fortemente integrada com) ferramentas Delphi e C++ Builder RAD (agora propriedade da CodeGear, diviso da Embarcadero Technologies).

  • C++ Win32 Tutorial

    2

    Notas Gerais Os cdigos presentes neste documento foram escritos em C++, mas podem ser facilmente convertidos para C, uma vez que evitei propositalmente o uso de orientao a objetos. Se o leitor demonstrar um pouco de boa von-tade, conseguir cumprir esta tarefa (traduo de uma linguagem para outra) sem maiores problemas. Se voc nunca programou em C++, no se desespere no h nada de assustador neste documento.

    Alguns podem achar que o cdigo muito simplista e nem um pouco orientado a objetos. Neste caso, devo pe-dir aos mesmos que reflitam sobre o pblico a que este material voltado. Freqentemente, pessoas procuran-do por esse tipo de ajuda comearam a programar h pouco tempo, ento acredito que seria desanimador para esses indivduos se eu abordasse uma grande gama de modelos de classes e assim por diante.

    E no vejo necessidade de me distanciar do leitor somente para passar algumas dicas e orientaes. Dito isto, redigi este documento sem quaisquer preocupaes com formalidades.

    Para compilar meus cdigos fontes, fiz uso do MinGW 5.1.6 (um port para Windows do GCC e outras GNU binu-tils) em conjunto com a verso nightly build do Code::Blocks mais precisamente, a svn build rev 6181, de 27 de fevereiro de 2010. Tambm os testei com a Microsoft Visual C++ 2008 Express Edition j saiu a 2010. possvel que voc venha a utilizar um compilador diferente destes em que o cdigo fonte possa no funcionar neste caso, temo que a frase a vida... se aplique muito bem. Eu tento me manter preso s rotinas principais da API sempre que possvel, ento no devem existir problemas maiores. Se voc compilar algo e seu compilador re-clamar de que a funo no est definida ou algo similar, tente encontrar a funo equivalente consultando a documentao do seu compilador (ou futricando nos headers caso voc se sinta vontade).

    Testei os programas no Windows XP 32bits, entretanto, a maior parte do que ser tratado aqui deve ser supor-tada tanto em verses mais antigas quanto nas mais recentes do Windows. Uma coisa que diferente a fun-o GetLastError() que voc me ver usando. Essa rotina no devidamente suportada nos consoles de 16bit que acompanham verses OS/2 do Windows (95, 98 e ME) e pode devolver zero mesmo existindo um erro. Tambm testei os cdigos no Windows 7 64bits, mas como o compilador 32bits

    3, no fundo acabei usando o

    modo de compatibilidade do sistema operacional, sendo que no tive problemas com os programas gerados.

    O Windows um sistema operacional de certa forma complicado e com uma API muito rica. Freqentemente, o mesmo resultado pode ser alcanado por mais de uma maneira. Os tutoriais deste documento geralmente de-monstram um modo de se fazer algo. Podem existir, e freqentemente existiro, outros caminhos. Se voc esti-ver olhando tutoriais em mais de um site, e eles abordarem seu problema em particular de maneiras distintas, no se surpreenda. Por isso, tente no ficar chateado se algum dia o monitor da sua disciplina estiver chamando seu programa para console fora do console e no conseguir enxergar como o mesmo funciona sem nem ao me-nos olhar seu cdigo fonte e entender o que ele faz, descontando severamente sua nota. Uma abordagem dife-rente no significa que esta ou aquela est errada, elas so apenas diferentes embora devamos buscar solu-es que priorizem o tanto o desempenho quanto a segurana sempre que possvel (s vezes uma situao ex-clui a outra, por isso analise seu caso). Resumindo, voc pode freqentemente aprender muito mais estudando as diferentes solues de um mesmo problema.

    Caso tenha dvidas ou encontre alguma dificuldade com o que no foi abordado aqui (isso sempre acontece, no mesmo?), eu recomendaria uma visita aos seguintes endereos eletrnicos: www.cprogramming.com, www.cppreference.com e www.cplusplus.com, onde existem outros tutoriais, FAQs enormes e fruns de discus-so. E nunca deixe de pesquisar... http://pt-br.lmgtfy.com/?q=Installing+MinGW

    Os fruns nos sites esto cheios de pessoas que tentaro ajud-lo, entretanto h, como sempre, uma ressalva: nem todos que esto tentando ajudar so necessariamente qualificados em faz-lo voc pode conseguir algu-mas respostas, no mnimo, exticas. A internet assim, esteja ciente disso Leia os FAQs e tente, ao menos, buscar sua resposta dentro do frum antes de fazer alguma pergunta.

    3 O MinGW64 ainda no estvel/confivel o suficiente e a expanso 64bits do VC++ no suportada pela verso Express da IDE, apenas pela Professional, que paga.

  • C++ Win32 Tutorial

    3

    Este documento foi criado tendo como base um conjunto de tutoriais que h muito utilizo como referncia (http://www.adrianxw.dk/index.html). Tentei contatar o autor, Adrian Worley (um dinamarqus que freqenta-va os fruns do www.programmersheaven.com), para consult-lo sobre a traduo, distribuio e cpia de sua pgina, mas no obtive sucesso. Devo tentar faz-lo novamente em outro momento, mas desde j atribuo os crditos de boa parte deste documento ao trabalho dele. Devo agradecer tambm os membros da Comunidade C/C++ Brasil, em especial o Sr. Josu Andrade Gomes e o Sr. Paulo Pires, pelas observaes e comentrios feitos sobre a primeira verso deste documento e pela orientao quanto a alguns temas.

    Por que a escolha de programar para Windows ao invs de Linux? Simples, porque o sistema operacional mais presente no nosso cotidiano est certo, reconheo que sou horrvel como usurio Linux, mas tenho meus mo-tivos. Ns ainda temos muito mais contato com o sistema operacional da Microsoft do que com distribuies Linux. Primeiramente porque h muito dinheiro envolvido neste produto em eterno desenvolvimento chamado Windows e a comunidade open source no consegue acompanhar o ritmo imposto pela empresa que plagia de forma descarada as melhores idias dos sistemas livres. E, apesar de o incentivo ao uso de sistemas operacionais e softwares abertos ou livres fazer parte das polticas do Governo e de muitas empresas espalhadas pelo pas (entenda esse comportamento como uma tentativa de reduo de custos), ns ainda estamos aqum da meta de conseguir o suporte necessrio para estes mesmos programas que usamos. A comunidade open source for-te e continua a crescer, mas dentro dos exemplos de sucesso sempre h ao menos uma empresa de grande por-te ditando os rumos do projeto, o que falta no Brasil. Ainda que o Governo incentive o uso do software livre, o mesmo no ocorre quanto sua manuteno e, mais importante at, ao seu desenvolvimento. E assim, projetos bons acabam morrendo vide o caso da distro Kurumin (que inclusive foi adotada pelo Governo e usada por muito tempo por essas empresas que vendem computadores de baixo custo nos hipermercados...).

    Como usurio Windows, assim como outros tantos, decidi estudar um pouco sobre este sistema operacional e compartilho neste documento parte do que aprendi e do que uso como referncia sempre que necessrio (estou longe de ser um desses programadores natos, talvez seja essa a razo de tentar reunir tudo em um s lugar, pra-ticando um pouco deste saber). Disponibilizo este material como uma forma de incentivo, um pontap inicial para que os estudantes de programao continuem a buscar conhecimento sobre o sistema operacional que utilizam.

    E por fim tente tirar o mximo de proveito deste material.

    -- Ncolas I. Teixeira

  • C++ Win32 Tutorial

    4

    Exibindo um diretrio, parte 1.

    Certo, ento seja l o que tenha em mente, seu programa precisa saber que arquivos esto em certo diretrio. Como fazer isso? Voc ficar contente em saber que esta tarefa fcil! Duas rotinas da Win32 API fazem justa-mente o que voc quer: FindFirstFile() e FindNextFile().

    Primeiramente, por que duas rotinas? Bem, os designers da API poderiam ter criado uma rotina que devolveria um array de strings, mas pondere sobre o problema deles: poderiam existir centenas de milhares de arquivos, cada um com dezenas de caracteres em seus nomes. A estrutura de memria necessria para manter essa divi-so deveria ser enorme!

    E s fica pior! O nome de arquivo (ou filename, como preferir) apenas uma das informaes que o Windows armazena para cada arquivo. Ele tambm mantm a data de criao, a data da ltima modificao, seu tamanho etc. plenamente possvel que um diretrio grande contendo arquivos com nomes longos, com todos os dados auxiliares inclusos, possa exigir mais memria do que o processo permite.

    A soluo escolhida foi ter uma rotina inicial que basicamente tira uma foto instantnea do diretrio e devolve a informao sobre o primeiro arquivo encontrado. Voc, ento, chama a segunda rotina repetidamente, onde, a cada chamada, a segunda rotina devolve a informao sobre o arquivo seguinte no diretrio, at que no exis-tam mais arquivos. Na verdade h uma terceira rotina que ns precisaremos usar, mas falemos mais sobre a mesma posteriormente.

    Vamos dar uma olhada nas duas. Os prottipos das mesmas na MSDN so mostrados aqui...

    HANDLE FindFirstFile( LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData );

    ... e...

    BOOL FindNextFile( HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData );

    ... ento vejamos como us-las para fazer algo til.

    Comecemos com a FindFirstFile(). O primeiro parmetro o nome do arquivo a se buscar. Certamente isso pode envolver os caracteres coringas do Windows. O outro parmetro um ponteiro para uma estrutura WIN32_FINDA_DATA esta estrutura de memria que o Windows ir preencher para voc com todos os dados sobre o arquivo. Ela descrita assim...

    typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; TCHAR cFileName[ MAX_PATH ]; TCHAR cAlternateFileName[ 14 ]; } WIN32_FIND_DATA;

    ... existem campos para atributos (provavelmente o mais comum Read Only, mas existem vrios outros); estruturas FILETIME para as datas/horas de Criado em, Acessado em e Modificado em; campos que armaze-

  • C++ Win32 Tutorial

    5

    nam o tamanho do arquivo (mais sobre isso depois...); alguns reservadores de memria pra expanso futura (ignore estes...); e dois filenames: um o nome verdadeiro do arquivo e o outro o filename moda antiga, no formato 8.3.

    Se nenhum erro ocorrer, FindFirstFile() devolve um Search Handle usado como um contexto para a busca. Contexto, como assim? Considere esse aplicativo: suponha que voc deseje ver se o mesmo arquivo existe em dois diretrios, voc chama FindFirstFile() com a string de busca apontando para um diretrio, ento nova-mente o faz no segundo diretrio. Se o primeiro arquivo no segundo diretrio no corresponder a um acerto, voc iria tentar o prximo arquivo no segundo diretrio, ento voc chamaria FindNextFile(). Mas como o sistema sabe que voc quer o prximo arquivo no segundo diretrio? De repente voc poderia estar fazendo o pedido no primeiro, no mesmo? para isso que este handle (manipulador) serve: voc passa o manipulador de busca que devolvido pela chamada de FindFirstFile() para FindNextFile(), ento agora o sistema sa-ber qual deles que ns queremos. O search handle liga uma chamada a FindNextFile() com uma chamada a FindFirstFile().

    O manipulador de busca o primeiro parmetro para a rotina FindNextFile(), o segundo , novamente, um ponteiro para uma estrutura WIN32_FIND_DATA. FindNextFile() devolve true se existir outro arquivo para devolver e no h erros.

    Acima, mencionei que usaramos uma terceira rotina, que a FindClose(). Quando terminamos o registro do diretrio, usamos FindClose() para finalizar o manipulador de busca, dizendo ao sistema que terminamos essa operao de busca em particular. O prottipo para FindClose() est aqui...

    BOOL FindClose( HANDLE hFindFile );

    ... simples, no?

    Dado o que ns vimos at agora, podemos escrever um programa muito modesto que listar os arquivos de um diretrio os exibindo na tela. Aqui est o fonte...

    #include #include using namespace std; int main() { HANDLE hFind; WIN32_FIND_DATA FindData; cout

  • C++ Win32 Tutorial

    6

    ... ento o que ns fizemos aqui foi um file logger bem simples. Sendo mais criterioso, sabemos que h mais do que isso para se ver. O que acontece, por exemplo, se no houver arquivos que cumpram os requisitos da bus-ca? E se no existirem arquivos .exe no nosso diretrio? Vamos dar uma olhadinha mais de perto nessa pergunta e em outros problemas em potencial na prxima sesso.

  • C++ Win32 Tutorial

    7

    Exibindo um diretrio, parte 2. Como exerccio, mudemos o pathname no cdigo de tal forma que FindFirstFile() no encontre nada. Pedi para que o programa procurasse por "C:\\Windows\\*.exz" (no existem arquivos .exz, eu conferi), e consegui isso...

    ... nada elegante e dificilmente de alguma valia para o nosso programa.

    Na parte 1 deste tutorial, eu disse que FindFirstFile() devolve um manipulador de busca se no houver er-ros. Se a rotina incluir o caso onde nenhum arquivo confere com a especificao (ou seja, um erro), ela devolve uma constante INVALID_HANDLE_VALUE. Melhor do que ser otimista e assumir que FindFirstFile() est de-volvendo um manipulador vlido, devemos conferir se isso est de fato acontecendo seremos futuros enge-nheiros e programadores, devemos nos acostumar a pensar nos infinitos problemas que podem aparecer (Lei de Murphy e da Perversidade da Matria so fatos presentes em nossas vidas, no mesmo?). Ns tambm preci-samos saber se a razo do INVALID_HANDLE_VALUE foi pelo fato de o programa realmente no encontrar arqui-vos ou se algum outro erro ocorreu. Para fazer isso, utilizaremos a rotina GetLastError() que freqentemen-te usada para se obter mais informaes sobre as razes pela qual uma rotina da API devolveu um erro de esta-do. uma rotina que voc vir a cham-la de amiga! Modifiquei parte do cdigo original para acrescentar essas verificaes

    hFind = FindFirstFile("C:\\Windows\\*.exe", &FindData); if (hFind == INVALID_HANDLE_VALUE) { ErrorCode = GetLastError(); if (ErrorCode == ERROR_FILE_NOT_FOUND) { cout

  • C++ Win32 Tutorial

    8

    invlido. Na realidade, se no existem arquivos, ns no precisamos nem chamar FindNextFile() ou FindClo-se() pelo fato de a busca nunca ter sido aberta, iniciada.

    Agora ns adicionaremos essa linha s declaraes...

    bool Continue = true;

    ... e alteraremos o bloco if() no cdigo acima de tal forma que se a chamada a FindFirstFile() devolver INVALID_HANDLE_VALUE, a varivel booleana Continue ser configurada para false...

    if (hFind == INVALID_HANDLE_VALUE) { ErrorCode = GetLastError(); if (ErrorCode == ERROR_FILE_NOT_FOUND) { cout

  • C++ Win32 Tutorial

    9

    while (FindNextFile(hFind, &FindData)) { cout

  • C++ Win32 Tutorial

    10

    Exibindo um diretrio, parte 3. Eu comecei a parte 2 deste tutorial alterando a mscara do arquivo que estava buscando (de .exe para .exz). Fiz aquilo para demonstrar o comportamento do programa se no houvesse arquivos que atendessem a essa condi-o. Eu irei comear essa pgina mudando o caminho de C: para H: (eu no tenho um drive h:). Quando fao isso, o programa que fizemos nas pginas 8 e 9 mostra isso como sada...

    ...porque FindFirstFile() diferencia entre no conseguir encontrar um arquivo correspondente busca e uma tentativa de busca em um caminho invlido. Aqui, eu estendi o teste para permitir essa diferenciao eventual-mente...

    if (hFind == INVALID_HANDLE_VALUE) { ErrorCode = GetLastError(); if (ErrorCode == ERROR_FILE_NOT_FOUND) { cout

  • C++ Win32 Tutorial

    11

    E os outros campos na estrutura WIN32_FIND_DATA, como eles so usados? Relembremos primeiro a definio da estrutura

    typedef struct _WIN32_FIND_DATA { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; TCHAR cFileName[ MAX_PATH ]; TCHAR cAlternateFileName[ 14 ]; } WIN32_FIND_DATA;

    ... agora vamos dar uma olhada mais cuidadosa nela.

    Atributos so associados a todos os arquivos, dizendo algo sobre o mesmo. Existe uma lista inteira destes atribu-tos, embora eu duvide que voc use a maioria deles eu s utilizei alguns poucos at hoje. A lista completa at o momento

    FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_COMPRESSED FILE_ATTRIBUTE_DIRECTORY FILE_ATTRIBUTE_ENCRYPTED FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_OFFLINE FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_REPARSE_POINT FILE_ATTRIBUTE_SPARSE_FILE FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_TEMPORARY

    ... Eu digo at o momento porque o Windows um produto em desenvolvimento contnuo e a lista pode mu-dar no futuro. Se voc est interessado em saber o que so todos estes atributos e o que fazem, procure o help do seu compilador ou consulte a MSDN. (Se o link no funcionar, procure por "MSDN" no Google e ento nave-gue at a biblioteca por alguma razo, a Microsoft est sempre mudando suas URLs, dificultando manter os links atualizados).

    FILE_ATTRIBUTE_DIRECTORY bastante til se o arquivo devolvido por FindFirstFile() ou FindNextFile() for um diretrio ao invs de um arquivo normal. Voc usar isso se planejar processar arquivos em um diretrio ou em qualquer um dos seus subdiretrios. FILE_ATTRIBUTE_READONLY outro que pode vir a ter algum uso e, impreterivelmente, true se o arquivo foi configurado para "read only".

    Para testar se algum atributo em particular foi estabelecido para um arquivo, faa um AND bit a bit entre o membro dwFileAttributes da estrutura WIN32_FIND_DATA e o atributo em que voc est interessado. No e-xemplo seguinte modifiquei o programa desenvolvido nas duas primeiras partes do tutorial para apontar arqui-vos READONLY, lembrando que essa modificao deve ser feita para ambos os blocos FindFirstFile() e Find-NextFile().

    { cout

  • C++ Win32 Tutorial

    12

    ... note que h trs arquivos alcrmv.exe, alcupd.exe e SOUNDMAN.EXE esto marcados como READ ONLY. Interessante que na minha mquina com Windows 7 no h arquivos somente leitura, diferentemente do que ocorre na mquina com o XP (essa a explicao pelo fato de o console ter essa aparncia diferente: usei outro sistema).

    As datas/horas de criao, ltima escrita e ltima modificao dos arquivos so devolvidas em uma estrutura FILETIME. Tal estrutura no muito til para uso na sada, mas a API fornece uma rotina FileTimeToSystemTi-me() que converte esses valores em uma estrutura SYSTEMTIME que contm vrios campos que voc pode pro-duzir como sada. A estrutura SYSTEMTIME declarada como...

    typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME;

    O exemplo a seguir exibe a converso e sada da data de criao, entretanto, todas a trs informaes estatsti-cas podem ser tratadas do mesmo modo. Eu adicionei esta linha s declaraes...

    SYSTEMTIME SysTime;

    ... e isso aos blocos de sada...

    { cout

  • C++ Win32 Tutorial

    13

    cout.width(2); cout.fill('0'); cout

  • C++ Win32 Tutorial

    14

    ... se voc precisar usar um valor de 64bit, carregue os valores 32bit em uma union LARGE_INTEGER. Ela decla-rada como...

    typedef union _LARGE_INTEGER{ struct{ DWORD LowPart; LONG HighPart; }; LONGLONG QuadPart; } LARGE_INTEGER;

    ... declare a Union assim...

    ULARGE_INTEGER FileSize;

    ... tambm poderamos usar LARGE_INTEGER, mas uma vez que um tamanho de arquivo negativo no tem signi-ficado, usando a variante unsigned nos fornecer um maior escopo. Prepare a unio desta forma...

    FileSize.LowPart = FindData.nFileSizeLow; FileSize.HighPart = FindData.nFileSizeHigh;

    ... e ento use FileSize.QuadPart para operaes 64bit.

    O ultimo campo que usaremos o cAlternateFileName. Ele simplesmente contm o equivalente para o forma-to 8.3 do nome do arquivo. Modifiquei o programa para exibir ambos os nomes de arquivos...

    { cout

  • C++ Win32 Tutorial

    15

    ... note que os nomes compridos tm filenames 8.3. Aqueles que j estavam em um formato 8.3 tm um campo cAlternateFileName vazio e sim, como d para ver acima, tive de improvisar.

    A ltima coisa que quero abordar neste tutorial diz respeito ao que chamamos de case sensitivity ou no bom portugus: maisculas e minsculas. A mscara passada para FindFirstFile() no case sensitive se voc der uma olhada em alguns dos resultados que mostrei at agora, eles incluem ambos arquivos .exe e .EXE, mes-mo embora eu tenha sempre passado *.exe como mscara de busca.

    Existe uma segunda funo da API, FindFirstFileEx(), que muito parecida com a FindFirstFile() que usamos em todo o tutorial. Cheguei a esta funo quando pesquisava (h muito tempo) como fazer uma busca case sensitive de arquivos. Buscando pela internet, encontrei uma referncia dentro da documentao que vinha com o antigo VC++ 6.0 Pro dizendo...

    dwAdditionalFlags Specifies additional flags for controlling the search. You can use the FIND_FIRST_EX_CASE_SENSITIVE flag for case-sensitive searches. The default search is case insensitive. At this time, no other flags are defined.

    ... e, sendo sincero, na poca (quase trs anos atrs) no consegui faz-la funcionar. A documentao mais atua-lizada (que a referncia online que o Visual Studio fornece, ou seja, a da MSDN hoje, 07/04/2010) diz algo dife-rente. Interessante que encontrei duas referncias mesma funo, uma de uso geral e outra como funo do Windows o que me deixou momentaneamente ainda mais intrigado: que outro SO a Microsoft andou desen-volvendo nos ltimos 20 anos? Qu se pasa, Microsoft?

    Bom, de qualquer forma, a descrio para o caso geral ...

    dwAdditionalFlags [in] Unsupported; set to zero.

    ... enquanto que a para o caso Windows

    dwAdditionalFlags [in] Unsupported; set to zero.

    Value Meaning FIND_FIRST_EX_CASE_SENSITIVE 1

    Searches are case-sensitive.

    FIND_FIRST_EX_LARGE_FETCH 2

    Uses a larger buffer for directory queries, which can increase performance of the find operation. Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP/2000: This value is not supported until Windows Server 2008 R2 and Windows 7.

  • C++ Win32 Tutorial

    16

    logo, a concluso a que chego que a prpria Microsoft no tinha conseguido programar esta funcionalidade at pouco tempo. Considerando que ela foi proposta na poca do VC++ 6.0 (no encontrei referncias mais anti-gas funo e sendo que este data de 1998), ento somente este ano, junto com o lanamento do Windows 7 e da verso Server 2008 R2, que eles conseguiram resolver esta pendncia.

  • C++ Win32 Tutorial

    17

    Trabalhando com o Console no Windows, parte 1.

    Que levante a mo quem j fez algo semelhante a isto quando comeou a programar! De repente em outra lin-guagem, talvez em outro sistema operacional, mas certamente foi algo como isso. Algum no se identificou com a situao? Voc no quer ver o cdigo fonte, quer? Est aqui, ento.

    #include using namespace std; int main(){ cout

  • vel individualmente. Este no o caso em um meio baseado em caracteres, onde a menor unidade que pode ser endereada e, portanto, modificada uma

    A rea de exibio de um console consiste de linhas e colunas de clulas de caracteres. uma grade bidimensional ou uma matriz de clulas, todos estes mente, a clula de caracteres no topo esquerdo a origem ou posio (0,0) em um eixo (x,y) imaginrio, A e assim por diante. Em uma janela de console tpicaAssim, em tal console, a clula no canto inferior direito ser nos zeros. A pequena gravura abaixo exibe graficamente o nmero de algumas clulas da poro superior equerda da tela (ou a matriz completa de um console 4x4, no mesmo?).

    A razo pela escolha destes valores zados pela e at mesmo disponveis res chamados "Visual Display Units", o

    Cada clula de caractere, como o nome denuncia, pode conter somente um caractereacontece com janelas baseadas em pixels. facilmente perceptvel esta diferena se eu, por exemplo, digitar cinco caracteres w, e na linha de baixo

    wwwww iiiii As linhas, assim como maior parte deste docuada, e voc pode notar a diferena no comprimento damesmos caracteres, sendo a sada...

    Como podemos ver, cada clula possui um nico caractere ultimo caractere w.

    Certo, ento ns temos uma grade de clulas, mas o que uma clula? De forma simplria, grade, onde cada clula tem 8 pixels de largura por 12 de altura.representados dentro desta grade, incluindo quaisquer espaamentos esto diretamente adjacentes aos pixels da prxima. Isto de certa forma adiante, esta caracterstica tem suas vantagens.nhados em uma clula.

    Note que uma coluna em branco direita do wespao entre eles. Perceba tambm que o ponto do i no alcana o topo da clula, o que assegura que qua

    C++ Win32 Tutorial

    18

    Este no o caso em um meio baseado em caracteres, onde a menor unidade que pode ser endereada e, portanto, modificada uma clula de caractere.

    de exibio de um console consiste de linhas e colunas de clulas de caracteres. uma grade uma matriz de clulas, todos estes termos so equivalentes e igualmente utilizados. Norma

    mente, a clula de caracteres no topo esquerdo a origem ou posio home, e designada clula de caractere imediatamente direita da home

    e assim por diante. Em uma janela de console tpica existem 80 posies de clulas na horizontal e 25 na vertical. onsole, a clula no canto inferior direito ser a (79,24), lembrando que a grade tem sua origem

    nos zeros. A pequena gravura abaixo exibe graficamente o nmero de algumas clulas da poro superior equerda da tela (ou a matriz completa de um console 4x4, no mesmo?).

    80x25 histrica. Antes de os terminais grficos serem amplamente utile at mesmo disponveis para a grande massa, as pessoas usavam terminais baseados em caract

    ", ou VDU's, que tinham tipicamente uma tela com layout

    Cada clula de caractere, como o nome denuncia, pode conter somente um caractere, diferentemente do que acontece com janelas baseadas em pixels. facilmente perceptvel esta diferena se eu, por exemplo, digitar

    , e na linha de baixo cinco caracteres i...

    As linhas, assim como maior parte deste documento, foram editadas com a fonte Calibrie voc pode notar a diferena no comprimento das mesmas. Escrevi um aplicativo console que mostra os

    ...

    cada clula possui um nico caractere o ultimo caractere i est diretamente abaixo do

    Certo, ento ns temos uma grade de clulas, mas o que uma clula? De forma simplria, grade, onde cada clula tem 8 pixels de largura por 12 de altura. Os caracteres que compem a fonte devem ser representados dentro desta grade, incluindo quaisquer espaamentos isso mesmo, os pixels de uma clula

    pixels da prxima. Isto de certa forma uma limitao, mas como veremos adiante, esta caracterstica tem suas vantagens. As figuras abaixo exibem como os caracteres w e i so

    Note que uma coluna em branco direita do w assegura que dois w adjacentes tero pelo menos um pixel de Perceba tambm que o ponto do i no alcana o topo da clula, o que assegura que qua

    Este no o caso em um meio baseado em caracteres, onde a menor unidade que pode ser

    de exibio de um console consiste de linhas e colunas de clulas de caracteres. uma grade ou um vetor so equivalentes e igualmente utilizados. Normal-

    , e designada como um ponto home (1,0), a prxima (2,0)

    existem 80 posies de clulas na horizontal e 25 na vertical. , lembrando que a grade tem sua origem

    nos zeros. A pequena gravura abaixo exibe graficamente o nmero de algumas clulas da poro superior es-

    Antes de os terminais grficos serem amplamente utili-grande massa, as pessoas usavam terminais baseados em caracte-

    layout 80x25.

    , diferentemente do que acontece com janelas baseadas em pixels. facilmente perceptvel esta diferena se eu, por exemplo, digitar

    Calibri, que no monoespa-Escrevi um aplicativo console que mostra os

    o ultimo caractere i est diretamente abaixo do

    Certo, ento ns temos uma grade de clulas, mas o que uma clula? De forma simplria, uma clula outra Os caracteres que compem a fonte devem ser

    isso mesmo, os pixels de uma clula uma limitao, mas como veremos

    As figuras abaixo exibem como os caracteres w e i so dese-

    assegura que dois w adjacentes tero pelo menos um pixel de Perceba tambm que o ponto do i no alcana o topo da clula, o que assegura que qual-

  • C++ Win32 Tutorial

    19

    quer caractere na linha acima a este i poder ser facilmente identificvel. De forma resumida, os caracteres normais so representados em grades 7x11 com uma linha em branco no topo e uma coluna em branco na mar-gem direita.

    Os pixels marcados com um X na figura acima so exibidos (ou aportuguesando, renderizados) na cor do pri-meiro plano (ou foreground, como preferir) e os pixels vazios na cor de fundo (background). Por padro, o pri-meiro plano branco ou cinza claro, enquanto que o fundo preto. Mais adiante mostrarei como mudar estas cores.

    Existe um espao de um pixel nas margens da rea de exibio do console. Isto evita que os caracteres que fa-zem fronteira com os cantos superiores, inferiores ou laterais se fundam com a moldura da janela (ou frame, como preferir). Voc no pode escrever neste espao. Desta forma, a rea de exibio de um console de 2+(8x) pixels de largura, onde x o nmero de caracteres na linha, por 2+(12y) pixels de altura, onde y o nmero de linhas visveis podem existir mais linhas acima ou abaixo da visualizao atual, depende da posio da barra de rolagem.

    A primeira parte deste tutorial acabou se revelando apenas uma teoria pura. Mas muito do que feito no conso-le assume que voc entenda essa mesma teoria que bsica, convenhamos.

    Na prxima parte mostrarei como dar nome ao seu console, obter alguns manipuladores padro, mover o cur-sor, escrever blocos de caracteres e limpar a tela.

  • C++ Win32 Tutorial

    20

    Trabalhando com o Console no Windows, parte 2. Eu comecei a parte 1 deste tutorial com o clssico programa Hello World!. A sada se parecia com isso

    Por padro, o Windows coloca o caminho do executvel no ttulo da janela. Podemos mudar isso para uma pala-vra ou frase qualquer. Aqui est o Hello World! modificado para fazer isso.

    #include #include using namespace std; int main(){ SetConsoleTitle("Hello!"); cout

  • C++ Win32 Tutorial

    21

    }

    Todos os consoles tm trs manipuladores padro e muitas das funes de operao do console precisam de um manipulador para efetuar I/O.

    Um manipulador simplesmente um inteiro de 32bit e representa o modo como o Windows diferencia entre objetos do mesmo tipo. Consideremos o console: ele tem uma barra de ttulo, botes de minimizar, maximizar e encerrar, uma ou mais barras de rolagem etc. Quando se pensa sobre isso, algo um tanto complicado de se lidar e em algum lugar deve existir um monte de dados que o sistema est usando para fazer tudo isso funcionar. O Windows esconde toda esta parte complexa do usurio (voc pode brincar com estes dados se quiser, claro), mas o ponto aqui que voc no tem de faz-lo se no quiser. O Windows toma conta disso e tudo o que voc tem de fazer dizer a ele o manipulador, ou seja l o que for que voc quer usar. Quando voc se aprofundar um pouco mais na programao para Windows, ver que vrias coisas so usadas se fornecendo um manipula-dor. Se voc no consegue entender isso agora, no se preocupe: um manipulador fcil de conseguir e fcil de usar.

    Para conseguir os manipuladores padro, declare uma varivel do tipo HANDLE e a inicie com uma chamada a GetStdHandle(). Esse programa (que na verdade no faz nada!), ilustra o processo. Usaremos os manipuladores padro mais tarde.

    #include int main(){ HANDLE hIn; HANDLE hOut; HANDLE hError; hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE); hError = GetStdHandle(STD_ERROR_HANDLE); return 0; }

    O manipulador de entrada usado com rotinas que lem os dados de um console, o manipulador de sada com rotinas que enviam dados para um console. O manipulador de erros tambm lida com sadas para o console por padro e, francamente, pouqussimo utilizado apenas esteja ciente disso.

    Existem duas rotinas ReadConsole() e WriteConsole() que realizam a I/O usando esses manipuladores, mas, ao menos por enquanto, continuaremos com as funes de I/O padro do C++.

    Agora que conseguimos os manipuladores, vamos fazer algo com eles: mover o caret pela tela. Para fazer isso, precisaremos usar a estrutura COORD. Esta uma estrutura muito simples contendo uma coordenada x e y. Ela declarada da seguinte forma.

    typedef struct _COORD{ SHORT X; SHORT Y; } COORD;

    Para mover o cursor, simplesmente configuramos a COORD para as coordenadas que queremos e chamamos a funo SetConsoleCursorPosition() da API. Aqui est um programa simples que faz isso.

    #include #include using namespace std; int main(){ HANDLE hOut; int i; COORD Position; hOut = GetStdHandle(STD_OUTPUT_HANDLE);

  • C++ Win32 Tutorial

    22

    for (i=0; i

  • C++ Win32 Tutorial

    23

    Position.X = 4; Position.Y = 4; FillConsoleOutputCharacter(hOut, 'X', 15, Position, &Written); return 0; }

    Vejamos o que acontece se eu mudar a sada para 150 ao invs de 15 caracteres.

    O ponto que quero destacar que quando a rotina alcanou o fim da linha, ela continuou do comeo da linha seguinte. Suponha que ao invs da linha 5 coluna 5 eu tivesse dito (0,0), e suponha que eu tivesse usado um ca-ractere de espao ao invs de X, e suponha tambm que eu tivesse pedido funo para escrever caracteres suficientes para encher completamente o buffer da tela Ora! Acabei de limpar a tela! Limpar a tela a pergun-ta mais comum sobre o console que aparece nos fruns. E aqui est a resposta. Por que opto por citar este e-xemplo ao invs de indicar a chamada ao sistema atravs de system(cls)? Porque de repente voc se encon-tra lidando com um display 24x2 de LEDs e no vai ser uma chamada ao sistema que resolver o seu problema alm de que precisamos monitorar o tipo de status que essa chamada devolve. Esta a soluo universal para este problema.

    Tambm possvel ler um ou mais caracteres de uma posio especfica no buffer da tela. Para fazer isso, usa-remos a rotina ReadConsoleOutputCharacter() da API. Essa rotina tem parmetros muito semelhantes lti-ma. Seu prottipo mostrado aqui.

    BOOL ReadConsoleOutputCharacter( HANDLE hConsoleOutput, LPTSTR lpCharacter, DWORD nLength, COORD dwReadCoord, LPDWORD lpNumberOfCharsRead );

    Assim como antes, o primeiro parmetro o manipulador padro. O prximo (lembre-se que ns estamos lendo agora) um ponteiro para onde queremos guardar os caracteres. Os parmetros restantes so como na rotina anterior nmero de caracteres a processar, o ponto de partida e um ponteiro para o lugar onde podemos ar-mazenar o nmero real de caracteres lidos.

    Esse programa usa a rotina duas vezes, uma vez para obter o caractere na posio (0,0) e uma segunda vez para recuperar cinco caracteres a partir da posio (4,0). Se o nmero de caracteres pedido maior que a linha atual, a leitura continuar a partir do incio da linha seguinte, assim como antes. E se o nmero pedido maior do que o tamanho do buffer, o caractere que simboliza o fim do buffer devolvido e o nmero real armazenado no parmetro final.

    #include #include using namespace std; int main(){

  • C++ Win32 Tutorial

    24

    HANDLE hOut; char Letter; char Letters[5]; COORD Where; DWORD NumRead; int i; hOut = GetStdHandle(STD_OUTPUT_HANDLE); cout

  • Trabalhando com o Console no Windows, parte 3.

    Eu recomendo que voc compile e execute console. Se seu console de um tamanho diferente do meu, talvez haja necessidade de se ajustar os valores de x e y, mas essa uma tarefa simples. O resto do tutorial assume que seu mapa de caracteres o mesmo possvel que haja um conjunto de caracteres de outro idioma carser diferentes.

    #include #include using namespace std; int main(){ HANDLE hOut; int i; int x = 0; int y = 0; COORD Position; hOut = GetStdHandle(STD_OUTPUT_HANDLE); for (i=32; i

  • C++ Win32 Tutorial

    26

    mitir espaamentos. Como d para notar na figura acima, estes no so caracteres normais alguns deles se fundem com os que esto acima e abaixo deles, o que gera certo desconforto visual. Aqui esto os mesmos ca-racteres mais espaados, ficando mais fcil de visualizar.

    Esses caracteres especiais no tm espaos na linha superior e nem na coluna direita. Eles foram criados propo-sitalmente para se fundirem com o caractere vizinho. Desta forma, podemos desenhar linhas, grades e caixas. Uma sada que voc pode criar est mostrada abaixo.

    As gravuras parecem tolas quando desenhadas neste tamanho, mas fiz isso apenas como exemplo e tambm porque no queria uma figura enorme nesta seo. Se voc olhar com ateno o mapa de caracteres, ver que so vrias as possibilidades.

    Para criar estas grades, escrevi uma funo bem simples que desenha um caractere em um lugar especfico da tela, mostrada aqui.

    void At(int x, int y, unsigned char What){ static HANDLE hOut; static bool First = true; COORD Position; if (First) { hOut = GetStdHandle(STD_OUTPUT_HANDLE); First = false; } Position.X = x; Position.Y = y; SetConsoleCursorPosition(hOut, Position); cout

  • C++ Win32 Tutorial

    27

    At(1, 0, (unsigned char)218); At(2, 0, (unsigned char)196); At(3, 0, (unsigned char)191); At(1, 1, (unsigned char)179); At(10,1, (unsigned char) 32); At(3, 1, (unsigned char)179); At(1, 2, (unsigned char)192); At(2, 2, (unsigned char)196); At(3, 2, (unsigned char)217);

    Dada a tabela de caracteres acima e esse exemplo simples (alis, 32 o caractere de espao), voc tem informa-es mais do que suficiente para criar todos os tipos de caixas, grades, labirintos, formulrios, etc. Agora pode-mos at fazer jogos ASCII, como o antigo Rogue veremos a questo das cores mais adiante.

    Enquanto desenhar caixas pode ser muito til, s vezes desejamos esconder ou modificar a aparncia do caret. Voc pode fazer isso com outra rotina da API chamada SetConsoleCursorInfo(). Ela recebe somente dois pa-rmetros: um manipulador padro de sada e um ponteiro para a estrutura CONSOLE_CURSOR_INFO. Essa outra estrutura relativamente simples definida pelo Windows, se parecendo com isso...

    typedef struct _CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; } CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

    O primeiro valor um inteiro simples, com valor de 1 a 100, e especifica o quanto a clula de caractere do cursor preenchida. O programa aqui configura o tamanho do cursor para 50. As trs figuras abaixo mostram os tama-nhos 1, 50 e 100. Perceba o uso de um "&" antes de ConCurInf, lembre que a rotina espera um ponteiro para a estrutura que voc declarou.

    #include int main(){ HANDLE hOut; CONSOLE_CURSOR_INFO ConCurInf; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTitle("Tamanho 50"); ConCurInf.dwSize = 50; ConCurInf.bVisible = true; SetConsoleCursorInfo(hOut, &ConCurInf); return 0; }

  • C++ Win32 Tutorial

    28

    O outro valor um campo booleano e indica se o cursor est visvel ou no. Um valor true (o padro) significa que o cursor est visvel, enquanto que false est invisvel. O programa seguinte oculta o cursor.

    #include int main(){ HANDLE hOut; CONSOLE_CURSOR_INFO ConCurInf; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTitle("Sem Cursor"); ConCurInf.dwSize = 10; ConCurInf.bVisible = false; SetConsoleCursorInfo(hOut, &ConCurInf); return 0; }

    Para exibir o cursor, chame a funo novamente com o membro bVisible configurado para true.

    uma boa idia configurar ambos os valores, especialmente na primeira vez que voc chamar a funo. Se con-figurarmos bVisible, mas ignorarmos dwSize, a varivel no especificada pode estar zerada ou conter um valor aleatrio (lixo) que poderia estar fora do intervalo 1-100 e a rotina falhar. De forma semelhante, se quisermos mudar o tamanho do cursor e no indicarmos que bVisible true, seu compilador pode zerar o campo no inicializado e o cursor desaparecer ao invs de mudar de tamanho. Como sempre, se voc conferir o valor de-volvido da chamada, voc ver se erros aconteceram no meio do caminho.

    Lembre-se, o cursor serve para mostrar ao usurio onde est o ponto de insero de caractere. Ocultar o cursor no altera este fato. Se voc esconder o cursor, tenha certeza de criar outros meios para facilitar a navegao pela tela.

    Na prxima seo, veremos como imprimir alguns outros caracteres especiais.

  • C++ Win32 Tutorial

    29

    Trabalhando com o Console no Windows, parte 4.

    At agora evitei utilizar caracteres acentuados e o c acedilhado. Esta parte do tutorial ser dedicada a outro tema muito recorrente nos fruns e que diz respeito nossa lngua ptria. Por gentileza, compile e rode o cdigo seguinte.

    #include using namespace std; int main(){ string sIn; string test = ""; cout sIn; cout

  • C++ Win32 Tutorial

    30

    Se consultarmos a tabela de caracteres usados pelo console veja o primeiro programa da seo anterior , po-demos notar que os caracteres que usamos no portugus esto l. Uma soluo para o problema da codificao seria invocar os caracteres diretamente da tabela utilizada...

    cout

  • C++ Win32 Tutorial

    31

    Agora que podemos manipular os code pages do console, precisamos apenas descobrir como escrever uma s-tring com essa nova codificao. Devo relembrar que a fonte Raster inadequada para trabalhar com outros CPs. A forma mais direta utilizar uma fonte com tabela ANSI ou UNICODE e trabalhar com estes caracteres. Nas propriedades

    8 do console, podemos alterar a fonte de Raster para Lucida Console, que ANSI. Ento teramos

    apenas de invocar alguma funo que escrevesse em ANSI no console. Ora, existe uma rotina que faz justamente o que ns precisamos: WriteConsoleA(). O prottipo desta funo est aqui.

    BOOL WriteConsole( HANDLE hConsoleOutput, const VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved );

    O primeiro argumento o manipulador da tela do console j sabemos como consegu-lo. O segundo um pon-teiro que indica onde nossa frase est armazenada existe um limite de 64K de dados que, convenhamos, mais que suficiente. O terceiro parmetro indica qual a quantidade de caracteres queremos que seja escrito. O quarto um ponteiro para uma varivel que nos dir quantos caracteres foram efetivamente escritos desta forma, caso ocorre uma falha temos como saber onde a rotina parou de escrever. O ltimo argumento, segundo a MSDN, uma varivel reservada e deve ser NULL.

    Desta forma, escrevi o seguinte programa...

    #include #include using namespace std; int main(){ HANDLE hIn, hOut; unsigned int uInCP, uOutCP; DWORD n; char *cIn = ""; // captura os manipuladores de entrada e sada do console hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE); // salva os code pages originais do console uInCP = GetConsoleCP(); uOutCP = GetConsoleOutputCP(); // configura o CP que desejamos SetConsoleCP(1252); SetConsoleOutputCP(1252); // escreve nossa frase WriteConsole(hOut, cIn, strlen(cIn), &n, NULL); // restaura o CP antigo SetConsoleCP(uInCP); SetConsoleOutputCP(uOutCP); return 0; }

    ... que na minha mquina apresentou a seguinte sada.

    8 Basta clicar no cone que aparece no canto superior direito da janela do console.

  • C++ Win32 Tutorial

    32

    Conseguimos alcanar nosso objetivo, mas este mtodo tem suas desvantagens. A primeira coisa que podemos notar a diferena no tamanho da fonte: Lucida tem 7x12 pixels, enquanto a Raster tem 8x12. Se tnhamos um programa que assumia as dimenses padro de fonte, o mesmo no preservar sua aparncia com a nova confi-gurao. Segundo, ns alteramos a tabela de codificao, deixamos de usar a CP-850 e adotamos a CP-1252. Modifiquei o primeiro programa da seo anterior para que mostrasse a tabela de codificao 1252 e consegui a seguinte sada na minha mquina.

    Se o leitor comparar esta tabela com a que conseguimos na seo anterior, ver que a segunda metade dela muito diferente, em especial os caracteres de 179 a 220, que tnhamos usado para criar nossas grades e caixas. Este mtodo afeta seriamente a compatibilidade entre o que os programas acreditam estar escrevendo e o que o console est de fato exibindo. Nosso programa de desenhar caixas j no teria utilidade alguma com esta con-figurao do console precisaramos reescrever nosso cdigo para alcanar este objetivo e ainda correramos o risco de no ter todos os nossos caracteres. Ento podemos concluir que esta tcnica, para o nosso caso, no a mais inteligente a se usar.

    Nenhum dos dois mtodos apresentados at agora conseguiu resolver nosso problema satisfatoriamente. Talvez seja hora de pensarmos um pouco mais e atacar a fonte do mal... isso a! Se a Microsoft tivesse convertido o command prompt para Unicode nada disso aconteceria! Vamos invadir e ocupar Redmond! Exigir um console em utf-8! Abaixo opresso! (O.o) Acalme-se, Padawan, no me referia a esse tipo de ataque rebelde. Devemos nos lembrar que nosso amigo, o emulador de terminal de texto, assim porque precisa manter a compatibilida-de com programas que foram escritos atravs dos anos afinal, compatibilidade tambm significa tornar as so-lues/problemas do passado uma constante no presente.

    Se voc alterou a fonte do seu console, desfaa essa modificao e tentemos outra forma de trabalhar com os caracteres especiais com o console.

    O que ns precisamos de uma forma de converter nossa string de caracteres que est em um code page 1252/Unicode para uma nova string de caracteres equivalentes e reconhecveis pelo CP 850 do console. Existem duas rotinas que, trabalhando em conjunto, podem realizar esta tarefa, mas ambas precisam de cuidados espe-ciais quanto ao uso. So elas: MultiByteToWideChar() e WideCharToMultiByte(). Estas sero as funes mais complicadinhas que usaremos, prometo que as prximas sero mais simples. O prottipo da primeira est aqui.

    int MultiByteToWideChar( UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,

  • C++ Win32 Tutorial

    33

    LPWSTR lpWideCharStr, int cchWideChar );

    MultiByteToWideChar() mapeia um string de caracteres normais para um string de caracteres wide char, sendo aquele string no necessariamente de um conjunto de caracteres multibyte (1252, utf-7, utf-8 etc.). O primeiro parmetro o code page a ser usado para fazer a converso qualquer valor de CP que esteja instalado ou dis-ponvel no sistema operacional. Tambm podemos usar como primeiro argumento os identificadores CP_ACP (indica a codificao padro do Windows), CP_MACCP (codificao do sistema Macintosh) e CP_OEMCP (codifi-cao do sistema OEM

    9), dentre outros mais informaes aqui. O segundo parmetro uma flag que indica o

    tipo de converso para converses com utf-7 e utf-8, este deve ser zero. O terceiro argumento um ponteiro para a string de caracteres a serem convertidos. O quarto parmetro deve ser o tamanho, em bytes, da string indicada no terceiro argumento. O quinto um ponteiro para onde devemos armazenar a string convertida. E o ltimo o tamanho, em caracteres, do buffer indicado no argumento anterior. Caso falhe, esta rotina devolve zero, caso contrrio, devolver o nmero de caracteres escritos na nossa string de traduo ou, se o ltimo parmetro for zero, devolver o tamanho necessrio, em caracteres, que essa mesma string dever ter.

    Perceba que esta rotina tem duas utilidades: converter uma string de um CP a outro escolhendo dentre vrios mtodos de converso e indicar qual o tamanho necessrio da string a receber a traduo.

    Devemos ter muita cautela ao lidar com esta funo j que a mesma pode comprometer o funcionamento do nosso programa. Essa rotina pode causar uma sobrecarga do buffer se mal utilizada, j que precisamos indicar o tamanho de lpMultiByteStr em bytes, enquanto que lpWideCharStr deve ser em caracteres para evitar essa sobrecarga, devemos especificar um tamanho buffer apropriado para o tipo de dados que o mesmo recebe.

    A rotina WideCharToMultiByte() converte uma string utf-16 (wide char) para uma nova string de caracteres. O prottipo dela est aqui.

    int WideCharToMultiByte( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar );

    Assim como na outra funo, o primeiro argumento o code page usado para fazer a converso e o segundo uma flag que indica o tipo de converso. O terceiro parmetro um ponteiro para a string em Unicode que de-vemos converter. O quarto o tamanho em caracteres da string indicada no argumento anterior. O quinto pa-rmetro um ponteiro para um caractere que usaremos caso este no possa ser indicado no novo code page. O sexto e ltimo parmetro um ponteiro para a flag que indica se a funo conseguiu usar os caracteres padro na converso. Esta rotina devolve o nmero de bytes escritos no buffer apontado por lpMultiByteStr se no falhar ou, caso cpMultiByte seja zero, devolve o tamanho em bytes necessrios para fazer a converso do buf-fer indicado por lpMultiByteStr.

    Agora que temos sabemos o que cada funo faz e como se comportam, podemos escrever nossa prpria rotina de converso da codificao 1252 para a CP que o console usa. Consegui chegar seguinte funo.

    #include #include #include #include using namespace std; string CPWin2CPConsole(string sIn);

    9 Original Equipment Manufacturer um regime de comercializao de software que a Microsoft disponibiliza para as empresas. Os softwa-res OEM so manipulados pela prpria Microsoft ou por terceiros para conter modificaes/personalizaes. Neste caso, um code page dito OEM quando no aquele presente no sistema original, e sim uma modificao portugus, por exemplo, sendo que o Windows um produto em ingls.

  • C++ Win32 Tutorial

    34

    int main(){ string sIn = ""; cout

  • C++ Win32 Tutorial

    35

    if (cb = WideCharToMultiByte(CP_OEMCP, 0, wCharArray, sw, temp, wCharNumber, NULL, NULL)) { sOut.assign(temp,cb); }

    A lgica deste if() a mesma do anterior. Eles diferenciam pelo funcionamento. Este converte o contedo Uni-code do que est em wCharArray para o code page do console (CP_OEMCP), armazenando em temp. Assim como o anterior, armazena em cb a quantidade de caracteres convertidos e o utiliza como varivel de controle.

    A rotina assign(), pertecente /, substitui o valor de sOut com o contedo de temp, o que nos deixa com o resultado da converso pronto para ser devolvido pela funo de traduo.

    Acabei criando um cdigo que converte somente do CP do editor para o CP do console. Com pequenas modifica-es, o leitor pode criar uma rotina de converso universal (na medida do possvel). Que tal tentar criar uma CP1ToCP2()?

    Este ltimo mtodo que usamos funciona muito bem quando existem caracteres equivalentes entre as code pa-ges: no adianta forar uma codificao para japons e esperar que o console mostre os caracteres porque as rotinas iro falhar. Este ltimo modelo tambm pode ser muito bem empregado quando lidamos com manipula-o de arquivos: caso a codificao dos strings do cdigo seja diferente da codificao usada no contedo de um arquivo, devemos recorrer a este tipo de converso.

    Parece que no, mas essa briga entre code pages fonte de muito aborrecimento entre os programadores. Enfim, espero ter dado alguma paz de esprito para aqueles que precisam fazer essas converses ao apresentar esta soluo.

    Na prxima parte deste tutorial, exploraremos alguns tpicos relacionados s cores.

  • C++ Win32 Tutorial

    36

    Trabalhando com o Console no Windows, parte 5.

    At agora todas as sadas de console que criamos foram em preto e branco. Esta parte do tutorial mostra como eu fiz isso a embaixo!

    Agora, antes que voc fique muito animado, deixe-me alert-lo de que a paleta de cores disponveis muito limitada. E, honestamente, esta parte cosmtica ao lidar com o console no o foco do nosso currculo, mas no por isso que ela seja menos importante, certo? Afinal, s vezes precisamos agradar ao usurio (o suficiente para que tenhamos bons resultados). Por fim, se voc, estudante de programao, decidiu em algum momento pesquisar sobre Como colorir as letras e o fundo no console ou algo do gnero, certamente se deparou com alguma pgina indicando o uso das funes textcolor() e textbackground() do cabealho CONIO.H, que no faz parte das bibliotecas padro do C/C++ e que, apesar de existirem uma infinidade de cdigos mostrando como usar estas funes, nenhum delas funcionou, no mesmo?

    Vejo-me na obrigao de contar-lhes uma historinha: h aproximadamente 20 anos, quando os PCs mais moder-nos tinham processadores de 100MHz e 16MB de RAM (o Windows no passava de uma interface grfica para o DOS), existia uma ferramenta de programao composta de IDE e compilador chamada Turbo-C, de uma empre-sa muito bem sucedida de nome Borland. Como parte do Turbo-C, existiam bibliotecas que faziam o de melhor no seu tempo. E dentre estas havia uma que se destacava quando o assunto era console: a CONIO.H (CONsole Input/Output). Essa nica biblioteca realizava praticamente tudo o que cobrimos nestes tutoriais e ainda fazia a interface entre o programa e o hardware. Como o Turbo-C era uma das principais ferramentas utilizadas e os programadores j estavam muito habituados a ela, vrios dos novos compiladores que apareceram a posteriori criaram bibliotecas semelhantes s que j existiam no compilador da Borland. To semelhantes que o gcc tem uma de mesmo nome, s que sem vrias das rotinas da CONIO.H original isso porque naquela poca, os PCs rodavam basicamente o MS-DOS e o DR-DOS, muito semelhantes em arquitetura (reduzindo os problemas de portabilidade), diferentemente do que acontece hoje, o que torna impossvel o uso destas rotinas. Ento no se decepcione se voc caiu nessa armadilha certamente uma das primeiras bibliotecas que voc veio a usar quando estava aprendendo C ou C++ foi a conio.h e acredito que todos ns fomos seduzidos por ela em algum momento. Mas agora, deixemos o passado de lado e trabalhemos com as solues do presente.

    Ns usaremos a funo SetConsoleTextAttribute() da WIN32 API. Essa funo recebe dois argumentos. O primeiro o manipulador padro j cobrimos essa parte. O segundo uma mascara de bit WORD de 16bit con-tendo zero ou mais bits.

    No necessrio se preocupar com os valores dos vrios bits. Existe uma srie de constantes que esto definidas na windows.h (na verdade, na wincon.h, mas esta est includa na windows.h), que simbolicamente permitiro a voc construir os valores necessrios de forma fcil. Olhemos para este programa e sua sada.

    #include #include using namespace std;

  • C++ Win32 Tutorial

    37

    int main(){ HANDLE hOut; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, FOREGROUND_RED); cout

  • C++ Win32 Tutorial

    38

    A operao OR bit-a-bit "|" combina os bits passados como argumento. Por isso, vermelho | verde resulta em amarelo, verde | azul resulta em ciano, azul | vermelho resulta em magenta e todos os trs combinados do branco. A ordem no importante, vermelho | azul d o mesmo resultado que azul | vermelho.

    Outro bit que pode sofrer operaes OR FOREGROUND_INTENSITY. Todas as cores que mostrei at agora esto um pouco apagadas, mas ao adicionar o bit de intensidade, as cores parecem brilhar. Esse programa mostra o uso e os resultados do bit de intensidade.

    #include #include using namespace std; int main(){ HANDLE hOut; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, FOREGROUND_RED); cout

  • C++ Win32 Tutorial

    39

    O preto conseguido ao se configurar nenhuma cor no primeiro plano (afinal, preto a ausncia de cores). En-to, no total ns temos 15 cores Eu avisei para no ficar muito animado...

    Alm de podermos mudar a cor do primeiro plano (a fonte), tambm podemos alterar as cores de fundo. As mesmas cores esto disponveis.

    #include #include using namespace std; int main(){ HANDLE hOut; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, BACKGROUND_RED); cout

  • C++ Win32 Tutorial

    40

    Um caractere de espao ser exibido apenas como um fundo colorido. Foi assim que criei a logomarca da Uni-versidade de Braslia no incio dessa seo a biblioteca graphics.h permitiria criar uma logomarca mais arre-dondada, prximo do real, mas o estudo da mesma foge do escopo deste tutorial.

    possvel configurar as cores da fonte e do fundo simultaneamente. Aqui, por exemplo, configurei a cor de fun-do para amarelo e a fonte para ciano intenso.

    #include #include using namespace std; int main(){ HANDLE hOut; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, BACKGROUND_GREEN | BACKGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); cout

  • C++ Win32 Tutorial

    41

    cout

  • C++ Win32 Tutorial

    42

    Quando utilizamos o par de aspas duplas, estamos instruindo o pr-processador a buscar os arquivos do cami-nho especificado por I (gcc) ou /I (Visual Studio C++): no primeiro caso, no mesmo diretrio em que seu fonte se encontra e, no segundo, naquele caminho especficado. Quando utilizamos os sinais de menor e maior que, estamos instruindo o pr-processador a buscar os arquivos presentes nas variveis do ambiente.

    #ifndef SHORTCOLOURS_H #define SHORTCOLOURS_H const int FW = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; const int FR = FOREGROUND_RED; const int FG = FOREGROUND_GREEN; const int FB = FOREGROUND_BLUE; const int FY = FOREGROUND_RED | FOREGROUND_GREEN; const int FC = FOREGROUND_GREEN | FOREGROUND_BLUE; const int FM = FOREGROUND_BLUE | FOREGROUND_RED; const int FWI = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; const int FRI = FOREGROUND_RED | FOREGROUND_INTENSITY; const int FGI = FOREGROUND_GREEN | FOREGROUND_INTENSITY; const int FBI = FOREGROUND_BLUE | FOREGROUND_INTENSITY; const int FYI = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; const int FCI = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; const int FMI = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY; const int FNULL = 0; const int BW = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; const int BR = BACKGROUND_RED; const int BG = BACKGROUND_GREEN; const int BB = BACKGROUND_BLUE; const int BY = BACKGROUND_RED | BACKGROUND_GREEN; const int BC = BACKGROUND_GREEN | BACKGROUND_BLUE; const int BM = BACKGROUND_BLUE | BACKGROUND_RED; const int BWI = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; const int BRI = BACKGROUND_RED | BACKGROUND_INTENSITY; const int BGI = BACKGROUND_GREEN | BACKGROUND_INTENSITY; const int BBI = BACKGROUND_BLUE | BACKGROUND_INTENSITY; const int BYI = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY; const int BCI = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY; const int BMI = BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY; const int BNULL = 0; #endif

    Originalmente tinha usado #defines ao invs de vrios const int. Me foi indicado, no entanto, algumas razes pelas quais devemos preferir a segunda forma em detrimento da primeira. Favor consultar o endereo eletrni-co http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7 para esclarecimentos.

    Este programa produz a mesma sada que o ltimo.

    #include #include #include "ShortColours.h" using namespace std; int main(){ HANDLE hOut; hOut = GetStdHandle(STD_OUTPUT_HANDLE); cout

  • C++ Win32 Tutorial

    43

    return 0; }

    A paleta de cores muito limitada, mas no fim muito til. Lembre-se, precisamos apenas que a interface seja boa o suficiente para conseguir um trabalho bem feito.

    Na parte 2 deste tutorial, mostrei como usar a ReadConsoleOutputCharacter() para recuperar os caracteres em uma ou mais posies do buffer. Por si s, a rotina no capaz de recuperar os atributos de primeiro plano e fundo desses caracteres coloridos. Esta informao tambm est disponvel, mas atravs de outra rotina chama-da ReadConsoleOutputAttribute(), que muito semelhante ReadConsoleOutputCharacter(). O prottipo da funo est aqui.

    BOOL ReadConsoleOutputAttribute( HANDLE hConsoleOutput, LPWORD lpAttribute, DWORD nLength, COORD dwReadCoord, LPDWORD lpNumberOfAttrsRead );

    Esse programa configura a cor do primeiro plano para FOREGROUND_GREEN | FOREGROUND_RED usando uma s-tring como sada. Ento usa ReadConsoleOutputAttribute() para recuperar o atributo do caractere em (4,0). O resultado 6, isso porque FOREGROUND_GREEN est definido para ter o valor 0x0002 e FOREGROUND_RED 0x0004. 2+4=6. Se voc se sente muito vontade em trabalhar com os valores numricos, use-os. Particular-mente, prefiro usar as constantes j definidas por tornar o cdigo mais compreensvel.

    #include #include using namespace std; int main(){ HANDLE hOut; WORD Attribute; COORD Where; unsigned long NumRead; hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_RED); cout

  • C++ Win32 Tutorial

    44

    Trabalhando com o Console no Windows, parte 6. Esta seo do tutorial abordar alguns poucos tpicos que, embora no sejam difceis de usar, podem ser um pouco complicados de se entender conceitualmente. Vou me esforar para que voc, leitor, ao menos assimile o que tentarei transmitir quase certeza de que vou me complicar, mas no custa nada arriscar. O que usare-mos agora so os eventos do console. Um evento algo que acontece ao console. Existem vrios tipos de e-ventos, mas neste tutorial iremos nos focar naqueles relacionados ao teclado e ao mouse. A manipulao dos eventos do console um assunto muito extenso; no tenho conhecimento suficiente e nem capacidade de co-brir tudo em um tutorial pequeno como este mas se mesmo assim, voc estiver interessado no tema, d uma lida na MSDN ou procure pela documentao do seu compilador.

    A funo que usaremos para conseguir os eventos do console a ReadConsoleInput(). Essa funo recebe qua-tro parmetros. O primeiro argumento o manipulador de entrada, que ns j vimos a respeito.

    O segundo parmetro um ponteiro para uma estrutura INPUT_RECORD. Esta uma estrutura razoavelmente simples que contm um valor indicando o tipo de evento que aconteceu (teclado, mouse etc.), e outra estrutura que contm os detalhes do evento.

    O terceiro parmetro o nmero de eventos a se ler. Os eventos no console so armazenados em um buffer o que significa que o sistema continua gravando eventos assim que eles acontecem, mas somente os entrega ao seu programa quando voc pergunta por um. Eles so entregues na ordem em que aconteceram. Frequente-mente voc perguntar por um evento de cada vez, ou seja, pega um evento, processa e pega o prximo. Voc pode, se desejar, usar um terceiro parmetro para ler vrios eventos em um array de estruturas INPUT_RECORD. Se voc perguntar por mais eventos do que existe no buffer, a rotina devolver aqueles que esto disponveis e o quarto parmetro conter o nmero verdadeiro de eventos recuperados, ou seja, Ele no ir esperar at que exista o nmero pedido.

    Algo muito importante a se destacar que, se no existem eventos no buffer, a chamada a ReadConsoleIn-put() no devolver valor algum! uma chamada bloqueadora (blocking call), ir aguardar at que exista um. Em um programa mais adiante, mostrarei como evitar esse bloqueio at que o evento ocorra.

    Considere este ptograma. interessante que voc o compile e execute.

    #include #include using namespace std; int main(){ HANDLE hIn; HANDLE hOut; COORD KeyWhere; COORD MouseWhere; COORD EndWhere; bool Continue = true; int KeyEvents = 0; int MouseEvents = 0; INPUT_RECORD InRec; DWORD NumRead; hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE); cout

  • C++ Win32 Tutorial

    45

    ReadConsoleInput(hIn, &InRec, 1, &NumRead); switch (InRec.EventType) { case (KEY_EVENT): ++KeyEvents; SetConsoleCursorPosition(hOut, KeyWhere); cout

  • C++ Win32 Tutorial

    46

    ... a structure INPUT_RECORD tem um membro chamado EventType, que diz qual tipo de evento est sendo re-portado. O Windows define constantes para os valores devolvidos e, neste caso, ns conferimos se o evento flagrado uma KEY_EVENT um evento do teclado ou MOUSE_EVENT um evento do mouse. Em ambos os ca-sos, incrementamos um contador. No caso dos eventos do teclado, h esse pedao adicional de cdigo...

    if (InRec.Event.KeyEvent.uChar.AsciiChar == 'x') { SetConsoleCursorPosition(hOut, EndWhere); cout

  • C++ Win32 Tutorial

    47

    COORD LoopWhere; COORD EndWhere; bool Continue = true; DWORD EventCount; int LoopCount = 0; int KeyEvents = 0; INPUT_RECORD InRec; DWORD NumRead; unsigned char HoldKey; hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE); cout

  • C++ Win32 Tutorial

    48

    Novamente, o lao do programa roda at que a varivel Continue se torne falsa. O programa exibe o contador do loop que reiniciado sempre que uma tecla pressionada.

    Sleep(10); // Para desacelerar um pouco o programa!!!

    Adicionei esta linha porque o lao estava sendo executado muito rpido e no conseguia ver os resultados na mquina em que estou trabalhando um Pentium Centrino T6600. Remova ou altere o valor de Sleep() essa chamada causa a suspenso da execuo do programa por um nmero especfico de milisegundos.

    GetNumberOfConsoleInputEvents(hIn, &EventCount);

    Esta funo olha adiante para ver quantos eventos esto na fila de entrada do programa, ou seja, o nmero devolvido por EventCount.

    while (EventCount > 0) { ReadConsoleInput(hIn, &InRec, 1, &NumRead); ... GetNumberOfConsoleInputEvents(hIn, &EventCount); }

    O lao interno recupera e processa os eventos que nos interessam caso existam.

    if (InRec.EventType == KEY_EVENT) { ... }

    Este bloco if() est dizendo que s estamos interessados nos eventos do teclado, como no caso de KEY_EVENT no ltimo programa.

    if (InRec.Event.KeyEvent.bKeyDown) { HoldKey = InRec.Event.KeyEvent.uChar.AsciiChar; } else { ... }

    Esse if() precisa ser melhor explicado. O membro bKeyDown da KEY_EVENT_RECORD um campo booleano e true se o evento reportar que uma tecla est sendo pressionada (lembre-se que o auto repeat como uma srie de eventos key down), e false se for um evento key up, (reportado uma vez). Fazendo essa conferncia para ver se isso false ns conseguimos um nico evento para uma pressionada de tecla mesmo se o usurio a segu-rar por um tempo o que costuma ser muito til. isto o que queria fazer neste programa. Ento por que preci-so manter uma cpia da tecla pressionada no evento de key down? Explicarei isto em breve.

    ++KeyEvents; SetConsoleCursorPosition(hOut, KeyWhere); cout

  • C++ Win32 Tutorial

    49

    Se o evento um key up, ns incrementamos o contador de eventos e zeramos o contador do lao. Macete old school que aprendi enquanto brincava de recriar jogos ASCII.

    if (HoldKey == 'x') { if (InRec.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED) { SetConsoleCursorPosition(hOut, EndWhere); cout

  • C++ Win32 Tutorial

    50

    HANDLE hOut; COORD MouseWhere = {19, 1}; COORD DClickWhere = {19, 2}; COORD LeftWhere = {19, 3}; COORD RightWhere = {19, 4}; COORD WheelWhere = {19, 5}; COORD LoopWhere = {0, 6}; COORD EndWhere = {0, 7}; bool Continue = true; DWORD EventCount; int LoopCount = 0; INPUT_RECORD InRec; DWORD NumRead; hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE); cout

  • C++ Win32 Tutorial

    51

    } else { cout

  • C++ Win32 Tutorial

    52

    Se dwEventFlags zero, um evento de boto do mouse aconteceu. A maioria dos mouses modernos tem pelo menos trs botes, mas no passado todos tinham somente dois. Mantenha isso em mente enquanto explico o modo aparentemente ridculo de conferir que boto foi pressionado ou solto!

    Primeiro, perceba que, assim como as teclas, um boto do mouse indo para cima e para baixo so reportados como eventos diferentes. Tradicionalmente, o evento boto up que considerado significante e eu te aconse-lharia a projetar seus programas com isso em mente. Simplesmente, se um usurio colocar o ponteiro do mouse onde ele quer clicar, pressionar o boto e ento mudar de idia (quem nunca fez isso?), ele pode mover o pon-teiro para longe de onde estava e soltar o boto.

    O bit 0 (0x01) no membro dwButtonState de MOUSE_EVENT_RECORD representa o boto esquerdo no mouse se fizermos um teste mandando imprimir a constante FROM_LEFT_1ST_BUTTON_PRESSED, veremos que seu valor 0x01. Fazemos um AND bit a bit para ver se o bit est ativo: se est, imprimimos down, caso contrrio, impri-mimos up.

    O bit 1 (0x02) representa o boto direito e ns fazemos o mesmo que foi feito antes para verificar seu estado novamente, RIGHTMOST_BUTTON_PRESSED uma constante de valor 0x02.

    Se tivermos um mouse com mais botes, a constante FROM_LEFT_2ND_BUTTON_PRESSED o valor 0x04, FROM_LEFT_3RD_BUTTON_PRESSED 0x08 e FROM LEFT_4TH_BUTTON_PRESSED 0x10.

    Confesso que fiquei muito tentado em usar os valores numricos no exemplo anterior se o fizesse, estaria quebrando minha prpria regra de usar sempre as constantes! O fato que a Microsoft nunca foi muito boa quando o assunto nomenclatura (tanto de seus produtos quanto de suas constantes). Ento no se surprenda se um dia voc encontrar que ESSA_CONSTANTE_GIGANTE_REPRESENTA_ISSO seja apenas um bit, como o caso no exemplo acima. Esses nomes enormes atrapalham no layout do cdigo fonte fazendo com que uma li-nha fique muito longa, ultrapassando a margem direita (100 caracteres no meu caso) e precisando ser quebrada em mais linhas. Em contrapartida, facilitam a vida do programador quando ele no precisa passar por situaes por que, cargas dgua, tem um 0x01 aqui?

    else if (InRec.Event.MouseEvent.dwEventFlags == MOUSE_MOVED) { SetConsoleCursorPosition(hOut, MouseWhere); cout

  • C++ Win32 Tutorial

    53

    As rodinhas do mouse so uma inovao relativamente nova. Verses anteriores do Windows (pr-Windows 2000) nem oferecem suporte a esta funcionalidade. Se voc tem um mouse com roda e quer us-lo, voc pode embora eu no tenha encontrado documentao na MSDN sobre os eventos que dizem se a roda foi rolada para cima ou para baixo. A soluo que encontrei satisfatria, mas como no a vi em documentao nenhuma, no posso garantir que ela sempre funcione.

    Eu no demostrei isso, mas possvel usar os mesmos eventos das teclas Ctrl com os eventos do mouse assim como usamos com os eventos do teclado.

    Na prxima parte deste tutorial, investigaremos sobre as dimenses do console e algumas outras funes ge-ralmente teis.

  • C++ Win32 Tutorial

    54

    Trabalhando com o Console no Windows, parte 7.

    Nesta parte do tutorial, quero trabalhar as dimenses do console. Para fazer isso, existem dois itens que preci-sam ser discutidos. O primeiro a janela atual que contm o console e o segundo a grade de clulas de carac-teres chamada de buffer de tela do console, que descrevi na parte 1 deste tutorial.

    Para demonstrar a co-relao entre os dois, abra um console qualquer um dos programas no tutorial servir para esta tarefa. Coloque o ponteiro do mouse no canto inferior direiro e tente redimensionar a janela. Perceba que possvel tornar a janela menor do que era originalmente e, note tambm que, a janela no redimensio-nada suavemente ao contrrio, ela o faz aos trancos. Perceba que se no existirem barras de rolagem, elas aparecero quando necessrio. Se voc tentar tornar a janela muito larga, assim que a barra de rolagem desapa-recer, o console trava sua dimenso horizontal. Dependendo das configuraes do Windows que esteja usan-do, voc provavelmente conseguir alongar verticalmente a janela at certo ponto. A razo para esses compor-tamentos que observamos est na forte ligao entre os tamanhos da janela e o buffer da tela.

    A janela exibe uma grade de clulas de caracteres. A razo para a janela redimensionar aos trancos porque ela no mostra partes de clulas eventos de redimensionamento menores que uma clula (comprimento ou altu-ra) so ignorados. possvel que a janela mostre menos que o tamanho todo do buffer da tela na verdade, este o caso normal por isso podemos reduzir a janela. Mas impossvel que ela exiba uma rea maior que o buf-fer de tela, portanto, no conseguimos estic-la indefinidamente.

    Claramente, o tamanho do buffer da tela importante, mas qual o tamanho dele? Quo grande ele ? O Win-dows fornece rotinas da API para obter esta informao. Este pequeno programa mostra como obter esta infor-mao.

    #include #include using namespace std; int main() { HANDLE hOut; CONSOLE_SCREEN_BUFFER_INFO SBInfo; hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &SBInfo); cout

  • C++ Win32 Tutorial

    55

    Apenas um lembrete: o buffer da tela tem origem no zero, ou seja, a clula superior esquesda [0,0] logo, o buffer da minha tela comea em [0,0] e termina em [79,299] .

    Vamos dar uma olhada rpida nos outros membros da SBInfo. dwCursorPosition outra COORD, e devolve as coordenadas da clula que atualmente contm o cursor. wAttributes um buffer de 16bit que devolve os atri-butos de cores atuais do primeiro plano (fonte) e do segundo plano (fundo), j discutidos na parte 4 deste tuto-rial. Os outros dois membros so srWindow e dwMaximumWindowSize, que ns usaremos posteriormente, ento eu no os explicarei aqui.

    A seguir, ns faremos de forma computacional o exerccio que voc fez antes mover o canto inferior direito da janela. Aqui est o programa.

    #include #include using namespace std; int main(){ HANDLE hOut; CONSOLE_SCREEN_BUFFER_INFO SBInfo; SMALL_RECT DisplayArea = {0, 0, 0, 0}; int x; int y; hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &SBInfo); x = SBInfo.srWindow.Right; y = SBInfo.srWindow.Bottom; DisplayArea.Bottom = y; While (x > (SBInfo.srWindow.Right / 2)) { --x; DisplayArea.Right = x; SetConsoleWindowInfo(hOut, true, &DisplayArea); Sleep(50); } While (y > (SBInfo.srWindow.Bottom / 2)) { --y; DisplayArea.Bottom = y; SetConsoleWindowInfo(hOut, true, &DisplayArea); Sleep(50); } While (x != SBInfo.srWindow.Right) { ++x; DisplayArea.Right = x; SetConsoleWindowInfo(hOut, true, &DisplayArea); Sleep(50); } While (y != SBInfo.srWindow.Bottom) { ++y; DisplayArea.Bottom = y; SetConsoleWindowInfo(hOut, true, &DisplayArea); Sleep(50); } return 0; }

    Desta vez no exibirei a sada, como na maioria dos exemplos anteriores, porque uma figura no seria capaz de mostrar o que o programa faz. Por gentileza, compile, rode e veja por voc mesmo o funcionamento do aplicati-

  • C++ Win32 Tutorial

    56

    vo. O que voc deveria ver a janela do console comear com certas dimenses, encolher horizontalmente e ento verticalmente para metade do tamanho que ela tinha e, por fim, se expandir para o tamanho original, primeiro horizontalmente e depois verticalmente. Vamos dar uma olhada no cdigo-fonte.

    x = SBInfo.srWindow.Right; y = SBInfo.srWindow.Bottom;

    A primeira coisa que fazemos chamar GetConsoleScreenBufferInfo(), como antes. Desta vez, ns usamos o membro srWindow, que uma estrutura SMALL_RECT contendo quatro short integers chamados Top, Left, Bot-tom e Right. Neste caso, a funo devolve um SMALL_RECT contend a regio rectangular do buffer da tela que est sendo atualmente exibida dentro da janela no meu sistema, a area inicialmente [0,0] a [24,79], ou seja, 25 linhas cada uma com largura de 80 caracteres. Armazeno os membros Bottom e Right da estrutura para uso posterior.

    DisplayArea.Bottom = y; While (x > (SBInfo.srWindow.Right / 2)) { --x; DisplayArea.Right = x; SetConsoleWindowInfo(hOut, true, &DisplayArea); Sleep(50); }

    No incio do programa, declarei outra estrutura SMALL_RECT chamada DisplayArea. Fiz isso porque a rotina que usaremos para mudar o tamanho da janela, na verdade, no quer saber quo grande a janela deveria ser!Ao invs disso, ela quer saber que parte do buffer da tela deveria ser mostrada.

    Existem dois modos de se fazer isso: absoluta ou relativa. Neste programa usamos as especificaes absolutas, desta forma, usamos uma estrutura SMALL_RECT para indicar o canto superior esquerdo e o canto inferior direito da rea do buffer da tela que queremos exibir. A janela ento se ajusta para mostrar aquela regio.

    Todos os membros da estrutura DisplayArea foram inicializados para zero, por isso Top e Left j esto corre-tos, mas preciso configurar manualmente o membro Bottom para ser igual altura original da janela. Atravs de um lao, decrementando x, atualizo a estrutura DisplayArea e chamo SetConsoleWindowInfo(). O primeiro parmetro o manipulador de sada. O prximo um parmetro booleano neste caso, eu o configure para true indicando que a estrutura DisplayArea contm coordenadas absolutas dentro do buffer da tela, como descrito acima. Finalmente, eu passo um ponteiro para a estrutura DisplayArea e a funo reduz o comprimen-to em uma clula de caractere. O lao continua at que o comprimento esteja pela metade do original. A pre-sena do Sleep() no lao apenas reduz a velocidade do programa para podermos ver o que est acontecendo.

    Os outros trs laos so, basicamente, como este aqui, e reduzem a altura pela metade e, ento expande a jane-la para seu tamanho original.

    Quando usar SetConsoleWindowInfo(), lembre que voc no pode perguntar janela para exibir partes que no existem do buffer da tela! Se voc pedir para exibir valores negatives no modo absolute ou maiores do que o comprimento ou a altura do buffer, a chamada ir falhar e devolver false. Analisando o cdigo, percep-tvel que eu no verifiquei o estado das rotinas da API nestes exemplos coisa que voc deve sempre fazer, mesmo que voc no saiba como corrigir as falhas, til saber que rotina falhou e, se possvel, o porqu.

    No prximo programa, usaremos ambas especificaes absolutas e relativas para criar um programa que rola o texto para cima, baixo, esquerda, direita e at mesmo diagonalmente. Aqui est o fonte.

    #include #include #include #include using namespace std; int main(){ HANDLE hOut; CONSOLE_SCREEN_BUFFER_INFO SBInfo;

  • C++ Win32 Tutorial

    57

    SMALL_RECT DisplayArea = {5, 5, 0, 0}; SMALL_RECT ScrollRight = {-1, 0, -1, 0}; SMALL_RECT ScrollDown = {0, -1, 0, -1}; SMALL_RECT ScrollLeft = {1, 0, 1, 0}; SMALL_RECT ScrollUp = {0, 1, 0, 1}; int Lines; int Characters; int Random; int i; hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &SBInfo); DisplayArea.Bottom = SBInfo.srWindow.Bottom - 5; DisplayArea.Right = SBInfo.srWindow.Right - 5; SetConsoleWindowInfo(hOut, true, &DisplayArea); srand((unsigned)time(NULL)); for (Lines = 0; Lines < (SBInfo.srWindow.Bottom - 1); Lines++) { for (Characters = 0; Characters < 80; Characters++) { Random = (rand() % 32); If (Random > 25) { cout

  • C++ Win32 Tutorial

    58

    DisplayArea.Bottom = SBInfo.srWindow.Bottom; DisplayArea.Right = SBInfo.srWindow.Right; SetConsoleWindowInfo(hOut, true, &DisplayArea); return 0; }

    Novamente no mostrarei a sada: rolagem de texto no perceptvel em um bitmap. O que voc deve ver uma tela com um falso texto rolando para a direita, para baixo, para a esquerda, para cima e, finalmente, diago-nalmente para baixo e para a direita. Olhemos o cdigo.

    GetConsoleScreenBufferInfo(hOut, &SBInfo); DisplayArea.Bottom = SBInfo.srWindow.Bottom - 5; DisplayArea.Right = SBInfo.srWindow.Right - 5; SetConsoleWindowInfo(hOut, true, &DisplayArea);

    Como no cdigo anterior, peguei as informaes sobre o tamanho da janela, configurei os membros Bottom e Right da estrutura DisplayArea Top e Left foram configuradas na inicializao e ento usei as especifica-es absolutas para reduzir o tamanho da janela em 5 clulas em todas as direes.

    srand((unsigned)time(NULL)); for (Lines = 0; Lines < (SBInfo.srWindow.Bottom - 1); Lines++) { for (Characters = 0; Characters < 80; Characters++) { Random = (rand() % 32); if (Random > 25) { cout

  • C++ Win32 Tutorial

    59

    Como antes, existem vrias chamadas a Sleep() por todo o programa. Elas apenas desaceleram as coisas para que possamos ver o funcionamento do programa.

    DisplayArea.Top = 0; DisplayArea.Left = 0; DisplayArea.Bottom = SBInfo.srWindow.Bottom; DisplayArea.Right = SBInfo.srWindow.Right; SetConsoleWindowInfo(hOut, true, &DisplayArea);

    Finalmente, o programa usa outra chamada em modo absoluto para restaurar a janela ao seu tamanho original.

    O buffer da tela tem nos limitado em tudo o que fizemos at agora. Vamos mudar o tamanho do buffer para que possamos fazer coisas maiores, ao invs de menores. Este primeiro programinha mostra como faz-lo e destaca um ponto importante. Aqui est o cdigo.

    #include #include using namespace std; int main(){ HANDLE hOut; CONSOLE_SCREEN_BUFFER_INFO SBInfo; COORD NewSBSize; hOut = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(hOut, &SBInfo); NewSBSize.X = SBInfo.dwSize.X + 5; NewSBSize.Y = SBInfo.dwSize.Y; Sleep(2000); cout

  • C++ Win32 Tutorial

    60

    dos meus sonhos! Para compensar esta limitao, o Windows coloca uma barra de rolagem direita do console que nos permite ver o contedo do buffer verticalmente.

    De forma semelhante, no ultimo programa, quando o buffer da tela era mais comprido do que a janela podia mostrar, uma barra de rolagem apareceu no canto inferior da janela do console, nos permitindo rolar horizon-talmente o buffer. Por fim, podemos aumentar o tamanho do buffer at o limite da tela, mas no podemos criar janelas maiores que a tela. O que voc precisa saber qual o maior tamanho possvel da janela dada o tamanho da tela e da fonte.

    J respondemos parcialmente a isso. O ltimo membro restante da estrutura CONSOLE_SCREEN_BUFFER_INFO que temos usado dwMaximumWindowSize, outra COORD.

    #include #include using namespace std; int main(){ HANDLE hOut; CONSOLE_SCREEN_BUFFER_INF