logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

Add to Google Subscribe with Bloglines

Talvez você ache interessante ver a lista com todos os posts ou minha lista com os melhores posts.

1, 2, 3 testando

Teste, 1, 2, 3.

Teste de formatação e de edição

"Mais um teste"

Em 21/07/2004 16:07 - Comentários (9)

Curso de Spam no SEBRAE

Esses dias encontrei um amigo que montou uma empresa de consultoria na área de telefonia. Ele veio me mostrar os impressos de divulgação da empresa, e me contou que estava fazendo divulgação via e-mail. Quando eu perguntei como ele conseguia os e-mails, ele falou que um conhecido dele o tinha emprestado um CD com milhões de e-mails e com os programas para enviá-los...

Eu expliquei que isso era spam, que era ilegal, que nenhuma empresa decente faz esse tipo de coisa, que as pessoas ficam irritadas com e-mails não solicitados, etc, etc. Foi quando ele argumentou que o e-mail era o meio mais barato de divulgar a empresa, e que se 0,01% dos milhões de e-mails derem retorno já é uma grande coisa.

No final ele me contou que fez um curso no SEBRAE, e foi lá que ele aprendeu as vantagens da "divulgação por e-mail"..

Em 22/07/2004 04:39 - Comentários (2)

Por isso que eu não compro livro traduzido....

Lançaram o famoso livro sobre "Refactoring" em português. Os caras tiveram a capacidade de traduzir "Refactoring" para refatoração... não é de matar? Por isso que eu compro direto da amazon...

Em 23/07/2004 13:34 - Comentários (0)

Tudo muda, o tempo todo

Eu estava lendo o Programming the Microsoft Windows Driver Model, do Walter Oney para tirar umas dúvidas sobre IRPs, e acabei abrindo o livro na parte que ele explica como funcionam as exceções em kernel mode. Ele também falava sobre o que acontece quando uma exceção é disparada e como usar blocos de __try/__except e __try/__finally para tratar uma exceção.

Nessa parte ele explica que quando uma exceção é disparada, o kernel sai procurando na pilha alguém que a trate. Depois ele fala que isso pode gerar problemas de performance, já que é necessário percorrer toda a pilha atrás de alguma função que lide com essa exceção. Eu também me lembro que muita gente não usa as exceções em C++ por causa de performance (fora os motivos apontados pelo Joel).

Performance, performance, performance. Hoje em dia, muitas dessas pessoas que falavam isso estão usando .NET, que tem um problema de performance muito maior, já que a runtime roda MUITO código além do seu. (Isso me lembra de programadores C reclamando que o compilador C++ gera código que eles não pediram, como o do copy constructor). É engraçado como a noção de prioridade performance/facilidade muda com o tempo. O jeito é não dar muito ouvidos e testar qual metodologia e linguagem que melhor resolve o seu problema.

Em 23/07/2004 23:54 - Comentários (0)

Como escrever um comparativo técnico tendencioso e sem fundamentos

Um sujeito que trabalha para a Oracle, fez um comparativo simplesmente ridículo entre ASP.NET e PHP. Escreveu um monte de abobrinhas malhando o ASP.NET e favorecendo o PHP, sem apontar dados concretos sobre os problemas que ele diz que o ASP.NET tem. Eu acho o PHP realmente muito bom, mas tentar divulgá-lo as custas de um comparativo ridículo desses, já é sacanagem.

E, pelo jeito, eu não fui o único que não gostou muito desse artigo. Olhe no final o rating do artigo, a maioria dos leitores também achou o artigo uma porcaria.... :-)

Em 26/07/2004 12:15 - Comentários (0)

Kernel mode dev lives!

A MSDN está publicando artigos sobre desenvolvimento de drivers com uma certa regularidade. Parece que o pessoal de kernel mode da Microsoft resolveu aparecer... O ultimo é sobre thread context e IRQLs.

Será que isso vai ajudar a reduzir nossa dependência do pessoal da OSR e da PCAUSA?

Em 26/07/2004 12:32 - Comentários (0)

Novos iPAQs

Meu velho 3650 parece uma carroça velha perto dos novos iPAQs. Um deles tem um processador de 624 Mhz, mais do que o meu Pentium III 600 que eu estava usando para desenvolver com Visual Studio.NET / WinDbg / VMWare até uma semana atrás.

Eu acho que não vai demorar muito para que um iPAQ rode uma máquina virtual com um Windows 9x/NT dentro. Eu já li alguma coisa, não me lembro onde, dizendo que estavam portando o bochs para PocketPC.

Em 26/07/2004 12:57 - Comentários (0)

História do Pentium

O pessoal da ArsTechinica publicou um ótimo artigo sobre história do Pentium, sob o ponto de vista técnico e histórico. Bem interesante, principalmente para quem é interessado em arquitetura de computadores e tem curiosidade sobre o funcionamento de processadores.

Em 26/07/2004 13:13 - Comentários (0)

Lembra daquelas histórias de suporte?

Quem nunca juntou os amigos para tirar um sarro lembrando daquelas histórias de suporte e de perguntas estúpidas de usuários? Encontrei um site recheado dessas histórias. Olhe uma:

Cliente: "Eu quero meus CDs de volta."
Suporte: "Seu CD está em um drive com defeito?"
Cliente: "Só um que não."
Suporte: "O que?"
Cliente: "Não é uma unidade 4x? Então, eu já coloquei 3 CDs, mas ele não aceita o quarto e nem devolve meus outros 3."

Em 29/07/2004 12:29 - Comentários (0)

10 ferramentas que todo programador .NET deve ter

Na MSDN Magazine desse mês, saiu um artigo sobre as 10 ferramentas que todo programador .NET deve ter. É um artigo bem resumido, mas que é legal para quem precisa somente das referências.

Como um continuação desse artigo, eu recomendo o livro Coder To Developer. É um livro curto (+-350 páginas) que explica um pouco da metotodologia por trás das ferramentas e fala sobre como fazer um software do início ao fim, do projeto ao setup.

Em 29/07/2004 13:08 - Comentários (1)

Por que os administradores de rede não se limitam a administrar a rede?

Era uma vez uma empresa. Era uma vez uma reunião entre os gerentes da área de desenvolvimento e os administradores de rede. Tudo corria bem na reunião, até que um administrador de rede diz a seguinte pérola:

- Estamos pensando em bloquear o MSN Messenger, porque ele acaba com a produtividade das pessoas.

Depois disso, o gerente de desenvolvimento tenta explicar que o MSN Messenger agiliza o trabalho, que ele gerencia pessoas que estão em outras unidades da empresa e que essa é uma forma ágil de comunicação, etc...

Agora, a pergunta: O que um administrador de rede entende de produtividade? Ele é um administrador de rede, um psicólogo ou um profissional de RH? Provavelmente, esse administrador de rede só usa o MSN Messenger para bater papo com os amiguinhos mesmo...

Em 29/07/2004 17:24 - Comentários (52)

"Windows Internals" no forno!

Hoje recebi a newsletter do site Sysinternals, dizendo que o Mark Russinovich e o Dave Solomon estão quase terminando o "Windows Internals", que é a próxima edição melhorada do Inside Windows 2000. Essa nova versão cobrirá as modificações feitas no kernel do Windows XP e 2003, e trará mais capítulos sobre resoluções de problemas e uso de dumps.

Em 30/07/2004 12:59 - Comentários (0)

C++: O esquecido operador vírgula

Um operador que é pouco usado, mas bastante útil em C++, é o operador vírgula. Resumidamente, ele avalia todas as instruções separadas pela vírgula, mas retorna sempre a última. Olhe um exemplo:

int a, b, c; b = 2; c = 3; a = (b,c);

Nesse caso, o valor da variável a será 3.

Uma das particularidades do operador vírgula, é que ele tem a menor precedência entre todos os operadores. Vamos tirar os parênteses:

int a, b, c; b = 2; c = 3; a = b,c;

Agora o valor da variável a será 2, já que pela precedência, a = b é a primeira expressão, e c é a segunda expressão.


Usos realmente úteis

O uso mais comum para o operador vírgula é inicializar mais de uma variável em um loop, como no exemplo abaixo:

int b,c; for(int a = 10, b = 20, c = 30 ; a < 10 ; a += --b, c++) { }

Nesse caso, nós declaramos a variável a e atribuímos o valor 20 à variável b.

Outro ótimo uso do operador vírgula é para fazer validação de casts não seguros. Existem lugares onde, apesar do parâmetro ser do tipo void* ou void**, você sempre deve passar um tipo determinado. Um exemplo clássico, é a função CoCreateInstance, que tem a seguinte assinatura:

STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv );

O último parâmetro, apesar de ser LPVOID* (ou seja, void**), espera um tipo definido. Ele quer um ponteiro para um ponteiro que possa ser convertido para IUnknown*.

Olhe o código a seguir, que tem um dos erros mais comuns em programação COM:

HRESULT hr; IUnknown* pUnk; hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, (void**)pUnk);

Encontrou o erro? Na realidade, &pUnk deveria ser passado, e não somente pUnk como foi passado. Com isso você vai ganhar um belo GPF de presente. Como estamos usando cast C, o compilador não reclama, e nosso código é compilado sem problemas. E aqui não podemos usar static_cast, porque IUnknown** não pode ser convertido para void**.

Vamos agora a uma solução usando o operador vírgula e uma macro:

HRESULT hr; IUnknown* pUnk; // // IC = interface cast // #define IC(ppUnk) (static_cast(*ppUnk),(void**)ppUnk) hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, IC(&pUnk));

O que fizemos aqui foi o seguinte: a macro primeiro checa se o apontado do que foi passado pode ser convertido para IUnknown*, usando static_cast. Essa primeira expressão (static_cast(*ppUnk)) vai verificar a validade do ponteiro em tempo de compilação, mas não surtirá nenhum efeito em tempo de execução. A segunda expressão ((void**)ppUnk) fará então o cast que deve ser feito para a chamada da função. Vamos agora tentar compilar com o mesmo erro do primeiro exemplo:

HRESULT hr; IUnknown* pUnk; #define IC(ppUnk) (static_cast(*ppUnk),(void**)ppUnk) hr = CoCreateInstance(CLSID_BLA, NULL, CLSCTX_ALL, IID_IUNKNOWN, IC(pUnk)); ----------------------------------- error C2440: 'static_cast' : cannot convert from 'IUnknown' to 'IUnknown *' No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

E como todo ponteiro para interface COM pode ser convertido para IUnknown*, resolvemos nosso problema.

Se você quiser mais informações sobre verificações em tempo de compilação e códigos nesse estilo, procure sobre metaprogramming e estude muito, mas muito mesmo sobre templates.

Em 30/07/2004 13:57 - Comentários (0)

Iniciando o desenvolvimento básico de drivers

Pois é, o pessoal da Microsoft resolveu retirar o desenvolvimento de drivers do ostracismo. Republicaram o artigo "How to develop a Windows Driver". Um artigo bem curto, que fala o básico sobre desenvolvimento de drivers: arrumar uma cópia do DDK, usar o checked build, compilar usando o BUILD, e, é claro, certificar seu driver junto à Microsoft.

Em 31/07/2004 21:40 - Comentários (0)

Dicas para particionamento e organização do HD, parte 1

Eu tenho visto por aí muita gente com HDs gigantescos, todos com uma partição só. Apesar de isso não causar problemas no uso diário, um melhor particionamento pode ajudar bastante quem precisa de performance e organização. Por isso eu resolvi escrever alguma coisa sobre isso. Se ficar bom, talvez eu coloque mais água no feijão e transforme isso em um artigo. (Antes eu tenho que terminar o WinDbg parte 3).

Dica 1: nunca crie somente uma partição

A primeira dica é a mais simples. Qual o motivo? Com uma partição só, fica mais difícil organizar suas coisas e mais difícil de fazer backup e seu HD fica mais fragmentado.

Crie no mínimo duas partições, uma para o sistema operacional e para os programas, e outra para os seus dados. Instale o sistema operacional e os programas que você mais usa na primeira partição e desfragmente a partição. Guarde seus dados na outra partição.

Dica 2: Mude as pastas padrão para o "Meus Documentos", "Favoritos", etc

Por padrão, o Windows coloca a pasta "My Documents" em "c:\Documents And Settings\[username]\My Documents". Isso torna infernal o processo de backup, já que se você usar xcopy para fazer backup você terá que digitar esse caminho imenso ou colocá-lo no .BAT.

Resolva o problema: baixe o TweakUI no site da Microsoft, entre em My Computer >> Special Folders e saia mudando. Mude o "My Documents" para algo como "d:\rodrigo" e "My music" para algo como "d:\mp3". Mude também a localização do "Desktop" para dentro de "My Documents", como "d:\rodrigo\desktop". Menos uma pasta separada para você lembrar na hora do backup. E a pasta do desktop é a mais fácil de esquecer na hora do backup.

Use sempre nomes fáceis para as pastas, e as coloque na partição de dados. Assim, você pode programar o backup para copiar todas essas pastas, que são mais fáceis de lembrar. Um dia você vai decidir formatar o HD, e assim ficará mais fácil lembrar o que copiar, e você terá menos medo na hora de escolher a opção "Yes, format my entire HD now!".

Dica 3: Faça backup, faça backup, faça backup, faça backup, faça backup, faça backup, faça backup

Nunca é suficiente dizer. As pessoas geralmente só lembram do backup quando alguma desgraça acontece. O backup não precisa ser em uma fita DAT com um software de milhares de dólares. Um simples arquivo RAR ou ZIP com seus arquivos mais importantes enviado para um disco virtual já resolve o problema. Você pode fazer o backup dos seus documentos uma vez por semana, e das mp3 a cada mês. O importante é que você faça isso regularmente.

Dica 4: Teste o backup!

Já vi o caso de uma empresa que fazia o backup regularmente, mas nunca testou se o backup era restaurado corretamente. Adivinhe o que aconteceu na hora de restaurar o backup? Você pode esquecer de configurar a ferramenta de backup para incluir as subpastas. Erros simples como esse se tornarão catastróficos na hora que você precisar do backup.

Futuramente...

Diferenças entre NTFS e FAT32, qual tipo usar em que caso, usando 3 ou mais partições

Veja também
Dicas para particionamento e organização do HD, parte 2
Dicas para particionamento e organização do HD, parte 3
Verdades e mitos sobre NTFS e FAT

Em 03/08/2004 01:14 - Comentários (1)

C++: a linguagem mais poderosa para programação .NET

O artigo C++: The Most Powerful Language for .NET Framework Programming só ajuda a confirmar a minha opinião de que, com as novas managed extensions incluídas no Visual C++ 8.0 (Visual Studio 2005), eu nunca terei que usar outra linguagem para programar em .NET.

Continuarei com os templates, com o pré-processador, com o otimizador, com a possibilidade de usar API Win32 sem precisar usar DllImport (que é o Declare do VB6, nada mais, nada menos), possibilidade de alocar objetos na pilha, etc. Sem contar o gerenciamento de projeto, build steps, opções para configurar compilador e linker, o respeito que a Microsoft tem com programadores Visual C++, etc.

Ah, e é claro, o fato de que a integração de controle de versão (Source Safe, Clear Case) funciona com o Visual C++ e NÃO FUNCIONA com VB.NET.

Em 04/08/2004 12:27 - Comentários (0)

Porque programar drivers (kernel mode) é mais legal do que programar aplicativos (user mode)
  • Você está dentro do kernel do sistema.
  • Você precisa de um bom conhecimento da arquitetura do kernel do Windows. Isso torna você um melhor programador user mode.
  • Programação em kernel mode é algo crítico. O BUILD configura o compilador C para tratar warnings como errors.
  • Você não pode errar. Um simples GPF ou divisão por zero é tela azul na hora!
  • Em kernel mode você é obrigado a usar seus conhecimentos de ciências da computação: arquitetura de computadores, listas ligadas, etc.
  • Os samples do DDK (Driver Development Kit) são mais bem organizados e mais bem comentados do que os samples de user mode. O código também é muito mais bonito. :-)
  • A API do kernel do Windows (Native API) é muita mais concisa e bem organizada do que a Win32 e algumas partes do .NET Framework.
  • Não existe programação para banco de dados em kernel mode. Nada de VB, nada de ADO.NET, nada de DataAdapter, nada de SQL, nada de fazer procedures. Praticamente o paraíso... :-)
  • Em kernel mode, você só pode usar Assembly, C ou C++. E muitos programadores pregam que você use somente C.
  • Temos ferramentas como o Driver Verifier e o Windows Checked Build para verificar se estamos fazendo tudo OK. Hoje temos o Application Verifier para user mode, mas isso é novo e quase ninguém conhece. O pessoal que programa em kernel mode (pelo menos os que sabem o que estão fazendo) usa o Driver Verifier sempre.
  • Especialização. Poucas pessoas programam em kernel mode.

Em 04/08/2004 23:46 - Comentários (0)

Depois dizem que computação é uma ciência exata

Eu ainda vivo na idade da pedra, e como tal, minha câmera fotográfica não é digital. Tirei umas fotos na semana passada e decidi tirar meu scanner da caixa para enviar algumas fotos para os amigos.

Meu scanner (que eu saiba) não tem driver para Windows XP, somente para Windows 2000. Fácil: pego a VMWare com Windows 2000 que eu uso pra fazer debug dos drivers que eu faço, instalo o scanner, software do scanner e pronto. Isso é o que eu pensava em minha vã inocência.

Subi a VMware e espetei o scanner na USB. O Windows XP reconheceu o "VMWare USB Device" e cinco segundos depois meu micro reinicia. Antes de xingar o scanner ou qualquer outra coisa, eu pensei como eu sou estúpido por não ter configurado o Windows para não reiniciar automaticamente depois de um BugCheck (tela azul) e para fazer o dump completo.

Com o micro reiniciado, configurei tudo, subi a VMware e espetei o scanner. Como esperado, tela azul de novo, e 5 minutos depois eu tinha o dump. Reiniciei o micro novamente e abri o dump no WinDbg para descobrir quem eu deveria xingar: a VMware, a Microsoft ou a Genius, fabricante do scanner.

Esperei uns 20 minutos até o WinDbg baixar os symbols para os drivers da minha máquina. Uma longa espera para alguém que só que encontrar um culpado. Consegui ver que algum driver tentou liberar memória duas vezes e disparou um BAD_POOL_CALLER. Quando vou olhar qual foi o driver que fez a besteira, tive uma infeliz surpresa: foi o firewall (Tiny Personal Firewall)...

Isso siginifica que o driver está fazendo hook do que não deve, até porque o TCPIP.SYS nem estava na pilha. Ou eu arrumo um outro firewall ou termino logo o meu...

Depois que você descobre como são feitas as salsichas, o molho de tomate e os drivers de antivírus com "real time scan", seus hábitos mudam bastante... :-)

Em 05/08/2004 14:06 - Comentários (1)

Visual C++ 2005 Managed Extensions: o início

Hoje eu resolvi fazer minhas primeiras experiências com Managed C++ 2005, para ver se é realmente simples como eu acredito que seja. Minha primeira experiência ridícula, serve para ilustrar como a compilação é quase tão simples como C# ou VB.NET.

Código fonte
int Main() { System::String^ str; str = L"Eu queria estar na praia"; System::Console::WriteLine(str); return 0; }
A compilação
cl mcpp1.cpp /clr:pure /link /entry:"Main" /subsystem:console Microsoft (R) C/C++ Optimizing Compiler Version 14.00.40607.16 for Microsoft (R) .NET Framework version 2.00.40607.16 Copyright (C) Microsoft Corporation. All rights reserved. mcpp1.cpp Microsoft (R) Incremental Linker Version 8.00.40607.16 Copyright (C) Microsoft Corporation. All rights reserved. /out:mcpp1.exe /clrimagetype:pure /entry:Main /subsystem:console mcpp1.obj
O resultado
Eu queria estar na praia

Simples, rápido e fácil. Agora eu tenho um autêntico e legítimo executável 100% managed para ser consumido pelo .NET Framework 2.0 beta 1. Agora é só esperar terminar o download do SDK do framework 2.0, e usar o ildasm para ver se está realmente tudo OK.

Mas, na verdade, eu gostaria de estar na praia.

Em 06/08/2004 17:42 - Comentários (0)

Como usar o Visual C++ 2005 Express para fazer programas SUBSYSTEM:Windows

No artigo sobre WinDbg, eu disse que caso o leitor não tivesse o Visual C++, ele poderia baixar o Visual C++ 2005 Express e o Platform SDK para fazer programas Windows. Agora eu vou explicar os passos necessários para isso.

1. Baixe o Visual C++ Express 2005.

2. Instale o Microsoft Platform SDK via web. Instale somente o [Core SDK >> Build Environment] e o [Internet Development SDK >> Build Environment]. Infelizmente, mas infelizmente mesmo, não é possível usar o Firefox para fazer a instalação, só Internet Explorer mesmo. (igual ao novo site da MSDN brasileira... shame...).

3. Agora vamos configurar o Visual C++. Menu "Tools" >> "Options". No TreeView, "Projects And Solutions" >> "VC++ Directories". Depois, no ComboBox "Show Directories for:" insira Program Files\Microsoft SDK\include em "Include Files" e Program Files\Microsoft SDK\lib em "Library Files".

4. Reinicie o Visual C++ 2005 Express

Para testar, abra o Visual C++ e crie um novo projeto "Win32" >> "Console Application" (é isso mesmo, console application). Quando abrir a tela com as configurações do novo projeto, escolha "Windows Application". Caso você tente compilar o projeto, verá vários erros de linker, já que o projeto não está configurado para fazer link com as libs das dlls Win32.

Agora vamos arrumar o problema do linker: entre nas propriedades do projeto, "Configuration Properties" >> "Linker" >> "Input". Em "Additional Dependencies", coloque "kernel32.lib user32.lib gdi32.lib" (assim mesmo, separadas por espaços). Isso deve ser feito em todos os projetos Win32.

Pronto. Agora divirta-se!

Em 09/08/2004 11:53 - Comentários (0)

Depois da praia, vamos nos purificar

No meu post de sexta-feita, eu usei o parâmetro de linha de comando "/clr:pure" para compilar o meu "Eu gostaria de estar na praia" (convenhamos que é melhor do que Hello World...). Agora que eu terminei de baixar o .NET Framwork SDK 2.0, usei o ildasm para verificar o código gerado. Olha o que temos:

Reconhecemos as seções do executável e várias funções da runtime do C/C++. Isso pode deixar um programador purista triste, já que carregamos algumas coisas que são do velho Visual C++.

Agora vamos mudar o parâmetro para "/clr:safe". Olhe o que temos agora:

Agora sim, um executável .NET puro, suficiente para agradar os puristas. Lembre-se que usando "/clr:safe", não podemos usar nada que não seja seguro e verificável pela runtime. Um simples "#include " irá gerar milhares de erros, já que as funções e estruturas da Win32 API não são verificáveis.

Em 09/08/2004 19:41 - Comentários (0)

PREFAST rocks!

Para quem não sabe, o Prefast é um analisador estático de código fonte, que vem junto com o DDK do Windows Server 2003. Ele foi feito para pegar erros de lógica, que o compilador não pega. Olhe esse código:

NTSTATUS s; IO_STACK_LOCATION* pIrpSp; pIrpSp = IoGetCurrentIrpStackLocation(Irp); ... switch(pIrpSp->MinorFunction) { case FUNC1: s = OnConnect(DeviceObject,Irp,NextDevice); break; case FUNC2: OnDisconnect(DeviceObject,Irp,NextDevice); break; default: IoSkipCurrentIrpStackLocation(Irp); s = IoCallDriver(NextDevice,Irp); } return s;

Achou o erro? O PREFAST achou. Olhe o log dele:

dispatch.cpp(197) : warning 1: Using uninitialized memory 's'. problem occurs in function 'OnDeviceInternalIoControl' Path includes 6 statements on the following lines: 177 178 180 182 189 197

A linha 197 é a que chama "OnDisconnect". Repare bem que o retorno dessa função não é atribuído à variável s. Caso o valor de "pIrpSp->MinorFunction" seja "FUNC2", a variável s nunca seria inicializada, e minha função retornaria o lixo que estivesse na pilha.

Em 12/08/2004 23:22 - Comentários (0)

Computação não é uma ciência exata, parte 2

Passei quinta e sexta da semana passada fazendo um dos serviços mais enfadonhos que um programador pode fazer: documentar um sistema que nem fui eu que criei. É quase uma tortura passar o dia inteiro com o Word aberto e com o Visual Studio fechado...

Estava eu terminando a documentação, quando enrosquei o pé no cabo de força do estabilizador e tudo apagou. É claro que eu nem tinha salvo a documentação, já que eu deixo meu micro em "hibernation" todo dia e o Word 2003 tem os recursos de recuperação de documentos e salvamento automático. Despreocupado, liguei o micro novamente e abri o Word na esperança de ver meu documento lá, do jeito que estava antes da infeliz fatalidade.

Pois é, ele não estava lá. Procurei os ~*.doc nos temporários do Windows e nada. Acredito que eu nem preciso descrever meu desespero, já que fazer documentação uma vez é enfadonho, e fazer duas vezes é torturante.

Sem mais opções, recomecei a escrever a documentação, e lembrei que o Word 2003 é o primeira versão que eu encontrei bugs, principalmente de redesenho de tela. Eu uso o Word desde a versão 6.0 para Windows 3.11, e nunca encontrei nenhum problema. Até aparecer o Word 2003. O pior não é o bug do redesenho, e sim o que me fez perder o documento, mesmo com a opção de salvamento automático ativada.

Passou o fim de semana, terminei minha segunda versão da documentação, e resolvi instalar o Service Pack 1 do Office 2003. Perguntei a um amigo se esse Service Pack solicitava o CD durante a instalação, porque todos os updates do Office que eu vi até hoje pediam. Ele me disse que no micro dele e de um outro amigo nosso, o Windows Installer não pediu o CD. Então, eu resolvi arriscar.

É claro que ele pediu o CD, e é claro que o CD do Office estava na sede da empresa, e não onde estou prestando consultoria. Afinal, é para isso que serve a Lei de Murphy.

Cancelei a instalação, fiquei assistindo a barra de progresso do Windows Installer retroceder enquanto eu torcia para que meu Office continuasse funcionando, para que eu não precisasse esperar que alguma boa alma me trouxesse o CD do Office ou que eu tivesse que fazer o download da MSDN.

Depois de cancelar o update, abri o Word para testar se tudo estava funcionando. Apareceu a tela do Windows Installer esperada, algumas dezenas de segundos e pronto, lá estava o Word aberto, COM O MEU DOCUMENTO PERDIDO SEMANA PASSADA RECUPERADO

Em 19/08/2004 02:01 - Comentários (0)

Por que você programa?

Tentando encontrar uma forma de explicar para um entrevistador como eu sou fascinado por programação sem parecer mentiroso ou artificial, eu me lembrei de uma coisa que li a muito tempo atrás: "Why Do You Code?".

Precisa existir algum motivo lógico? No meu caso é simplesmente inexplicável, quase uma doença. Caso alguém queira tentar fazer um diagnóstico, vou enumerar alguns sintomas:

  • Meu micro tem 512MB de RAM e uma Geforce FX 5200 128MB. Eu até fico feliz que minha máquina rode Doom3 numa velocidade razoável, mas esse não é motivo da minha felicidade. Eu fico feliz porque posso abrir duas máquinas virtuais, o Visual Studio.NET, a MSDN, o Firefox com milhares de tabs e o WinDbg ao mesmo tempo.
  • Eu trabalho com programação o dia inteiro. Sabe o que eu faço pra me divertir quando chego em casa? Abro meu Visual C++ e trabalho em um software que eu estou fazendo a 10 meses, envolvendo computação distribuída e device drivers. Ou seja, continuo programando e estudando.
  • Fico feliz pelo fato da minha HD ter 120GB. Assim eu posso instalar sem preocupação a MSDN, o DDK, o Platform SDK, o Visual Studio 6, o Visual Studio.NET, posso baixar milhares de fontes do Source Forge, criar várias máquinas virtuais para testar softwares como o Visual Studio 2005, Yukon e sistemas operacionais, posso instalar Linux ou até mesmo o Longhorn em outra partição, etc, etc.
  • Fico feliz por ganhar um salário bom. Assim eu posso gastar dinheiro comprando mais livros sobre programação todo mês na Amazon.
  • Se hoje eu tenho um bom conhecimento de inglês, é de tanto ler livros sobre programação e aperfeiçoar o inglês para estudar mais sobre programação e para trabalhar em projetos envolvendo programação no exterior. Um dia um amigo me disse que queria virar programador e perguntou por onde começar. Eu disse para começar estudando inglês.

Essa matéria que eu citei conta a história de um sujeito que queria aprender a programar mas não tinha micro, só uma WebTV (um finado aparelho no qual você podia navegar pela web com sérias limitações e enviar/receber e-mails ligando o aparelho à linha telefonica e à TV, quase um TK90X). Então ele escrevia os fontes C em um e-mail, e enviava para um amigo que tinha computador. Esse amigo compilava e mandava os erros de volta. Assim ele começou a programar.

Esse sujeito parece maluco? Pois é, mas eu comecei mais ou menos assim. Eu aprendi a programar com 12 anos, quando minha mâe trabalhava na UNESP de Botucatu e eu resolvi fuçar em computadores no polo computacional de lá. Depois eu resolvi pegar uns livros para ler. O que você faria se visse um garoto de 12 anos de idade na biblioteca da sua faculdade procurando livros de programação? Hoje eu fico imaginando como devia ser estranho...

Então eu peguei um livro de BASIC e comecei a programar em GW-BASIC usando os micro de lá. Mas eu não tinha micro em casa, não podia programar sempre. Não demorou muito tempo para que eu começasse a escrever programas em um caderno, na minha casa. Eu tinha 12 anos de idade...

Tem alguma explicação para isso? Existem pessoas que programam para viver, e pessoas que vivem para programar. Eu me encaixo na segunda categoria, e conheço algumas pessoas assim.

Por que eu programo? Sinceramente, eu não sei explicar. Só espero que o entrevistador entenda...

Em 19/08/2004 03:18 - Comentários (27)

Planeta C++

O criador do C++ (Bjarne Stroustrup) mantém uma pequena lista dos softwares que foram feitos usando C++. Acredito que mais de 95% dos softwares que rodam em plataforma Windows são feitos em C e C++. Não sei se existe uma estatística oficial sobre isso, mas a esmagadora maioria dos software conhecidos são feitos em C ou C++.

Para as pessoas que trabalham diretamente com C ou C++ isso parece óbvio, mas como no Brasil pouca gente trabalha com isso, esse post deve trazer a resposta para a pergunta que os programadores VB e Java nunca pararam para fazer.

Não se esqueçam de olhar o item "Microsoft".

Em 19/08/2004 15:34 - Comentários (4)

Diversão para programadores de drivers

Muito melhor do que jogar paciência, é fazer as palavras cruzadas da OSR e testar seu conhecimentos sobre desenvolvimento de drivers para Windows. Alguém poderia providenciar mais alguns desses sobre Win32 e C++. E talvez até .NET... :-)

Além disso, eles publicaram um artigo interessante sobre como trocar os arquivos do Windows (sem que o Windows File Protection coloque os originais de volta) usando os recursos nativos do kernel debugger. Por exemplo, o IFS Kit (usado para fazer drivers de file system) vem com os fontes do fastfat.sys, que é o driver do sistema de arquivos FAT (sim, o mesmo do Windows, feito pelo Gary Kimura). É interessante compilá-lo em modo checked e substituir o do Windows para ver como funciona, mas o WFP não deixa que você troque o arquivo. Existem algumas gambiarras possíveis, como editar uma DLL do Windows usando um editor hexadecimal, mas até eu que estou acostumado a fazer uns patches no kernel do Windows usando o WinDbg acho isso um pouco de exagero...

Em 25/08/2004 12:43 - Comentários (3)

Dicas para particionamento e organização do HD, parte 2

Nessa segunda parte, darei uma pequena explicação do sistema de arquivos FAT. Depois faremos uma comparação entre FAT e NTFS para podermos decidir qual tipo é melhor para qual finalidade.

FAT é um sistema de arquivos que foi criado na década de 80, originalmente para ser usado em disquetes. Foi o sistema de arquivos padrão da Microsoft desde as primeiras versões do MS-DOS, e continua sendo o mais utilizado até hoje, sendo suportado pelas últimas versões do Windows (XP, Server 2003 e Longhorn) e por diversos outros sistemas operacionas, como Linux e OS/2.

Primeiro vamos resumir a história do FAT:
  • FAT12: Primeira versão, foi projetada para disquetes. Nomes de arquivo no formato 8.3. O "12" vem do número de bits usados para numerar os clusters;
  • FAT16: Com o aparecimento dos primeiros HDs (naquele tempo alguns chamavam de "Winchester", lembram?), o FAT12 não era mais suficiente. Aumentaram então o número de clusters para 16 bits;
  • VFAT: Com o surgimento do Windows 95, a Microsoft queria suportar nomes de arquivos longos, como no OS/2 (que usava HPFS). Então foi feita uma adaptação no FAT para ele suportasse nomes longos e ainda ficasse compatível com os aplicativos antigos;
  • FAT32: Com o surgimento dos HDs maiores, o tamanho máximo de 2GB para um partição FAT16 tornou-se uma limitação crítica. Então o número de clusters foi aumentado para 32 bits. Apesar de resolver o problema, o novo sistema de arquivos não era compatível com os sistemas operacionais mais antigos. O Windows NT4, por exemplo, não suporta FAT32 nativamente. O FAT32 também suporta nomes longos, assim como o VFAT (não sei se podemos chamar de VFAT32...).

Por ser um sistema de arquivos antigo, muitas das funcionalidades presentes nos sistemas de arquivos mais modernos não estão disponíveis. Para facilitar, vou enumerar as vantagens e desvantagens do FAT32:

Vantagens:

  • Como a estrutura é simples, o acesso aos arquivos é rápido;
  • É compatível com praticamente todos os sistemas operacionais, como Windows 98, Windows NT 4, Linux, OS/2, etc.

Desvantagens:

  • Não tem recursos de segurança. Não permite que o usuário ou administrador limite o acesso de determinados arquivos a determinados usuários;
  • O controle anti falhas é muito simples e pouco eficiente. O FAT usa como sistema de segurança para corrompimento dos metadados uma cópia backup da tabela de alocação. Isso já se mostrou ineficiente. Uma simples queda de energia durante uma operação que modifique os metadados pode tornar a partição inacessível.

Em todas as comparações que eu fizer daqui para frente, somente o FAT32 será considerado. O FAT16 tem a limitação de 2GB no tamanho da partição, motivo suficiente para ser desconsiderado.

Na terceira parte eu descreverei o NTFS.

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 3
Verdades e mitos sobre NTFS e FAT

Em 26/08/2004 17:47 - Comentários (0)

Notícias do Windows Longhorn

Microsoft decide que o WinFS não fará parte do Longhorn e que o WinFX, Avalon e Indigo serão disponibilizados para Windows XP e 2003. Acho que isso vai dar muito o que falar. Fico feliz que o "Raymond Chen camp" venceu mais uma, mas acho que a situação vai ficar bem complicada. .NET Framework 2.0 + WinFX + Avalon + Indigo, imaginem só o tamanho do download de tudo isso. Mas isso me faz pensar em algumas coisas:

  • Já era sabido que o Indigo seria portado, isso só foi colocado no mesmo pacote de divulgação;
  • Se o Avalon vai ser portável para Windows XP, isso quer dizer que não será possível muita dependência do Avalon em relação ao kernel e ao DirectX, a não ser que ele faça parte de um Service Pack (o que eu duvido que aconteça). Eu acho o managed code lento pra manipulação de gráficos. Mas isso poderia ser resolvido no Longhorn, atrelando o Avalon diretamente com o DirectX. Assim a maioria do trabalho seria feito em kernel mode, o que diminuiria a quantidade de trabalho feito em managed code;
  • Acho que isso tende a deixar as APIs managed bastante atreladas ao Win32, a não ser que a Microsoft faça do WinFX um subsystem separado. Acredito que um subsystem managed para user mode, contando com boa parte da implementação em kernel mode apoiado em DirectX iria ficar muito interessante e com um desempenho muito bom. Mas não acho que a Microsoft vá por esse lado (não testei o Longhorn ainda), porque isso tornaria difícil manter a compatibilidade e fazer a integração com o Win32, já que o WinFX estaria fora do win32k.sys;
  • Mais uma vez o Bill Gates terá seu tão sonhado sistema de arquivos adiado... Mais uma vez...
  • Não teremos nenhuma revolução como a Microsoft diz, mas sim uma evolução. Avalon será a [GDI+++ + DirectX], Indigo o Remoting/SOAP vitaminado, e o WinFX será um WinForms 3.0. Ainda muito bom, mas sem o exagero característico do pessoal de marketing da MS.

[editado em 10/09/2003. "WinFX será um WinForms 2.0" >> "WinFX será um WinForms 3.0"]

Em 30/08/2004 02:43 - Comentários (0)

Como sempre, Carmack nos surpreende

Existe uma palavra que define Doom3: medo. Além dos gráficos muito bons, que todo mundo fala, o enredo e ambiente do jogo também são ótimos. O jogo realmente dá medo, e eu não consigo jogar mais de uma hora por causa da tensão. John Carmack mais uma vez prova que continua sendo um gênio.

Agora vou ler um livro para me acalmar depois de uma sessão de Doom3...

Em 11/09/2004 01:31 - Comentários (0)

Dicas para particionamento e organização do HD, parte 3

Como prometido, agora na terceira parte vou explicar um pouco sobre NTFS. Acho desnecessário dizer, mas isso é só o básico sobre os sistemas de arquivos. Caso você precise de maiores detalhes, procure a especificação ou maiores informações na web.

O NTFS (NT File System) foi criado especialmente para o Windows NT, logo na sua primeira versão. Houve uma grande discussão durante o projeto do Windows NT sobre a viabilidade de criar-se um novo sistema de arquivos ou usar/adaptar um existente na época, como o FAT ou o HPFS. Sabemos hoje a decisão que foi tomada.

Os únicos sistemas operacionais realmente compatíveis com NTFS são os da linha Windows NT: Windows NT 3.x, Windows NT 4.0, Windows 2000 (5.0), Windows XP (5.1), Windows Server 2003 (5.2) e a próxima versão, ainda em alfa, Windows Longhorn (6.0). Existem drivers para Linux, mas somente readonly. Para Windows 9x existe o NTFS for Windows 98 dos nossos geniais amigos da SysInternals, que usa os drivers do Windows NT para acessar as partições NTFS.

Vamos às vantagens e desvantagens do NTFS.

Vantagens:

  • NTFS possui journaling, que controla todas as modificações nos metadados da partição. Os metadados são as informações de como e onde estão os dados e os arquivos na partição. Todas as modificações são gravadas em um log ANTES de serem efetuadas. Assim, caso um problema (como queda de energia) aconteça durante a gravação, é possível terminar ou reverter a operação na próxima vez que o computador for ligado. Note que isso garante a integridade da partição, mas não a integridade dos dados. O controle de dados é deixado por conta da aplicação. Os bancos de dados (como SQL Server ou Oracle) fazem controle de integridade de dados;
  • Possui suporte a metadados para os arquivos e pastas, o que permite, entre outras coisas, que as informações de permissão do arquivo sejam gravadas diretamente na partição. Por isso que você consegue determinar permissões de acesso em uma partição NTFS, e não em uma partição FAT (usando Windows NT/2000/XP, é claro);
  • Possui suporte a streams alternativas. Além de gravar no arquivo, você pode criar um novo fluxo de dados para o arquivo e gravar informações separadamente. Isso permite, por exemplo, que você mantenha diferentes versões dos dados do arquivo em streams diferentes;
  • Tem menos problemas com fragmentação do que o FAT;
  • Possui suporte nativo a criptografia e compactação (a partir do Windows 2000).

Desvantagens:

  • Só é 100% compatível com o Windows NT/2000/XP.

(tentei encontrar mais desvantagens, mas não achei. Existem boatos de que FAT é mais rápido do que NTFS, mas não achei nenhum estudo realmente conclusivo sobre isso)

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 2
Verdades e mitos sobre NTFS e FAT

Em 13/09/2004 00:43 - Comentários (2)

Pode acontecer algum dia, em algum computador

Mensagem de erro mostrada por um programa de instalação quando executado no Windows Me :

Se ao invés de linguas nórdicas, o Raymond Chen estivesse estudando português, e ele por acaso lesse meu weblog, ele ficaria muito bravo... :-)

Em 13/09/2004 00:51 - Comentários (0)

Parece brincadeira, mas a Microsoft fez

Parece brincadeira, mas um dia depois de escrever o post tirando sarro do Windows Me ("Pode acontecer algum dia, em algum computador"), com uma mensagem que pouco provavelmente apareceria em um software comercial, encontrei essa mensagem no Microsoft Word 2003:

Essa mensagem estaria correta se eu não tivesse um browser instalado. Mas o detalhe é que o link que eu cliquei ("More information about this error message online") foi aberto no Firefox, que é meu browser padrão...

Além disso, o Office 2003 só roda em Windows 2000 ou superior, o que descarta a possibilidade de não haver um browser instalado.

Em 14/09/2004 03:27 - Comentários (0)

Mais uma mudança de paradigma

Quando eu comecei a acessar a Internet, em 1997, uma das primeiras coisas que você aprendia era que você deveria ser sempre anônimo. Além dos nicks para os chats, a grande maioria das pessoas tinham um e-mail com nick. Eu lembro de um colunista de uma conceituada revista de informática dizer que criar um e-mail com o próprio nome ou sobrenome era uma falta de criatividade. Hoje sabemos que isso não passa de estupidez.

Um fenômeno que eu estou percebendo de um tempo para cá é que as pessoas estão perdendo o medo da Internet, e ela está se tornando menos anônima.

Primeiros foram as páginas pessoais (faz tempo...). Todo mundo queria ter uma página pessoal, mesmo as pessoas que não tinham muito a dizer. Nem que fosse para colocar quais eram seus passatempos prediletos e onde você mora. Mas as pessoas eram receosas em colocar o nome completo ou mesmo uma foto, afinal, alguém poderia usar essas informações para fins ilegais. Lá as pessoas diziam o primeiro nome e do que gostavam.

Depois, bem depois, vieram os blogs. Além das pessoas colocarem suas preferências na rede, agora as pessoas colocam seu diário e sua opinião pessoal também. A nova mania são os fotoblogs, onde as pessoas colocam fotos em todas as situações possíveis e imagináveis. Agora as pessoas publicam sua rotina, com nome, sobrenome, e muitas fotos.

E agora, o Orkut. Lá você coloca nome, sobrenome, fotos e ainda diz quem é ou não é seu amigo. As pessoas participam de fóruns e discutem diversos assuntos (de sexo a bons bares para tomar chope alemão) com o nome estampado e um link para um profile quase sempre bem detalhado. Agora além de tudo isso, você diz quem são seus amigos, discute suas preferências e gostos abertamente. Coisa que muitas pessoas não fazem nem com os amigos no mundo real. Deixamos de ser anônimos, mas ainda usamos o fato de estar por trás de um computador para dizer coisas que não diríamos em outra situação.

E ultimamente, tenho visto que as pessoas entram em fórums de discussão usando o próprio nome e colocando um foto verdadeira como avatar. No MSN isso também tem sido mais constante.

Eu fico me perguntando o que virá depois...

Em 15/09/2004 01:46 - Comentários (0)

Somos todos estranhos

Lendo o blog do Raymond Chen, como faço todos os dias, encontrei um post muito interessante, comparando a arquitetura x86 com as demais arquiteturas disponíveis no mercado. Nesse post ele fala sobre as características da arquitetura x86, e que muitas delas não são encontradas em outras arquiteturas (como PPC, MIPS, Alpha e ia64). Como estamos acostumados com a arquitetura do x86, parece que essas arquiteturas são estranhas e exóticas. Mas não são.

Olhando a lista resumida do Raymond, vemos que a arquitetura x86 é limitada, por manter compatibilidade desde o 8086 (PC XT). E vemos também que a nova arquitetura da Intel, a ia64, agora compartilha diversas características com as outras arquiteturas. Inclusive algo que em termos de performance chega a ser óbvio, que é colocar os parâmetros em registradores, e não na pilha. Existe, dois artigos interessante sobre arquitetura ia64 na MSDN Magazine (aqui e aqui)

Mais uma prova de que nem sempre o melhor produto é o que ganha o mercado. O padrão Beta sempre foi melhor do que o VHS, mas ninguém usa. Essas outras arquiteturas são bem melhores que o x86, mas ninguém usa (em desktops).

Em 16/09/2004 14:17 - Comentários (0)

A um ano atrás eu teria orgulho disso, mas hoje...

São Paulo será a 2ª maior cidade do mundo em 2005

Em 16/09/2004 17:52 - Comentários (0)

Minha opinião sobre open source e o governo

Eu sou um programador que trabalha com tecnologia Microsoft. Uso Visual C++, C#. VB, SQL Server, etc. Gosto bastante dos softwares da Microsoft e o método que eles usam para desenvolvê-los. Mas não sou um defensor ferrenho da Microsoft. Existem alguns produtos deles que são ruins, eles tomam decisões erradas e têm algumas posições políticas pouco recomendáveis.

Como todo programador C/C++, sou bastante interessado pelo Linux. Programo em PHP e estou aprendendo Qt/GTK e Mono. Acho muito interessante que ele seja desenvolvido por programadores voluntários, que seja livre e com o código aberto. Mas eu não me importo muito com a ideologia do Linux. Eu tento julgá-lo pela qualidade, e não pelo puritanismo de usar Linux só pelo fato de não ser proprietário ou coisas assim. As empresas gastam dinheiro para desenvolver produtos e vendê-los. E eles querem garantir o direito deles a essa invenção/criação/descoberta usando patentes e coisas assim. Isso é absolutamente normal. (O sistema de patentes norte-americano é ruim e anormal, mas não vou falar disso agora). O software pode ser livre, mas a grande maioria dos outros bens de consumo (considerando que software é um bem) não são.

Eu sou contra a preferência do governo por software livre. E sou absolutamente contra a existência de qualquer lei nesse sentido, porque isso acaba com a concorrência. O critério para escolha de software pelo poder público deve ser o mesmo critério que ele usa para qualquer coisa que compra: preço e qualidade, custo e benefício. Ele não deve se prender a uma determinada "origem", seja ela o pessoal do open source, a Microsoft, a IBM ou qualquer outra empresa.

Além disso, com essa lei, o governo somente deixará de comprar serviços de algumas empresas para comprar de outras. Por acaso a IBM ou RedHat são mais boazinhas do que a Microsoft só porque promovem o Linux? O custo da licença de um software é só uma fração do seu custo ao longo do tempo. Além disso temos o custo de suporte e atualização. Isso quer dizer que, ao invés do governo ficar amarrado com a Microsoft, ficará amarrado com a IBM, RedHat, Conectiva ou qualquer empresa que trabalhe com Linux. É correto privilegiar essas empresas e seus parceiros? Uma empresa que dá suporte (help desk) Linux não necessariamente contribui com a comunidade open source. Por que uma empresa dessas deve ter preferência em detrimento de uma empresa que dê suporte Windows?

Mas sim, hoje existe privilégio por parte da Microsoft. As pessoas estão tão acostumadas a usar softwares Microsoft que muitas vezes não param para estudar se existe um alternativa que seja viável. Isso sem contar o orçamento de marketing da Microsoft. Se deve existir uma lei sobre escolha de software, ela deve exigir que o governo faça um estudo de um numero mínimo de alternativas de software para aquele problema específico. Então o governo vai comprar sistemas operacionais? Ok, então que eles façam um estudo da viabilidade de usar Linux ou Windows, estudar o custo-benefício da solução. O Linux é mais barato, mas tem problemas de compatibilidade com hardware e software. O Windows tem compatibilidade 100%, mas tem um alto custo com licenças e te deixa preso com somente um fabricante.

Resumindo, o critério deve ser qualidade e custo-benefício. Querem que o Linux seja usado por todos? Façam com que ele tenha qualidade. Do resto o mercado se encarrega.

Em 20/09/2004 20:02 - Comentários (8)

Tudo que eu já sabia

Para empresas, faltam profissionais qualificados na área de tecnologia

Em 04/10/2004 19:24 - Comentários (0)

Verdades e mitos sobre NTFS e FAT
Verdade: NTFS é mais seguro (quanto a perda de arquivos)

O NTFS conta com um sistema de journaling, que grava um log de tudo que está sendo modificado na estrutura da partição NTFS. Caso acabe a luz ou ocorra algum desastre similar, o NTFS termina (ou desfaz) as últimas modificações nos metadados da partição, deixando sua partição acessível. Quando acaba a luz e você usa FAT, só resta rezar enquanto religa máquina.

Mito: É mais fácil recuperar dados em uma partição FAT

Esse mito vem do fato que os disquetes de boot DOS e Windows 9x não acessam partições NTFS. Mas as pessoas geralmente esquecem que os disquetes de boot já não são essenciais. Todos os CDs do Windows 2000/XP podem ser usados para dar boot na máquina e acessar os arquivos da partição usando a opção de "Recovery".

Para esses casos específicos também existem as distribuições Linux que consegue fazer boot com interface gráfica, conexão com Internet e suporte a Pen Drive USB, tudo isso rodando diretamente do CD. Assim você pode copiar seus arquivos tranquilamente. Algumas permitem que você copie o Linux inteiro na RAM, retire o CD de boot e use o gravador de CD normalmente. A grande maioria das distribuições Linux hoje em dia vêm com o driver de NTFS (somente leitura). (A grande excessão é o Fedora Core, mas eles devem ter algum motivo político/ideológico para isso...)

Mito: FAT32 é mais rápido do que NTFS

Eu também tinha essa impressão. O fato do driver de FAT do Windows chamar fastfat.sys também ajuda. Mas eu nunca encontrei nenhum estudo conclusivo que dissesse isso. O que eu mais encontrei na Internet foram informações de que o NTFS é mais rápido do que o FAT em partições grandes. E como hoje em dia o menor HD que você consegue comprar é de 80 GB...

Mito: Partição NTFS não fica fragmentada

Realmente, a partição NTFS tem bem menos problema com fragmentação. Mas não é verdade que NTFS não fragmenta. Tanto não é verdade que existe um desfragmentador no Windows.

Veja também
Dicas para particionamento e organização do HD, parte 1
Dicas para particionamento e organização do HD, parte 2
Dicas para particionamento e organização do HD, parte 3

Em 05/10/2004 12:17 - Comentários (1)

MPROB (mais um post referenciando outro blog)

O pessoal da Microsoft às vezes exagera no uso de siglas:

Congrats to the XMLP WG for getting the MTOM and XOP specs to CR

Alguém pode me explicar o que o Don Box quis dizer com isso?? :-)

Em 06/10/2004 18:59 - Comentários (0)

Mais uma prova que computação não é uma ciência exata

A Microsoft, a um tempo atrás, implementou a política "Zero Defects" para desenvolvimento de software. Essa política determinava que, enquanto todos os bugs de um determinado software não estivessem corrigidos, nenhuma nova funcionalidade seria adicionada. Mas os softwares da Microsoft (assim como 100% dos softwares existentes) ainda tem bugs. O que acontece então? Leiam isso:

Larry's rules of software engineering, Part 3: "Zero Defects" doesn't result in zero defects

Em 08/10/2004 12:41 - Comentários (0)

Mais ferramentas

Achei sites muito bons com ferramentas:

John Lyon-Smith: Libs C++/Win32, libs .NET. Além disso, tem o "C# Script Executor 2.6.8", que permite usar C# como ferramenta de script.

SmidgeonSoft: Ferramentas a la Sysinternals. Debuggers, Disassemblers, e visualizadores de objetos do NT (threads, arquivos, etc). Merece uma visita.

Em 08/10/2004 13:17 - Comentários (0)

Conhecimento é poder

Eu já escrevi sobre como é extremamente necessário que você tenha informações para tomar decisões. Tenho pensado bastante em open source, e resolvi me informar melhor de uma vez por todas. Eu sou um usuário Linux razoável, tenho um SUSE instalado em casa e até consigo me virar. Bom, mas que graça tem usar um sistema operacional sem programá-lo?

Além do C/C++, que é lingua franca em todos os sistemas operacionais, tendo estudado algumas bibliotecas. Fiz alguns testes com Qt (o toolkit usado pelo KDE) e também com GTK (usado pelo Gnome). Tendo mais ao Qt, por ser nativamente C++ e por achar o KDE mais conciso do que o Gnome.

Além do C++, tenho feito testes com o Mono, a runtime .NET para Linux, do pessoal da Ximian/Novell. Apesar de toda a dificuldades com a implementação do Windows.Forms (eles também desistiram do Wine), o trabalho deles é espantoso. Nos poucos testes que eu fiz, o JIT do Mono me pareceu sensivelmente mais rápido do que o JIT do .NET Framework. Ainda vou fazer alguns testes com sistemas maiores, mas a primeira impressão é muito boa. Na empresa onde eu trabalho, estamos começando a projetar um sistema grande, com a camada de negócios separada da apresentação. Isso vai me dar vários componentes sem dependência de Windows.Forms para testar no Mono. E prometo que publicarei os testes aqui no site.

Falando em Mono, eu troquei uns e-mails com o Miguel de Icaza, o cara que projetou e coordenou a implementação do Mono. O mais legal é que, em um dos e-mails, ele me deu uma "bronca", dizendo que o fato de eu ter nascido no mundo Microsoft não é um motivo para eu me sentir tão perdido no UNIX ("não vou comprar seu argumento, simplesmente porque computadores são divertidos"). Além disso, ele me indicou um livro, o The UNIX Programming Environment, que eu comprei e estou lendo.

Esse livro foi escrito em 1984. Pode parecer desatualizado, não? É, mas não está. E lendo esse livro eu, além de aprender um pouco da filosofia do UNIX, tenho aprendido que as grandes novidades na área de programação são reinvenções de tudo que já existe desde de 1960.

O UNIX trabalha com o conceito de stream, usando e abusando da capacidade de redirecionar o stdout de um programa para o stdin de outro (em português: redireciona a saída de um programa para a entrada de outro). Esse conceito permite que vários programas trabalhem em conjunto, multiplicando o poder das ferramentas. Você pode até trocar aquele funcionário incompetente por um shell script :-).

Mas como um programa entende a saída de outro? Simples, usando o formato texto, com tudo separado com quebra de linha (\n). Assim todo mundo se entende. Isso te lembra alguma coisa? Não mesmo? Pense melhor...

XML. Padrão baseado em SGML (texto) para intercâmbio de informações. Assim todo mundo se entende. Mas, considerando os fatos acima, XML é uma evolução, e não uma revolução. Seria uma revolução se todos os programas, desde o início dos tempos, trabalhassem com formatos fechados e binários, como o Microsoft Word. Mas é evolução, já que isso já existe desde a década de 60.

Mais uma simples evolução: SOA, arquitetura orientada a serviços. RPC baseado em SOAP (XML) trafegando em HTTP. Sem binding em tempo de compilação = IDispatch. Sem guardar estado = programação procedural. Sim, isso mesmo, programação procedural. As funções são "abrigadas" em objetos, que simplesmente fazem o mesmo papel que um namespace C++. Isso vem desde o pooling de componentes do COM+, com objetos stateless (que não guardam estado). E aí, qual a revolução?

Conclusões que eu cheguei:

  • Evolução é uma palavra que só serve para o pessoal de marketing
  • Sim, o Linux é legal... :-)
  • Como ler um livro pode fazer a gente pensar em tantas coisas
  • Como é colossal a quantidade de informações que você pode absorver estudando sobre um assunto
  • Quando você estuda um assunto, você obtêm informações que podem te levar a conclusões sobre outros assuntos
  • Ainda bem que eu não virei advogado, como minha família queria...

Em 11/10/2004 13:07 - Comentários (1)

Saudações ao debutante

Alfred Gary Myers, o cara que mais entende de .NET que eu conheço, resolveu de uma vez por todas escrever um blog. Vão aqui meus votos para que ele realmente leve isso a sério e escreva freqüentemente.

Em 18/10/2004 20:03 - Comentários (2)

Projeto de lei 1947/2003 sobre a regulamentação da profissão de Analista de Sistemas: eu sou contra!

Existe um projeto de lei, proposto por um deputado do Rio de Janeiro (Eduardo Paes), que prevê a regulamentação da Profissão de Analista de Sistemas e a criação de um conselho, como a CREA, para os profissionais de informática.

Até aí, tudo muito bom. Até ler o conteúdo da lei:

Art.2º Poderão exercer a profissão de Analista de Sistema no País:

I - os possuidores de diploma de nível superior em Análise de Sistemas, Ciência da Computação ou Processamento de Dados, expedidos no Brasil por escolas oficiais ou reconhecidas pelo Governo Federal;
II - os diplomados por escolas estrangeiras reconhecidas pelas leis de seu País e que revalidarem seus diplomas de acordo com a legislação em vigor;
III - os que, na data de entrada em vigor desta lei, tenham exercido, comprovadamente durante o período de, no mínimo 5(cinco) anos, a função de Analista de Sistema e que requeiram o respectivo registro aos Conselhos Regionais de Informática.

E tem mais:

Art.31 Constituem infrações disciplinares, além de outras:
I - transgredir preceito de ética profissional;
II - exercer a profissão quando impedido de fazê-lo, ou facilitar, por qualquer meio, o seu exercício aos não inscritos ou impedidos;

Não esqueça que "impedido de fazê-lo" é alguém que não tenha diploma na área de informática. E passar um projeto para uma pessoa não formada será uma infração passível de punição.

O deputado Eduardo Paes é advogado, ex Secretário do Meio Ambiente do Rio, e assumidamente não entende de informática. Não estou simplesmente o desqualificando, mas acho que a visão dele da nossa área é equivocada, ele está tratando nossa área como se fosse Medicina e Engenharia. Inclusive, as pessoas que defendem a criação da lei insistem em fazer comparações com essas áreas, que como sabemos, tem um dinâmica completamente diferente da nossa. Eles chegam até a dizer que tirando os "sem diploma" do mercado o salário aumentará graças a lei da oferta e procura. Egoísmo pouco é bobagem mesmo...

Eu já gastei meu latim (sem sucesso) em uma lista de discussão sobre o assunto, tentando explicar que tirar as pessoas sem diploma do mercado não é o mais correto. Sabemos que existem MUITOS analistas e programadores sem diploma que são MUITO melhores do que os formados. Além disso, me parece um instrumento visivelmente criado por alguém que não tem conhecimento da área, e para profissionais limitados que querem manter seus empregos às custas de um diploma. Querem criar mais um indústria dos "assinadores de projeto", como existe em jornalismo e engenharia. Talvez a regulamentação seja boa, mas o texto dessa lei está completamente equivocado.

Criei uma nova lista de discussão para as pessoas que SÃO CONTRA essa lei. Caso alguém queira ajudar com argumentos, ou ajudar a pensar na forma mais adequada de manifestação para evitar que essa lei seja aprovada, sinta-se convidado.

Caso você não conheça direito a lei, dê uma lida nela e nos seus apensos. Caso você chegue a conclusão que também é contra, entre na lista. Caso ainda não tenha opinião formada ou deseje somente discutir o mérito da questão, use a lista da RioJUG que foi criada para isso.

Parece que o pessoal da Sociedade Brasileira de Computação também é contra a exigência de diploma na área. Fiquei muito feliz ao saber disso.

Para mais detalhes

Texto do projeto de lei
Página com todas as informações dos projetos. Leia os apensos e veja as justificativas, você dará boas risadas

Em 19/10/2004 12:16 - Comentários (16)

Wikipedia tem tudo, inclusive algoritmos

Quem leu o About do site deve saber o quanto eu gosto da Wikipedia. É a enciclopédia livre e aberta, com mais conteúdo do que as enciclopédias pagas. E como bastante gente que trabalha diretamente com programação escreve lá (até eu já escrevi algumas coisas...), existe bastante conteúdo sobre programação.

Mas um das coisas mais legais é que a Wikipedia tem muita informação sobre algoritmos, inclusive com implementações em diversas linguagens. Duvido que a Barsa que sua mãe comprou tenha a implementação de quicksort em Lisp. :-)

Além da definição de um algoritimo, temos a explicação e implementação de quick sort, busca binária, red black tree, entre outros. Fora os links para sites com mais detalhes e como animações Java para você entender melhor o algoritmo. Precisa de mais alguma coisa?

Em 19/10/2004 17:56 - Comentários (1)

Mostre-me teu Menu Iniciar e digo quem tu és

Em 20/10/2004 00:02 - Comentários (1)

Se o programador é bom, não importa a ferramenta

Uma das características de um bom programador é saber escolher a ferramenta certa para a tarefa certa. Apesar de eu ser um ferrenho programador Visual C++, quando eu preciso fazer programinhas de cadastros eu uso VB6 ou .NET.

Mesmo assim, existem pessoas que fazem coisas muito boas, mesmo que a ferramenta não seja a mais adequada. Eu lembro que a uns 8 anos atrás eu copiei da antiga revista Micro Sistemas (alguém lembra dessa?) um driver de mouse para QBASIC, usando DATA e PEEK/POKE. Eu eu comecei a fazer um Adventure, com interface gráfica e tudo, em QBASIC. Cheguei até a fazer uma lib para interface tipo Windows, como botão 3D e um sistema tosco de caixas e coordenadas. Bons tempos aquele, quando eu nem sabia que programação era uma profissão, pra mim era tudo diversão.

O fato é: a grande maioria dos softwares são feitos em C/C++. Apesar de serem linguagens altamente técnicas e muito complexas para iniciantes, a flexibilidade faz com que elas sejam as preferidas. Já falei sobre isso em um post anterior. Mas existem pessoas que que usam linguagens menos comuns - ou até mesmo não adequadas - para contruir programas. Alguns exemplos:

BitTorrent: Software para downloads distribuídos, uma das grandes invenções em P2P. Um conceito maravilhoso, implementado em Python e com a GUI feita em wxWindows, usando um bind para Python.

Azureus: Client para BitTorrent, feito em Java. Não que Java não seja uma ferramenta adequada para isso, mas os programas GUI feitos em Java, na sua grande maioria, são muito feios. Esse programa (junto com o Eclipse e as IDEs da Borland) é muito bonito e funciona muito bem. É meu client BitTorrent preferido.

DHTML Lemmings: O CAMPEÃO!!! Link enviado pelo meu amigo Fábio. Um clone do jogo Lemmings implementado em DHTML/CSS/JavaScript. Joguei só as primeiras fases, mas o jogo é perfeito. Eu custei a acreditar que ele foi feito somente em JavaScript.

Em 20/10/2004 17:24 - Comentários (0)

Testando ASP.NET, rodando em Linux e Mono

O MonoForge, uma iniciativa do provedor italiano Impulso, está aceitando candidatos para um período de beta test para hospedar aplicativos feito em ASP.NET. A grande novidade é que os servidores usarão Linux e Mono (a runtime .NET para Linux/Unix/MacOS da Ximian/Novell), ao invés servidores com Windows.

Serão 10MBs de espaço, e 1 banco mySQL. Sendo assim, você não pode simplesmente copiar seu aplicativo ASP.NET feito para SQL Server e vê-lo rodando, a não ser que você tenha usado alguma camada para abstração de banco de dados. Mas como as classes de acesso a banco de dados suportam as interfaces IDbXXX, você pode dar sorte, e um find/replace para trocar as declarações pode resolver o seu problema - se você não usou nenhum recurso esotérico do SQL Server, é claro.

É uma ótima oportunidade para testar o Mono sem usar um servidor próprio.

Em 25/10/2004 13:04 - Comentários (0)

Um dia minha filha terá orgulho disso

Minha pequena Heloisa nasceu faz um pouco mais de uma semana. Além da minha felicidade de pai de primeira viagem, isso também gerou uma história interessante para contar. Afinal, nada da vida de um escovador de bits pode passar em branco... :-)

Quem conhece um pouco de gravidez, sabe que horas antes (pode ser até 48 horas) do parto, a mulher começa a sentir as contrações e o corpo começa a se preparar para o trabalho de parto. As mães que fazem pré natal são orientadas pelos médicos e enfermeiras a anotar a duração e intervalo das contrações, para saber como anda a evolução do trabalho e ajudar a saber a hora certa de ir para um Hospital (ou para uma Casa de Parto, como foi o meu caso).

Enquanto eu estava no trabalho, minha esposa ligou dizendo que já estava sentindo algumas contrações leves. E também me disse que estava controlando isso, anotando os horário no papel. Poxa, no papel?? Nem uma planilhinha Excel?? Em que mundo vivemos?? :-)

Quando eu cheguei em casa, ouvi o que eu queria: "Ficar olhando no relógio e anotando essas contrações já está enchendo o saco...". Foi aí que eu perguntei se um programinha no iPaq ajudava. Disse que seria só clicar quando começasse e quando terminasse a contração, que ele faria o resto. Então, ela disse sim. :-)

Abri feliz meu Visual Studio, iniciei um projeto para PocketPC em C#. Meia hora, um DataGrid, um DataSet e um Button depois, eu tinha uma versão alfa, que testamos nas próximas contrações. Corrigi alguns bugs e fui colocando mais recursos a medida que ela ia pedindo. Mais uma hora programando e acompanhando as contrações eu tinha um programa para controlar contrações pronto.

        

A parte que está no DataGrid é simples. Os tempos que estão embaixo são as médias. O programa salva o DataSet em um arquivo "\contra.xml", e pergunta no início se você quer carregá-lo para continuar contando as contrações.

Você pode baixar os fontes do programa aqui. Caso queria somente o programa para usar, você pode baixar o programa, instalar o .NET Compact Framework no seu PocketPC e depois copiar o programa pra ele (não precisa instalar). E como ele é feito em .NET, esse programa também roda em qualquer PC que tenha o .NET Framework instalado (fica feio, mas roda).

Acho desnecessário, mas lá vai:
Esse programa é livre e pode ser usado por qualquer um, sem restrições. Sendo assim, NÃO DAREI NENHUM TIPO DE SUPORTE. Também não me responsabilizo caso alguma conta esteja errada, não se esqueça que eu programei isso enquanto minha esposa estava tendo contrações... Também não me responsabilizo se alguém parir em meia hora ou se a mulher passar o tempo todo jogando paciência no iPaq e não controlar as contrações. E claro, ignorarei qualquer pedido para que o programa seja portado para VB.NET :-)

Em 31/10/2004 17:40 - Comentários (12)

WinDbg novo!

O WinDbg 6.4.4.4 beta já está disponível para download. Eu já baixei o meu :-)

Em 01/11/2004 00:15 - Comentários (0)

Novo servidor

Depois de ficar realmente decepcionado com a instabilidade do meu antigo servidor, resolvi fazer uma troca. Espero que esse novo hosting faça jus aos 97% de aprovação e satisfação que eles conseguiram no Find My Hosting. A aprovação do meu antigo era de 80%, talvez isso diga alguma coisa...

Em 02/11/2004 11:28 - Comentários (5)

Programando drivers com bom humor

Toda equipe sempre tem um programador que acaba colocando uma gracinha ou outra em um programa. Eu faço questão de não deixar as equipes que eu participo sem esse tipo de pessoa. Além de ter o costume de colocar uns comentários não muito convencionais no código, eu às vezes aproveito para colocar um ovinho de páscoa nos programas. O que seria da nossa vida sofrida de programador se não tivessemos bom humor?

Olhem só um erro que foi retornado pelo BUILD (programa que gerencia a compilação de drivers) do DDK quando eu, por engano, coloquei um arquivo .h na lista de SOURCES:

BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
BUILD: Object root set to: ==> objchk_w2K_x86
BUILD: Compile and Link for i386
BUILD: Loading c:\WINDDK\3790\build.dat...
BUILD: Computing Include file dependencies:
BUILD: Examining d:\data\sp\source2\drivers\spfwu directory for files to compile.
    d:\data\sp\source2\drivers\spfwu 
BUILD: d:\data\sp\source2\drivers\spfwu: Interesting sources extension: callbacks.h

Eu também acho que .h é uma extensão bem interessante... :-)

Em 02/11/2004 19:47 - Comentários (3)

Programadores sepultam programas mortos

Terra Informática: Programadores sepultam programas mortos

Slashdot: Programmers Hold Funerals for Old Code

Eu tenho tantos programas para chorar... Os meus pelo menos eu mantenho em estado de hibernação no meu HD, não tenho coragem de matar. Afinal, algum dia eu ainda posso querer modificar o programa em Clipper que eu fiz quando tinha 16 anos, vai saber...

Editado em 05/11/2004: Um link do Slashdot é muuuuuuuuuito melhor do que um link do Terra Informática

Em 04/11/2004 13:18 - Comentários (0)

Atendendo a pedidos: Controle de Contrações para PC, baixável e feito em WTL

Atendendo aos pedidos do meu amigo Wanderley e da minha querida doula Ana Cris, resolvi fazer uma versão do meu Programa Para Controle de Contrações Para Parturientes e Similares que roda em PC e feito em WTL. O layout do programa não é lá essas coisas, mas eu tentei manter o design e o funcionamento parecidos com a versão para PocketPC. E apesar do pedido da Ana Cris para que o programa fosse "instalável", eu resolvi teimar e fazer um que não precisa instalar, é só baixar e rodar. :-)

Chega de ladainha, eu só quero fazer o download...

O programa foi desenvolvido usando o Visual C++ 7.1 e o WTL 7.5. Como eu não usei nada esotérico, acredito que ele deve ser compilável usando versões mais antigas do WTL. Mesmo assim, não sei se isso vai compilar no Visual C++ 6.0.

Como em Win32 não existe DataGrid, eu usei um ListView, que serve muito bem para esse propósito. Ao invés de usar um DataSet para gravar os dados, eu usei um std::list e populei o ListView "na mão".

Foi difícil resistir a tentação de usar o ATL::CAtlList no lugar do std::list, mas eu estou querendo me livrar do vício do ATL e ser um pouquinho mais multiplataforma. Acho que eu nunca escrevi sobre isso ainda, mas eu me recuso a usar a lib do C. Eu gosto mesmo é de CreateFile, ATL::CAtlFile, CreateFileMapping, CoCreateInstance e CoMarshalInterThreadInterfaceInStream :-)

Olhem os trechos principais do código:


typedef struct _CONTRA_INFO
{
   __time64_t StartTime;
   __time64_t EndTime;
} CONTRA_INFO;

typedef std::list CContraList;

...

void CMainDlg::ChangeButtonText()
{
   m_Button.SetWindowText(m_bContraInProgress ?
                          _T("Ufa, acabou...") : 
                          _T("Está doendo, socorro!!"));
}

void CMainDlg::UpdateList()
{
   CContraList::iterator CurrentIterator, PreviousIterator;
   int iItem;
   
   ATL::CTime CurrentTime, PreviousTime;
   ATL::CTimeSpan Duration, Interval;
 
   CurrentIterator = m_ContraList.end();
   CurrentIterator--;
   
   if(m_bContraInProgress)
   {
      //
      // contração acabou de iniciar. Calcular o intervalo entre 
      // a última e colocar o horário inicial no ListView
      //

      //
      // Colocar no ListView
      //
      CurrentTime = CurrentIterator->StartTime;
      iItem = m_lstContra.InsertItem(100000, CurrentTime.Format(_T("%x %X")));

      //
      // se for a primeira contração, não tem intervalo para calcular
      //
      if(CurrentIterator != m_ContraList.begin())
      {
         PreviousIterator = CurrentIterator;
         PreviousIterator--;
         

         ATLASSERT(PreviousIterator->EndTime != 0);
         Interval = CurrentIterator->StartTime - PreviousIterator->EndTime;

         m_TotalInterval += Interval.GetTimeSpan();

         m_lstContra.SetItemText(iItem,2,Interval.Format(_T("%M:%S")));
      }
   }
   else
   {
      //
      // a contração acabou. É só calcular a duração e colocar no último item
      //
      iItem = m_lstContra.GetItemCount() - 1;
      
      ATLASSERT(CurrentIterator->StartTime & CurrentIterator->EndTime);
      Duration = CurrentIterator->EndTime - CurrentIterator->StartTime;

      m_TotalDuration += Duration.GetTimeSpan();

      m_lstContra.SetItemText(iItem,1,Duration.Format(_T("%M:%S")));
   }
}

void CMainDlg::DoStatistics()
{
   ATL::CTimeSpan t;
   int iItemCount = m_ContraList.size();
   
   if(iItemCount < 2)
      return;

   t = m_TotalDuration / (iItemCount - (m_bContraInProgress ? 1 : 0));
   m_stcDuration.SetWindowText(t.Format(_T("%H:%M:%S")));


   //
   // como o primeiro item não tem intervalo, é -1
   //
   t = m_TotalInterval / (iItemCount - 1);
   m_stcInterval.SetWindowText(t.Format(_T("%H:%M:%S")));
}

LRESULT CMainDlg::OnContraClick(WORD,WORD,HWND,BOOL&)
{
   CONTRA_INFO ContraInfo;
   CContraList::iterator i;
   
   if(!m_bContraInProgress)
   {
      //
      // início da contração
      //
      ContraInfo.StartTime = _time64(NULL);
      ContraInfo.EndTime = 0;

      m_bContraInProgress = TRUE;
      m_ContraList.push_back(ContraInfo);
   }
   else
   {
      ATLASSERT(!m_ContraList.empty());
      i = m_ContraList.end();
      i--;
      
      i->EndTime = _time64(NULL);
      m_bContraInProgress = FALSE;
   }

   UpdateList();
   ChangeButtonText();
   DoStatistics();
         
   return TRUE;
}

Algumas coisas podem ser notadas nesse trecho de código:

  • Não acho que esse código seja muito mais complicado do que um código em .NET. Complicado e difícil de compreender é a lib do C, que tem mais de 3 décadas. ATL/WTL facilitam muito as coisas, e o código fica MUITO pequeno e MUITO rápido.
  • Eu não me preocupei com otimizações. O máximo que eu fiz é usar __time64_t ao invés de ATL::CTime na hora de gravar os horários, para reduzir a atividade desenfreada de copy constructors. Até porque, hoje em dia todo mundo usa .NET, que é muuuuuuuuito lento (não me venham com esse papo de "depende"). Então não me venham falar que meu código em C++ poderia ser mais rápido... :-)
  • Programar drivers faz bem! Quem já programou KernelMode vê a clara influência do estilo WinDDK no meu código. Uso de ASSERTS, código claro, comentários claros e destacados, variáveis com nomes claros. Olhe algum sample do DDK e veja o que é programar bonito!
  • Eu usei TCHAR (veja as macros _T()) para que eu pudesse compilar em UNICODE (Windows NT/2000/XP/2003) e em ANSI (Windows 9x/Me). No projeto existem 3 configurações: Debug, Release e Release ANSI. Eu costumo compilar somente UNICODE, mas resolvi de última hora ser solidário com os usuários de Win9x, afinal, eles já têm problemas suficientes.
  • Eu coloquei o ATL:: antes de CTime e CTimeSpan para deixar bem claro que o código não tem nada a ver com MFC.

Como o programa é feito em WTL (a melhor lib do planeta para desenvolver código Win32), nós temos um executável de 84kb sem dependência de nenhuma runtime ou DLL. É só baixar o executável e rodar.

Downloads
Executável UNICODE (Windows NT/2000/XP/2003/Longhorn)
Executável ANSI (Windows 9x/Me). Eu não tenho uma máquina Windows 9x para testar, me avisem caso não funcione.
Fontes do programa. Você vai precisar do WTL para compilar.

Em 07/11/2004 17:41 - Comentários (0)

Realizando o meu desejo: Todos os posts do weblog, em ordem cronológica

Eu leio diversos weblogs diariamente, e existe um recurso que eu sinto falta: poder ler todos os posts de um weblog em ordem cronológica. Em todos os blogs os posts são organizados no esquema de "os mais atuais primeiro", e quando alguém escreve uma série de posts relacionados, você é obrigado a ficar lendo de trás para frente, como fazemos com rastros de e-mail. Ou pior, você tem uma lista com os posts antigos e você tem que clicar um por um. Alguns weblogs tem a funcionalidade de calendário, para que você veja em uma tela só os posts de um determinado mês. Sinceramente, a não ser que seja um weblog daqueles sentimentais ("hoje acordei feliz, o dia está bonito e eu vesti aquela minha meia das Meninas Superpoderosas"), a data em que o post foi escrito pouco importa.

Como eu não posso modificar os weblogs dos outros, então eu mudo o meu. Se você quer ver tudo que eu escrevi até hoje, e em ordem cronológica, agora você pode. Um link para esse recurso também está disponível no arquivo do WebLog.

Em 07/11/2004 20:20 - Comentários (0)

Firefox 1.0!

Hoje, dia 09/11/2004 está oficialmente lançado o Mozilla Firefox 1.0

Eu uso o Firefox desde o 0.1 (quando ele ainda chamava Firebird), ele já era muito bom desde aquela época. E está ficando cada dia melhor.

Caso você ainda sofra com os demorados patches do Internet Explorer, sugiro que você mude para o Firefox. Caso você seja um power-user, mude para o Firefox e depois escolha as extensões (plugins) que mais te agradam. Eu uso Ad-Block (bloqueia banners), Tabbrowser Extensions (melhor controle das tabs), Sage (leitor RSS), e o All-in-one Gesture (comande seu browser somente movendo o mouse). Resumindo: caso você navegue na web, eu sugiro que você use o Firefox!


Get Firefox

Em 09/11/2004 10:13 - Comentários (0)

Novo artigo: Como ser um bom programador

Existem diversos fatores que levam alguém a ser um bom programador. Selecionei os mais importantes e escrevi um artigo, sobre como ser um bom programador. Coloquei nele também algumas dicas importantes sobre como procurar informações na Internet e em lista de discussão. Além de lembrar que essa estória de "não preciso de livros, tem tudo na Internet" é coisas de gente que não sabe o que fala. :-)

Em 15/11/2004 19:15 - Comentários (0)

Achei um blog sobre desenvolvimento de drivers!

Para os interessados em programação de drivers para WinNT, dêem uma olhada no blog de Steve Dispensa (MVP para Windows DDK), one ele escreve algumas dicas sobre desenvolvimento e teste de drivers.

Estou preparando um post sobre como iniciar o desenvolvimento de drivers e onde encontrar informações, aguardem!

Em 18/11/2004 10:13 - Comentários (0)

Como fazer drivers para Windows NT/2000/XP/2003, parte 1

A dúvida mais frequente que eu recebo pelo formulário de contato é: "como eu faço para desenvolver drivers para Windows?". Vou publicar algumas dicas aqui para quem está iniciando.

Pré requisitos

Primeiramente eu espero que você seja um bom programador, programar drivers não é fácil. :-)

Para fazer um driver, você precisa SABER programar drivers (programação kernel mode). Parece óbvio mas não é. Eu recebo todo mês pelo correio a newsletter da OSR, e a do mês passado era uma edição especial com o dobro do tamanho normal, falando sobre teste de drivers. E lá eu li algo muito interessante: em kernel mode, é impossível fazer programação cowboy. Programação cowboy é quando você não entende muito do assunto, pega um código exemplo daquilo que você quer e sai ajustando e remendando até que fique bom, e depois entrega para o cliente. Quando a coisa roda em kernel mode, isso nunca vai ficar bom, a não ser que você saiba mesmo o que está fazendo. Eu conheço uma empresa que está remendando um driver faz mais de dois anos, e até hoje não funciona direito.

Você precisa ser um bom programador C, ponto. Caso você ainda tenha dúvida sobre ponteiros ou estruturas de dados, programação de drivers é um péssimo lugar para treinar isso. O indicado é que você consiga fazer um programa Win32 com certa facilidade antes de se aventurar a programar drivers. Alguns conceitos de passagem de parâmetros IN e OUT usando estruturas e ponteiros void que viram ponteiro para estruturas mágicas também são usados em Win32. Ah, e se você não souber o que é uma lista ligada, veja antes na wikipedia, você vai usar isso muito.

(E antes que alguém pergunte, experiência com C#, VB.NET, VB6 ou Java não vai te ajudar em muita coisa além do treino em lógica de programação)

Onde encontrar informações

Agora que você já passou pelo blá-blá-blá, vamos ao que realmente interessa. Se você quer programar em kernel mode você terá que estudar bastante, e é melhor que comece o quanto antes.

Livros

Microsoft Windows Internals - Esse é o primeiro que você deve ler e reler. Não trata especificamente sobre programação kernel mode, mas descreve o funcionamento do kernel do Windows. E é ESSENCIAL que você saiba isso.

Programming the Windows Driver Model - Esse trata especificamente de programação kernel mode. Windows Driver Model (WDM) é um modelo de programação de drivers (mais voltado para drivers que controlam hardware). Caso seu driver siga as regras do WDM, seu driver pode ser compilado para Windows 2000 ou superior e Windows 98 e superior. Mesmo que seu driver não seja para um hardware (um firewall é um driver e não controla hardware), vale a pena ler esse livro, ele explica muito bem como funciona a sincronização e o I/O em kernel mode.

Existem mais livros, esses dois são os que eu tenho e uso para consulta. Procure na Amazon.

Internet

OSR Online - Empresa dos Estados Unidos que além de dar treinamentos sobre desenvolvimento kernel mode, disponibiliza bastante material sobre isso.

Windows Drivers Developer's Digest (W3D) - Artigos escritos por autoridades do assunto, como Walter Oney e Thomas Divine.

Listas de discussão - Nessa categoria temos as listas da OSR, o newsgroup da Microsoft sobre drivers e a lista da PCAUSA (específica para a parte de rede e afins).

Na continuação dessa série, falarei sobre o DDK e como colocar a mão na massa.

Em 21/11/2004 09:47 - Comentários (7)

Como contratar alguém para uma equipe de testes

Sem uma equipe de testes é MUITO DIFÍCIL entregar um software com qualidade, qualquer que seja a equipe de testes. A equipe pode ser formada por profissionais treinados e experientes em testes ou um estagiário mais esforçado. Apesar de não ser o ideal, o estagiário esforçado é muito melhor do que ninguém ligado ao desenvolvimento testando. Uma coisa muito importante: um estágiário de desenvolvimento serve, mas um estagiário ligado ao usuário não serve. O foco dele é outro e muitas vezes a equipe não tem o acceso necessário a ele. Todos os "testadores" devem ser ligados a equipe de desenvolvimento.

(Já trabalhei em uma empresa onde o responsável pelos testes era o Analista Funcional. Não considero isso uma equipe de testes, pois um Analista Funcional vai fazer os mesmos testes que o usuário vai fazer, testes de regras de negócios. Uma equipe de testes faz testes de stress, casos de usos e usos atípicos)

Eu já trabalhei com equipe de testes e sem equipe de teste. Sem é muito mais difícil, porque o programador tem que perder tempo com testes de validação, tentar deixar campos sem preencher, etc. Um bom estagiário faria isso sem problemas, e é muito melhor do que o programador perder tempo com isso. Isso otimiza o tempo de desenvolvimento e deixa o programador fazendo o que ele tem que fazer: programar.

Uma equipe de testes ajuda os programadores a escreverem um código de melhor qualidade. Se o programador sabe que a maioria dos erros serão pegos pela equipe de testes, ele terá mais atenção ao programar. Não existe nada mais desanimador do que um sistema de controle de bugs (sua empresa tem um, não tem?) com dezenas de bug com o seu nome estampado.

Caso sua empresa não tenha uma equipe de testes ou você esteja montando essa equipe, dê uma lida nesse artigo da Sticky Minds com dicas para contratar "testadores".

Em 26/11/2004 15:12 - Comentários (0)

Lendo a camiseta do ThinkGeek sem programar

O site do ThinkGeek está com uma promoção onde, gastando mais do que US$ 100,00 você ganha uma camiseta:

Essa camiseta tem o logo do SourceForge na frente e um número binário giganteco atrás. É CLARO que esse número quer dizer alguma coisa. Perdi 10 minutos hoje para encontrar o significado, me propondo a não usar uma só linha de código. Olhe o que eu fiz:

  1. Copiei o número binário do site;
  2. Criei um novo arquivo TXT no Visual Studio.NET, e colei o binário lá;
  3. Usando Regular Expressions, tirei as quebras de linha (troquei "n" por "");
  4. Usando Regular Expressions novamente, deixei que cada linha tivesse oito dígitos (troquei "{^........}" por "1n"). Pronto, agora temos números binários de 8 dígitos, que podem ser convertidos para char.


  5. Copiei o resultado e colei no Excel;
  6. Usei a função "BIN2DEC" do Excel para converter os binários em decimais. Caso o seu Excel diga que essa função não existe (colocando #NAME nas células), vá no menu Tools >> Addins e ative o "Analysis ToolPak";
  7. Depois, usei a função "CHAR" para achar os caracteres correspondentes aos números de acordo com a tabela ASCII;


  8. Copiei os caracteres para Visual Studio.NET e substitui "n" para "";
  9. I spent $100 at ThinkGeek and all I got was this lousy limited edition t-shirt.


Caso você tenha outra solução, sinta-se a vontade para mostrá-la nos comentários.

Em 02/12/2004 15:40 - Comentários (5)

Internet sem firewall é suicídio

Um texto da ArsTechinica conta sobre uma experiência feita com computadores conectados à Internet via banda larga, para descobrir quanto tempo eles levariam para ser atacados.

O resultado é inacreditável. O computador com Windows XP Service Pack 1 foi invadido 4 minutos após ter sido conectado. Na maioria dos casos, o computador invadido é transformado em um robô para envio de spam e vírus, e o usuário nem se dá conta disso.

Apesar de complicado para os usuários comuns, isso tem uma solução. Atualize seu Windows, e permita que o Service Pack 2 do Windows XP ative o firewall.

Em 03/12/2004 09:00 - Comentários (0)

Você acha que todos os autores de livros sabem o que falam?

Eu encontrei essa pérola no livro "Aprendendo ASP.NET com C#", de autoria de "Américo Damasceno Jr":

" No Windows temos duas áreas de memória que podemos colocar algo: o stack e o heap

O stack é uma área menos nobre, pois quando entram novos item nela, os mais antigos caem fora (se autodestroem, digamos assim). Coisas que não requerem muita permanência são jogados no stack. Um parâmetro que não é um value-type e seu valor são um caso típico, pois geralmente o processamento do bloco termina rapidamente."

  • Quer dizer que eu posso "colocar" algo em uma área de memória? O que seria esse algo?
  • "pois quando entram novos item nela, os mais antigos caem fora". Ouvi poucas coisas mais estúpidas em toda minha vida
  • "parâmetro que não é um value-type e seu valor são um caso típico". Essa parte eu não consegui entender.
  • Sabe o que é pior? Eu já vi gente no fórum da MSDN brasileira recomendando esse livro.

    Eu sei que mesmo os livros ruins têm mercado, principalmente entre os desenvolvedores iniciantes. Mesmo assim eu me reservo o direito de repugnar um livro escrito às pressas, com péssima qualidade e cheio erros.

    Em 13/12/2004 09:14 - Comentários (1)

    O bom filho à casa torna

    Chega de DataSets, chega de DataAdapters, chega de MessageBox.Show. A partir de segunda-feira (dia 20 de dezembro), voltarei a trabalhar com minha linguagem de programação preferida. Agora, ao invés de ".NET de dia e C++ de noite", eu voltarei ao esquema "C++ de dia e C++ de noite".

    Apesar da tristeza por deixar meus amigos de trabalho, minha alma de escovador de bits fala mais alto. Até onde eu sei terei bastante ATL, multithread e sockets pela frente. Ou seja, do jeito que eu gosto... :-)

    Em 15/12/2004 14:39 - Comentários (1)

    Usando o WinDbg para entender um software multithread

    É necessário que você conheça pelos menos as funções básicas do WinDbg para entender esse post. Escrevi dois artigos sobre WinDbg que podem ajudar (parte 1 e parte 2)

    Como já disse no post anterior, comecei o ano com o pé direito, voltando a trabalhar com Visual C++. E essa mudança de emprego e as festas de fim de ano são as minhas desculpas para o grande intervalo nos posts. E feliz 2005 para todos!

    Mas esse tempo não foi de todo perdido. Encontrei alguns problemas com o RSS do blog e resolvi. Agora o RSS está verificado e deve funcionar com qualquer NewsReader.

    Vamos ao que interessa. A empresa onde eu trabalho produz software de alto desempenho para o mercado financeiro. Os softwares se comunicam de diversas maneiras, sockets, File Mapping, MSMQ, etc. Muitas vezes existem comunicações entre as threads do mesmo processo, e essas comunicações são feitas de diversas maneiras, filas, mensagens inter thread, etc. Lendo o código é possível entender o que cada ThreadFunc faz, mas o mais fácil mesmo é colocar o software para rodar e analizar as comunicações entre as threads. Vamos ver como o WinDbg ajuda nisso.

    Com o executável já rodando e sob o controle do WinDbg, vamos dar um break e analizar como as coisas acontecem. Primeiro vamos listar as threads do processo usando o comando "~":

    0:027> ~
       0  Id: 890.bf8 Suspend: 1 Teb: 7ffde000 Unfrozen
       1  Id: 890.cd8 Suspend: 1 Teb: 7ffdd000 Unfrozen
       2  Id: 890.da0 Suspend: 1 Teb: 7ffdc000 Unfrozen
    ...
      41  Id: 890.95c Suspend: 1 Teb: 7ff91000 Unfrozen
      42  Id: 890.2a4 Suspend: 1 Teb: 7ff90000 Unfrozen
      43  Id: 890.de4 Suspend: 1 Teb: 7ff8d000 Unfrozen
    

    Bom, temos 44 threads. Um processo pode ter diversos thread pools, cada um para uma determinada tarefa. Podemos ter um thread pool para recepção de comunicação TCP/IP, outro pool para acesso ao banco de dados e outro ainda para acessar o MSMQ. Primeiro temos que saber o que cada thread está esperando.

    (Está se perguntando o que é um thread pool? É quando você colocar diversas threads para atender solicitações em paralelo. Cada vez que é recebida uma solicitação pelo processo, a thread que gerencia o pool sinaliza uma das threads para que ela trate essa solicitação. Isso faz com que uma solicitação não trave outra, já que elas estarão rodando em threads diferentes. Uma thread de um pool quase sempre fica esperando uma sinalização (evento, semáforo, etc) usando WaitForMultipleObjects)

    O comando "~", além de listar as threads do processo, também é usado como um modificador de comandos, fazendo com que o comando em uso seja direcionado para uma ou mais threads especificadas. Vamos testar isso com o comando "k". Esse comando faz um dump da pilha da thread atual. Vamos usá-lo:

    0:027> k
    ChildEBP RetAddr  
    02f3ffc8 77f77fe8 ntdll!DbgBreakPoint
    02f3fff4 00000000 ntdll!DbgUiRemoteBreakin+0x36
    

    Como nós forçamos um breakpoint com CTRL+BREAK, nós estamos na thread de debug. Primeiro vamos utilizar o comando "~" para ver algumas informações da thread, e depois veremos como o comando "~" pode modificar o comando "k":

    0:027> ~35
      35  Id: 890.84 Suspend: 1 Teb: 7ff98000 Unfrozen
          Start: kernel32!BaseThreadStartThunk (77e4a99b) 
          Priority: 0  Priority class: 32
    
    0:027> ~35 k
    ChildEBP RetAddr  
    0374fda8 77f4372d SharedUserData!SystemCallStub+0x4
    0374fdac 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0374fe54 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0374fe6c 0041d3bc kernel32!WaitForMultipleObjects+0x17
    0374fee8 004300eb XXX!CInterThreadMessageQueue::WaitForMessageEx+0x8c 
    0374ffb8 77e4a990 XXX!CResponseThread::ResponseThreadProc+0x9b
    0374ffec 00000000 kernel32!BaseThreadStart+0x34
    

    Agora temos um dump da pilha da thread 35. Mas como queremos estudar o que faz cada thread, com esse comando teríamos que ver thread por thread. Agora usaremos o suporte a curingas do comando "~".

    0:027> ~* k
    ...
       1  Id: 890.cd8 Suspend: 1 Teb: 7ffdd000 Unfrozen
    ChildEBP RetAddr  
    00bbfea0 77f4372d SharedUserData!SystemCallStub+0x4
    00bbfea4 77f6c86c ntdll!NtWaitForMultipleObjects+0xc
    00bbff48 77f6d7f5 ntdll!EtwpWaitForMultipleObjectsEx+0xf7
    00bbffb8 77e4a990 ntdll!EtwpEventPump+0x27d
    00bbffec 00000000 kernel32!BaseThreadStart+0x34
    
       2  Id: 890.da0 Suspend: 1 Teb: 7ffdc000 Unfrozen
    ChildEBP RetAddr  
    014dfe20 77f4313f SharedUserData!SystemCallStub+0x4
    014dfe24 77c57b85 ntdll!NtReplyWaitReceivePortEx+0xc
    014dff8c 77c60829 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0x193
    014dff90 77c60771 RPCRT4!RecvLotsaCallsWrapper+0x9
    014dffb0 77c60857 RPCRT4!BaseCachedThreadRoutine+0x9c
    014dffb8 77e4a990 RPCRT4!ThreadStartRoutine+0x17
    014dffec 00000000 kernel32!BaseThreadStart+0x34
    
       3  Id: 890.c88 Suspend: 1 Teb: 7ffdb000 Unfrozen
    ChildEBP RetAddr  
    015dff10 77f4262b SharedUserData!SystemCallStub+0x4
    015dff14 77e418ea ntdll!NtDelayExecution+0xc
    015dff7c 77e416ee kernel32!SleepEx+0x68
    015dff88 77162501 kernel32!Sleep+0xb
    015dff94 771625ea ole32!CROIDTable::WorkerThreadLoop+0x12
    015dff9c 77160000 ole32!CRpcThread::WorkerLoop+0x1e
    015dffac 77162653 ole32!_imp__InstallApplication  (ole32+0x0)
    015dffb8 77e4a990 ole32!CRpcThreadCache::RpcWorkerThreadEntry+0x1f
    015dffec 00000000 kernel32!BaseThreadStart+0x34
    
    ...
    
       5  Id: 890.f38 Suspend: 1 Teb: 7ffd9000 Unfrozen
    ChildEBP RetAddr  
    01a2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01a2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01a2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01a2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01a2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01a2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01a2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       6  Id: 890.f08 Suspend: 1 Teb: 7ffd8000 Unfrozen
    ChildEBP RetAddr  
    01b2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01b2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01b2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01b2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01b2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01b2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01b2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       7  Id: 890.71c Suspend: 1 Teb: 7ffd7000 Unfrozen
    ChildEBP RetAddr  
    01c2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01c2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01c2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01c2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01c2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01c2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01c2ffec 00000000 kernel32!BaseThreadStart+0x34
    
       8  Id: 890.38c Suspend: 1 Teb: 7ffd6000 Unfrozen
    ChildEBP RetAddr  
    01d2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01d2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01d2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01d2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01d2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01d2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01d2ffec 00000000 kernel32!BaseThreadStart+0x34
    
    
    ...
    
      29  Id: 890.c4c Suspend: 1 Teb: 7ff9d000 Unfrozen
    ChildEBP RetAddr  
    0324fe64 77f4372d SharedUserData!SystemCallStub+0x4
    0324fe68 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0324ff10 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0324ff28 00455d59 kernel32!WaitForMultipleObjects+0x17
    0324ffb8 77e4a990 XXX!ReceiverThreadProc+0x49 
    0324ffec 00000000 kernel32!BaseThreadStart+0x34
    
      30  Id: 890.8ac Suspend: 1 Teb: 7ff9c000 Unfrozen
    ChildEBP RetAddr  
    0334fe80 77f4372d SharedUserData!SystemCallStub+0x4
    0334fe84 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0334ff2c 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0334ff44 00455b68 kernel32!WaitForMultipleObjects+0x17
    0334ffb8 77e4a990 XXX!ListenerThreadProc+0x48 
    0334ffec 00000000 kernel32!BaseThreadStart+0x34
    
      31  Id: 890.f44 Suspend: 1 Teb: 7ff9b000 Unfrozen
    ChildEBP RetAddr  
    0344fe78 77f4372d SharedUserData!SystemCallStub+0x4
    0344fe7c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    0344ff24 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    0344ff3c 00455a2c kernel32!WaitForMultipleObjects+0x17
    0344ffb8 77e4a990 XXX!PublisherThreadProc+0x4c 
    0344ffec 00000000 kernel32!BaseThreadStart+0x34
    
    

    Podemos ver que as threads em vermelho são parte de um pool (note que a pilha das threads é a mesma, chamando as mesmas funções). Agora vamos fazer debug dessas threads do pool, colocando um breakpoint bem no retorno do WaitForXXX. Repare que quando fazemos o dump da pilha, temos o endereço de retorno da função:

       8  Id: 890.38c Suspend: 1 Teb: 7ffd6000 Unfrozen
    ChildEBP RetAddr  
    01d2fc38 77f4372d SharedUserData!SystemCallStub+0x4
    01d2fc3c 77e41bfa ntdll!NtWaitForMultipleObjects+0xc
    01d2fce4 77e4b0e4 kernel32!WaitForMultipleObjectsEx+0x11a
    01d2fcfc 0041d3bc kernel32!WaitForMultipleObjects+0x17
    01d2fd78 004171bd XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    01d2ffb8 77e4a990 XXX!CExecutionThread::ExecutionThreadProc+0x1bd 
    01d2ffec 00000000 kernel32!BaseThreadStart+0x34
    

    Esse é o endereço que será executado logo após o retorno da função. Então, se colocarmos um breakpoint nesse endereço, o programa parará logo que a thread retornar do Wait. Vamos colocar o breakpoint e usar o comando "ln" (que mostra o symbol mais próximo de uma posição de memória) para ver como o RetAddr está realmente na função que queremos:

    0:027> ln 0041d3bc
    (0041d330)   XXX!CInterThreadMessageQueue::WaitForMessageEx+0x8c
    
    0:027> bp 0041d3bc
    
    

    Pronto. Agora quando chegar uma solicitação para uma das threads, o programa parará no breakpoint e poderemos ver o que a thread faz.

    Uma última dica: você pode também colocar o breakpoint somente em uma thread, caso não tenha interesse em fazer debug de todas as threads do pool (até porque se o programa estiver recebendo diversas solicitações em várias threads vai ficar difícil fazer o debug). Para isso use o modificador "~" e especifique o ID da thread antes de colocar o breakpoint:

    0:027> ~8 bp 0041d3bc
    0:027> bl
    ...  0:~008 XXX!CInterThreadMessageQueue<>::WaitForMessageEx+0x8c 
    

    Em 03/01/2005 12:36 - Comentários (1)

    Meu Windows Internals já está em casa!

    Fiz um ótimo negócio em encomendar o Windows Internals na Amazon antes do lançamento. A data prevista para o lançamento era de 5 de janeiro nos USA, o livro chegou na minha casa dia 4. A previsão de entrega era 2 de fevereiro... :-)

    Ainda não li o livro inteiro (...), mas tudo me parece ótimo! O livro já começa com a introdução escrita por David Cutler, o arquiteto do kernel do Windows NT. Logo depois, no capítulo 1, temos uma perspectiva histórica dos componentes do Windows (subsistemas, Win32 API, meia página sobre WinFX e .NET, etc) e a apresentação ao leitor das ferramentas necessárias para escarafunchar o sistema: Debugging Tools for Windows (WinDbg e outros), ferramentas Sysinternals e Windows 2003 Resource Kit.

    Certos programadores da equipe do Windows não só escreveram alguns capítulos, como revisaram todo o livro. Além disso, um dos autores (não lembro se o Mark Russinovich ou o David Solomon) teve acesso aos fontes do kernel do Windows, para ajudar a garantir a consistência e a veracidade das informações. Isso só ajuda a garantir que esse livro é definitivamente a bíblia sagrada da arquitetura NT!

    Só um aviso: esse livro não é especificamente sobre programação. Apesar de experiência com programação ser bem vinda durante a leitura, o livro descreve a arquitetura do sistema, não ensina a programá-lo. Assim, caso você seja um administrador de sistemas NT/2000/XP/2003, esse livro lhe será muito útil. Conhecendo a estrutura do sistema, você terá muito mais informações para lidar com problemas, principalmente telas azuis e conflitos diversos entre softwares (já que você saberá como eles interagem entre si).

    Em 06/01/2005 07:57 - Comentários (2)

    Mais uma falha séria no Internet Explorer, com demonstração

    Eu não uso antivírus (eu sei como o scan em tempo real é (mal) feito e sei o quanto acaba com a performance do computador), não uso Adware (que de tão bom diz que um Windows recém instalado tem spyware) nem qualquer tipo de software de segurança. Quando um computador é contaminado com algum tipo de vírus ou malware, eu removo na unha mesmo. Nada que uma busca no Google e Filemon/Regmon/ProcessExplorer/TcpView não resolvam. Apesar de não ser o mais prudente, minha experiência em arquitetura Windows (estou lendo o "Windows Internals", lembra?), um firewall bem configurado e um Windows atualizado têm me mantido longe das pragas virtuais. Eu uso Windows XP Professional (SP2 + últimas correções).

    Lendo o Slashdot hoje, encontrei um link para uma página da Secunia que demostra uma nova falha encontrada no Internet Explorer. Abri meu Internet Explorer (eu uso o Firefox, é claro) e o apontei para página com o teste. Muito me impressionou quando, clicando no link para testar se o micro era vulnerável, um cmd.exe foi aberto.

    Mas dessa vez a Microsoft foi bem rápida. A correção já está disponível no Windows Update. Apesar de meu Windows estar configurado para fazer atualizações automáticas, quando fiz esse teste eu tinha acabado de ligar o computador, e as atualizações ainda não haviam sido baixadas. Dez minutos depois, o Windows me avisou que haviam novas atualizações. Se eu usasse o Internet Explorer e entrasse em uma página que explorasse essa falha, seria tarde demais....

    Em 11/01/2005 19:43 - Comentários (0)

    Procure no Google, versão 0.1 alpha

    Todo mundo tem pelo menos um amigo que faz perguntas estúpidas antes e procura no Google depois. Muitas perguntas que me fazem por e-mail ou MSN seriam respondidas pelo primeiro link encontrado pelo Google. Você também passa por esse tipo de situação? Cansado de perder a concentração quando você está programando? Seus problemas acabaram!

    Além da "manezisse" de perguntar para os outros antes de procurar no Google, os manés ficam tremendamente ofendidos quando você responde com um "procure no Google". Além de se sentir ofendido, o mané age como se isso fosse óbvio (apesar disso, ele não fez o óbvio, se tivesse feito não faria a pergunta). Talvez o mané não sabia interpretar os resultados do Google...

    Foi para resolver esse problema que nós (eu) do 1bit.com.br criamos o "Procure no Google 0.1 alpha". Para não responder com um "Procure no Google", ou mesmo com um link para o Google (o mané pode se sentir ofendido da mesma forma), foi criado um sistema altamente complexo de redirecionamento, que envia o mané para o Google, mas sem mandar um link do Google para ele (já que isso poderia ofender o mané).

    O funcionamento é simples. Aponte seu navegador para www.1bit.com.br/png/png.php, preencha o formulário com a pergunta do mané e clique em "Gerar link". Depois é só repassar o link gerado para o mané. Quando ele clicar no link que você passou, ele será automaticamente redirecionado por nossos servidores para uma página do Google com as respostas que ele precisa.

    Não é só isso! O nosso super "Procure no Google 0.1 alpha" ainda possibilita que você envie seu amigo (que além de mané não sabe inglês) para uma busca do Google somente com páginas em português!

    Vamos à um exemplo de utilização do nosso fantástico "Procure no Google 0.1 alpha". Digamos que seu amigo lhe envie a mensagem "Onde posso encontrar tutoriais de ASP?". Ao invés de ser indelicado e mandá-lo procurar no Google, você acessa o nosso espetacular "Procure no Google 0.1 alpha" e gera um link para a consulta "Tutorial ASP". Depois é só repassar o link gerado para seu amigo:

    http://www.1bit.com.br/png/png.php?solution=7475746f7269616c20415350266c723d6c616e675f707426686c3d70742d4252

    Pronto! Agora seu amigo mané está feliz, e você pode voltar a programar em paz!

    Em 12/01/2005 14:18 - Comentários (2)

    Forçando a especialização de templates em C++

    Estou refazendo o marshaller de um projeto meu, para que ele use menos macros e fique mais orientado a objetos. Para cada tipo de dado suportado pelo marshaler, deve haver uma classe responsável. E todas as classes devem ser derivadas de um template específico.

    (Para quem não sabe o que é um marshaler, vamos lá: o marshaler é o responsável por transportar os parâmetros de uma chamada remota, de um contexto para outro. Quando você usa DCOM e chama um método que está fora do seu processo (rodando em outro EXE ou até em outro computador da rede), o marshaler do DCOM pega os parâmetros da função que você chamou, coloca isso num buffer e manda para o outro contexto. Lá ele lê esse buffer e chama a função do objeto remoto. Para o programador tudo é transparente, já que o DCOM (com uma ajuda das classes de proxy/stub) faz tudo sozinho).

    No início, o código ficou assim:

    template <typename T>
    class CSpTypeMarshaler
    {
    ...
    };
    
    template <>
    class CSpTypeMarshaler<DWORD>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para DWORD");
       }
    };
    
    
    template <>
    class CSpTypeMarshaler<GUID>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para GUID");
       }
    };
    
    //
    // aqui vou declarar os templates
    //
    CSpTypeMarshaler<DWORD> DwordMarshaler;
    CSpTypeMarshaler<GUID> GuidMarshaler;
    CSpTypeMarshaler<char> CharMarshaler;
    

    Até aqui tudo bem, nada que a especialização de templates não faça. Mas eu tenho um problema: é possível instanciar o template com qualquer tipo, MESMO QUE NÃO EXISTA ESPECIALIZAÇÃO PARA ELE. Como cada tipo deve ser tratado de uma forma especial, deve existir uma especialização para cada tipo.

    Uma possível solução é criar um construtor para o template não especializado. Esse construtor deve gerar um erro de compilação, para que não seja possível instanciar sem especialização. Olhe como ficou agora:

    template <typename T>
    class CSpTypeMarshaler
    {
    public:
       CSpTypeMarshaler()
       {
          //
          // isso NÃO FUNCIONA, gera erro sempre
          //
          // #error "Você precisa especializar o template"
    
          //
          // vamos declarar uma array com limite negativo. 
          // Isso gerará um erro de compilação quando o template
          // for instanciado sem especialização
          //
          char por_favor_especialize_esse_template[-1];
    
       }
    ...
    };
    
    template <>
    class CSpTypeMarshaler<DWORD>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para DWORD");
       }
    };
    
    template <>
    class CSpTypeMarshaler<GUID>
    {
    public:
       CSpTypeMarshaler()
       {
          ATLTRACE("especialização para GUID");
       }
    };
    
    //
    // aqui vou declarar os templates
    //
    CSpTypeMarshaler<DWORD> DwordMarshaler;
    CSpTypeMarshaler<GUID> GuidMarshaler;
    CSpTypeMarshaler<char> CharMarshaler;
    

    Agora, quando eu instanciar o template com o tipo char (que não tem especialização disponível), o compilador acusará o erro:

    ConfigManager_ProxyStub.h(48) : error C2118: negative subscript
    ConfigManager_ProxyStub.h(46) : 
    while compiling class-template member function 
    'CSpTypeMarshaler<T>::CSpTypeMarshaler(void)'
        with
        [
            T=char
        ]
    

    Quando o programador desavisado for verificar o motivo do erro, encontrará uma variável por_favor_especialize_esse_template.Quem usa a biblioteca Boost pode usar o BOOST_STATIC_ASSERT ao invés de usar esse método da array com tamanho negativo.

    Em 17/01/2005 22:23 - Comentários (7)

    Com a nota 9.6, agora sou certificado em C# também

    Fui lá eu, sem pretensão alguma, fazer a prova de certificação em C# (70-316). Dei só uma olhada no livro, e fui contando com a experiência e o curso intensivo de .NET que foi trabalhar com o Alfred. Como eu já não estou mais trabalhando com .NET faz 1 mês, achei prudente fazer a prova antes de esquecer o que é um DataSet (eu juro que estou tentando...) :-)

    Tive uma grande surpresa ao final da prova: além de passar na prova, minha nota foi 960 (1000 é o máximo), 9.6 numa escala de 1 a 10. Confesso que fiquei feliz e impressionado. Mas agora é que vem a melhor parte: Ninguém pode falar que eu só "aponto os defeitos" do .NET porque sou um programador C++ que não consegue enxergar além dessas APIs malucas. Com essa nota, eu posso falar mal (leia-se "apontar os defeitos") do .NET com autoridade. :-)

    Achei a dificuldade da prova mediana. É preciso ter bons conhecimentos em ADO.NET, saber usar de forma correta o DataReader, saber exatamente como funciona o DataAdapter (e qual sua interação com o banco de dados e com o DataSet), saber usar eventos como OnRowUpdated, além de RowErrors e coisas assim. Se você usa ADO.NET como usava o ADO, você não passa na prova. Cai também gerenciamento de Assemblies (GAC, COM Interop, etc), deploy (usando XCOPY ou MSI), WinForms (cai coisas de help e assessibilidade, que a maioria das pessoas não usa), e Debug. No meu caso, a experiência com Visual C++ e com a plataforma Windows em geral ajudou bastante, principalmente em debug e deploy.

    PS: Eu não odeio .NET. Só prefiro muito mais trabalhar com C++. Eu já escrevi um artigo sobre minhas opiniões quanto ao .NET.

    Em 18/01/2005 11:17 - Comentários (11)

    GMail e 7Zip: A evolução do software

    Realmente não há limites para o software. Enquanto os programadores e engenheiros de sistemas continuarem tentando criar algo novo, nós sempre iremos nos surpreender. O mais interessante é que a evolução do software só depende de nós, não depende diretamente dos engenheiros de hardware. Na época que o Doom foi lançado, ninguém acreditava que um computador da época conseguiria rodar um jogo com aquela qualidade. Mas John Carmack foi lá, fez um algoritmo de "3D pero no mucho", aproveitando o máximo da capacidade dos computadores. E o mundo dos jogos nunca mais foi o mesmo.

    Dois softwares me surpreenderam bastante nos últimos tempos: o GMail e o 7Zip. Um webmail e um software de compactação. Algo que existe desde que a web é web. Apesar disso, os dois software tem alguns diferenciais que os fazem se destacar no meio das dezenas de opções que temos.

    Vamos ao primeiro: o GMail tem um conceito simplesmente fantástico, com um interface gráfica simplesmente maravilhosa. Tão maravilhosa que desde que eu comecei a usar o GMail eu abandonei o Outlook, acho o GMail muito mais ágil. E apesar de fantástica, a proposta deles é simples: uma boa interface, bastante espaço, um sistema de busca eficiente. Eles não inventaram nada, somente agruparam boas idéias e bons recursos em um conjunto coeso. E esse conjunto coeso fez os usuários exigirem mais qualidade de todo o mercado (de webmail), e elevou o nível de todos os softwares. Todo mundo está vendo a correria do Yahoo e do Hotmail para aumentar o espaço de armazenamento (para míseros 250 MBs) e colocar mais recursos.

    O segundo: 7Zip. Um software de compactação que consegue compactar até 30% mais do que o WinRAR (que já é melhor do que o ZIP). Além do algoritmo melhor, o programador teve a ousadia (que eu não entendo porque não tiveram até hoje) de acreditar que o usuário não se importaria em esperar um pouco mais pela compactação para ter um resultado final bem melhor. Em um dos testes que eu fiz, compactei uma pasta com 42MBs de DLLs e PDBs. Nas suas respectivas configurações de compactação máxima, o WinRAR produziu um arquivo de 6.8 MB, enquanto o 7Zip produziu um com 4.8 MB. Além disso, o 7Zip é free e opensource, e o WinRAR abre os arquivos produzidos por ele (extensão 7zip).

    Essas boas surpresas só me ajudam a acreditar cada vez mais no ramo de desenvolvimento de software. Você não precisa ter uma idéia genial para criar um software. Se você agrupar boas idéias em um software de qualidade, você estará fadado ao sucesso (a não ser que você dê muito ouvidos ao pessoal de marketing antes de terminar o produto). Idéias maravilhosas e inovadoras são difícieis de aparecer. Mas um software de qualidade e que resolva o problemas das pessoas e das empresas é (só um pouco) mais fácil. É só acreditar e ir em frente. Se as empresas que produzem softwares de má qualidade e as consultorias desonestas conseguem ter sucesso, por que um software de qualidade não conseguiria?

    Em 29/01/2005 11:39 - Comentários (8)

    Ode ao C++

    Resolvi a uns dias atrás escrever um post sobre algo como "Por que C++?", e convidei meu amigo (e consultor particular em padrão C++) Wanderley Caloni Jr para ajudar. Não bastasse aceitar prontamente, ele também enviou-me um poético texto que rebatizei de "Ode ao C++":

    /**
    * @title Porque C++
    * @author Wanderley Caloni Jr
    * @date 31.01.2005
    */
    
    É natural que um programador tenha preferência por uma linguagem.
    Geralmente por motivos pessoais que se refletem nas características da
    linguagem. Eu, por exemplo, tenho vários motivos para amar essa
    linguagem:
    
    Linguagem C. Todas as vantagens da linguagem C estão embutidas em C++.
    E sem aquele papo erudito que deve-se programar em OO para ser C++.
    Por ser multiparadigma, a linguagem também suporta o melhor da
    programação procedural e estruturada.
    
    Popularidade. C++ é o que há. Linguagem unânime e reconhecida no mundo
    todo como de uso geral. Dificilmente você vai encontrar um algoritmo
    que não tenha representação em C++.
    
    Economia e Expressividade. Pode parecer bobagem, mas coisas como
    operador de incremento e valor em todas expressões permite que se faça
    muita coisa com poucas linhas. Isso a torna muito expressiva. Isso, em
    outras palavras, quer dizer que você pode juntar várias expressões
    numa só, e esse conjunto será também uma expressão.
    
    Liberdade. Em C++ você é o culpado de virtualmente qualquer coisa de
    bom e ruim que aconteça no seu programa, pois você tem que seguir
    poucas regras e tem que ser responsável no que faz. C++ não te ajuda a
    seguir um bom modelo de programação com restrições embutidas. Isso a
    torna difícil para iniciantes, mas conforme aumenta a experiência,
    maior o prazer em programar.
    
    Portabilidade. A possibilidade de compilar e rodar o seu código em
    vários ambientes - de compilação e execução - é uma característica
    útil e agradável. No meu caso é só agradável, pois dificilmente faço
    código portável, apesar das boas noções que tenho sobre o assunto. E
    são essas boas noções que me permitem afirmar que C++ suporta muito
    bem essa possibilidade.
    
    Rapidez. Pode não ser importante em muitos casos, mas já é do instinto
    do programador o desejo de eficiência no código. E nada como programar
    numa linguagem extremamente eficiente em tempo de execução para se
    sentir feliz de ver o código rodando.
    

    Após ler esse texto, eu irei - emocionado - me dedicar a escrever um texto com a minha versão e minhas opiniões. E claro, colocarei também aquela "água mercadológica no feijão", para ajudar a esclarecer que a pergunta correta não é "Por que C++?" e sim "Por que algo que não C++?"

    Em 02/02/2005 19:09 - Comentários (1)

    Um bom exemplo de comparação de performance entre linguagens/runtimes

    Vou reproduzir aqui um comentário que vi no Slashdot, em um post que falava de uma discussão sobre a segurança da JVM e do .NET, envolvendo o Don Box:

    You're quite right that Java's speed is excellent these days (for non-GUI code, at least). I've spent a lot of time recently working with a large system that was first implemented in Java (by highly skilled developers) and then ported to C++ (by greenhorns). The C++ port is only 50-100% faster, which isn't worth the price in developer time that's been wasted on memory leaks and other forms of memory corruption that were never a factor in Java. Besides that, supporting multiple platforms with the C++ version is the #definition of pain. However, the C++ version uses only about 1/4 or 1/5 as much memory as the Java version, and starts up far more quickly. If a *desktop* application needs to be deployed on older machines, or if the application is so memory-intensive it taxes the limits of today's server hardware, Java still falls flat.

    Grifei as partes que achei mais importantes. Ah, e antes que você vá procurar em um dicionário, eu já fiz - greenhorn quer dizer "iniciante".

    Vamos aos fatos:

    Uma aplicação feita por programadores experientes em Java, quando portada para C++ por programadores novatos e inexperientes ficou (só) 50-100% mais rápida. Ok, eu prometo que vou parar de ficar repetindo que o C++ é muito mais rápido que ambientes gerenciados. Mas eu juro que só faço isso porque existem pessoas (principalmente aqui no Brasil) que ainda teimam em dizer o contrário. Agora imagine uma aplicação portada por programadores experientes...

    Mesmo com o ganho de performance não valeu a pena, já que os memory leaks e erros de memória corrompida nunca aconteceriam em Java. Concordo plenamente. Os ambientes gerenciados foram criados para não deixar que esses erros aconteçam. Quando a performance não é o mais importante, faça em Java/.NET. Um programador inexperiente Java/.NET vai fazer um aplicativo lento que consome muita memória. Um programador inexperiente C/C++ vai fazer um aplicativo que dá GPF por qualquer coisa.

    A versão C++ consome de 1/4 a 1/5 da memória consumida pelo aplicativo Java e inicia muito mais rápido. Esse é um dos problemas dos ambiente gerenciados, o consumo de memória. Experimente usar a extensão SOS (Sons of Strike, para debug da CLR) do WinDbg, e faça um dump de todos os objetos no heap do Garbage Collector. Você vai entender... A grande vantagem é que, apesar do consumo médio de memória ser alto, ele tem um limite no decorrer da vida do aplicativo, já que o GC recolhe todos os objetos. Já em C/C++, se sua aplicação aloca bastante memória e não desaloca, uma hora ela esgota a memória da máquina. Como sempre, em C/C++, você tem que saber o que faz e fazer direito (mais do que em ambientes gerenciados).

    Esse é o tipo de comparação de performance que eu acho válida. Faça um aplicativo, de no mínimo de médio porte, e compare. Comparar performance de "Hello World"s e outras coisas simples não indicam muita coisa útil. Um exemplo: a alocação de memória em ambiente com GC é mais rápido do que em C/C++, já que envolve somente uma soma de ponteiro. A alocação em C/C++ envolve algoritmos de heap, mais complicados e demorados. E isso não faz os ambientes gerenciados mais rápidos.

    Resumindo: cada ferramenta tem seu propósito (eu já disse isso várias vezes, não é?). Se o aplicativo citado fosse feito por programadores C++ experientes, ele talvez seria 300% mais rápido e consumiria 1/10 de memória. Parece óbvio fazer em C++ não? Mas não é. Talvez a empresa gaste menos pagando programadores Java e comprando mais 3 servidores. Nem sempre performance e baixo consumo de memória é o foco. Se fosse, estaríamos usando DOS até hoje...

    Em 07/02/2005 19:19 - Comentários (5)

    Escovação de bits ao vivo e a cores

    Nada como terminar uma quarta feira com um bom chopp Brahma e discutindo tecnologias, metodologias e outras "ias" mais. Fazendo valer nossa última frase antes de irmos embora ("isso merece um post!"), deixo aqui registrado o encontro "escova bit" que participei hoje: Alfred Myers, Fabio Galuppo, e a "minha pessoa".

    Depois de discutirmos de C++ à PDCs, e de Datasets à gerenciamento de projetos, voltei para casa com a certeza de que encontros para discutir desenvolvimento de software - regados a um bom chopp - fazem muito bem para saúde e para o cérebro. Esses encontros além de divertidos são muito bons para trocar experiências. Pena que hoje em dia é difícil encontrar pessoas com nível técnico, bagagem e boa vontade suficiente para isso...

    Em 09/02/2005 21:48 - Comentários (1)

    Google: a Microsoft da web

    A grande novidade no mundo do software é o Google. Pode não parecer tão óbvio para as pessoas, já que o software deles não roda diretamente nos computadores dos usuários. Quando você faz uma consulta no Google, um cluster de aproximadamente 100.000 PCs (isso mesmo, cem mil) é utilizado para procurar em bilhões de páginas, tudo isso em questão de milesegundos. E com milhões de usuários fazendo isso ao mesmo tempo.

    Apesar de todo esse sucesso com seu carro chefe, o Google está deixando de ser "a empresa do mecanismo de buscas" para ser uma empresa de aplicações web. GMail, Orkut, Google Maps, Google Suggest, Google Desktop Search... E essa lista cresce a cada dia que passa.

    E como eles estão fazendo isso? Conheço milhares de empresas que desenvolvem sistemas para web e continuam só desenvolvendo sistemas para Web... O segredo deles é que eles desenvolveram mais que uma aplicação de buscas, eles desenvolveram uma plataforma para desenvolvimento de software, um software para viabilizar mais softwares. Uma plataforma de computação em cluster especializada em rodar aplicações web. E agora eles estão usando essa plataforma para desenvolver softwares que têm as mesma necessidades do mecanismo de busca: atender milhões de usuários, ter petabytes de espaço para armazenamento e um tempo de resposta mínimo.

    Essa plataforma do Google envolve gerenciamento do cluster, um FileSystem distribuído (o Google File System) que roda nesse cluster, entre outros. E o melhor é que as aplicações que são desenvolvidas nessa plataforma podem aproveitar sem muito esforço qualquer melhoria que nela for feita. Eles podem focar os profissionais mais competentes na melhoria dessa plataforma, e fazer pequenos ajustes nos softwares que rodam sobre ela. Quando alguém da equipe do Google tiver a idéia para um software web, é só desenvolver em cima dessa plataforma e pronto: milhões de usuários podem usar. Isso agiliza MUITO o desenvolvimento e permite que um novo produto seja lançado no mercado em muito menos tempo. Isso deixa a concorrência maluca, porque eles não conseguem acompanhar o ritmo e acabam fazendo alguma besteira no meio do caminho. A Microsoft usou bastante essa estratégia e derrubou líderes de mercado como o WordPerfect. É bom lembrar que a própria Microsoft começou fazendo uma plataforma para desenvolvimento de software: os compiladores de linguagem BASIC.

    A Microsoft construiu uma base sólida para desenvolver seu aplicativos: seus sistemas operacionais. (ok, a base só é sólida a partir do Windows NT, o Windows 9x é pior que gelatina). Tudo que a Microsoft desenvolve é em cima dessa plataforma. Cada dia que passa ela agrega valor a essa plataforma desenvolvendo mais softwares para ela. E cada dia que passa ela melhora essa plataforma para poder construir software melhores sobre ela. O Google segue a mesma linha e tem tanta competência na sua plataforma quanto a Microsoft tem na dela.

    Essa é uma ótima estratégia: gerar receita agregando valor a uma plataforma base muito bem construída. Se seu software exporta uma API, e você mesmo a usa para desenvolver alguma coisa, é um ótimo sinal. Você está usando o seu software para construir software. Por que você acha que as equipe da Microsoft mais respeitadas são as do Windows e a do Visual C++?

    (Procurando sobre o Google File System, encontrei um post interessante sobre esse assunto, em um blog escrito por alguém que tem um pensamento parecido sobre o Google. Vale a leitura. E antes que você fale que o Orkut é uma porcaria, não se esqueça que ele é feito em ASP.NET e não roda nessa plataforma do Google, que é baseada em Linux)

    Em 11/02/2005 20:05 - Comentários (3)

    10 anos de Delphi (programadores WinForms, comemorem!)

    O Delphi - ferramenta da Borland baseada em Pascal que nasceu para desbancar o VB - fez dez anos de vida no dia 14 de fevereiro. A Borland tentou com o Delphi (e talvez tenha conseguido) unir o poder do C++ e a facilidade do VB.

    A Borland colocou no site uma entrevista com Anders Hjelsberg, Gary Whizin, e Zack Urlocker, como parte da comemoração do aniversário. Além da Borland, os programadores WinForms também tem que comemorar, afinal, a VCL do Delphi e seu recursos (como herança visual) foram as grandes inspirações para boa parte do que está contido em System.Windows.Forms. E parabéns ao Delphi!

    Em 16/02/2005 18:14 - Comentários (0)

    Olhe só quem está no orkut...

    http://www.orkut.com/Profile.aspx?uid=7881279797765706622

    E olhe só a lista de amiguinhos dele... Achei isso navegando entre as comunidades de programação e chegando à comunidade Windows Technical: Off Topic, da (nem tanto) famosa lista de discussão homônima.

    Em 17/02/2005 23:04 - Comentários (0)

    Como fazer drivers para Windows NT/2000/XP/2003, parte 2

    Parte 2 = continuação da parte 1 :-)

    Já leu os livros recomendados na parte 1? Eu sei que leu! Então vamos ao que mais precisamos para fazer um driver.

    Driver Development Kit (DDK)

    O DDK é o SDK para fazer drivers, ele contém os headers C e todas as ferramentas necessárias para todas as etapas do desenvolvimento de um driver. Quando eu digo "todas as ferramentas necessárias" é verdade, até o compilador é incluído no DDK. É possível compilar drivers usando o VC++ 6 ou superior, mas é altamente não recomendado. Essa configuração não é suportada pela Microsoft, e o pessoal do newsgroup de drivers da Microsoft geralmente se recusa a responder perguntas relativas a compilação quando o "perguntador" usa essa configuração.

    Para obter o DDK você tem duas opções. A primeira é ter uma assinatura MSDN. Dessa forma é só entrar no "Subscriber Downloads" e baixá-lo. Para quem não tem uma assinatura MSDN, a opção é entrar no site do Windows DDK e pagar US$ 25,00 para que a Microsoft envie o DDK pelo correio.

    O DDK também vem com vários samples para várias arquiteturas de drivers (USB, SCSI, NDIS, etc) e a documentação (que também pode ser encontrada na MSDN online). A maioria dos samples são muito bons e muito bem documentados. Além disso, em boa parte das arquiteturas é recomendável que você comece a fazer seu driver a partir de um sample (ao invés de começar do zero). Drivers WDM, por exemplo, devem gerenciar Plug'n'Play, e só essa parte do código tem umas 2000 linhas...

    Existe somente uma categoria de drivers que não pode ser feita com o DDK: os drivers relacionados com File System (File Systems e filtros). Para esse tipo de driver é necessário comprar o IFS Kit (Installable File System Kit), que custa a bagatela de US$ 899,00 (um amigo meu costumava dizer que era o arquivo .h mais caro que ele conhecia. Até ele perguntar para o pessoal da OSR o preço do kit de File System deles...). Os antívirus com scan em tempo real, por exemplo, são implementados como filtros de File System.

    É bom lembrar que, apesar da palavra driver ser quase sempre usada para denominar um software que controla um hardware (driver da placa de som, por exemplo), no Windows é comum chamar de driver todo software que roda em kernel mode, mesmo que ele não controle hardware. Firewalls e filtros de file system são exemplos de drivers que não controlam hardware. Mesmo assim, no Windows existem alguns drivers que rodam em user mode.

    Editor de código C/C++

    Apesar de ser possível escrever um driver usando o Bloco de Notas, é recomendável que você use um editor apropriado para isso. Eu costumo usar o Visual Studio.NET, usando o "MakeFile Project" para compilar o driver usando o compilador do DDK e não o Visual C++. Você pode usar o editor de sua preferência.

    Em 18/02/2005 18:39 - Comentários (5)

    Por que o meu Visual C++ está fazendo REBUILD toda hora?

    Eu tenho um Solution do Visual C++ 7.1 que tem 12 projetos, entre COM/ATL e WTL, com algumas dependências entre eles. Entre essas dependências existe um header "spmain.h" que é incluído no "stdafx.h" de todos os projetos. Ontem eu adicionei algumas declarações nesse header, já consciente que todos os meus projetos seriam recompilados. Dois minutos depois tenho todos os projetos recompilados, e começo a escrever código em modo automático, até uma hora que eu não me lembro (só lembro que passava da meia noite). Depois desliguei o modo berserker e fui dormir.

    Quando fui mexer no projeto hoje de novo (sim, eu programo as domingos), percebi que o Visual C++ fazia rebuild dos projetos toda vez que eu compilava. Se fosse um projeto pequeno tudo bem, mas 30 mil linhas de C++ em 12 projetos não compila lá muito rápido...

    Procurando informações sobre esse problema, encontrei um artigo do KB da Microsoft para NMAKE e Visual C++ 5.0 que falava alguma coisa sobre problemas com arquivos na rede e com a data da instalação do Visual C++. Como meus arquivos não ficam em rede, só me restava verificar o problema da data.

    Tiro e queda: verificando a data dos arquivos, percebi que o "spmain.h" estava com data do dia 21/02/2005 (detalhe: hoje ainda é dia 20/02). Abri o arquivo, adicionei um CRLF e fechei. Problema resolvido! Se você tiver um problema parecido (eu já passei por isso antes), já sabe como resolver.

    Outro fato interessante: um projeto C# que eu estava mexendo ontem também estava com o mesmo problema, todos os arquivos estavam com data do dia 21/02. Mas como o build do C# é MUITO mais rápido que o build C++, eu acabei nem me dando conta...

    Em 20/02/2005 19:44 - Comentários (6)

    Mais um benchmark interessante

    A Microsoft apresenta no site do Visual C++ um benchmark interessante. A proposta é verificar a performance de um backend para o mercado financeiro (uma bolsa asiática), baseado em Visual C++, MSMQ e SQL Server. Lá eles descrevem de forma acadêmica diversos fatores que foram levados em conta, desde configuração de discos, até servidores SQL federados com chaves em hash para diminuir a concorrência e o gargalo do banco.

    O artigo apresenta boas dicas sobre performance para aplicações críticas e sobre metodologias para medição disso. Performance é algo complicado de medir, porque além de ser um conceito relativo, existem diversos fatores obscuros que podem distorcer um estudo - desde um design de uso de memória que aproveite o fator localidade para facilitar o trabalho do cache do processador, até a configuração do arquivo de paginação do Windows. Como Raymond Chen já explicou, otimização de performance não é algo tão óbvio quanto parece.

    No final do artigo, o autor diz que a equipe reimplementou o aplicativo em C# para fazer uma comparação de performance de processamento de mensagens entre código gerenciado e não gerenciado. Esse é um dos primeiro benchmarks que eu vejo que serve de alguma coisa. Eu já escrevi aqui algumas dezenas de vezes que comparar duas linguagens testando um algortitmo simples em loop não leva a conclusão nenhuma. O que funciona é isso, pegar uma aplicação crítica e implementar em duas linguagens. Primeiro o autor apresenta o seguinte gráfico de comparação (maior é melhor):

    E depois faz umas considerações muito interessantes sobre como essa diferença absurda entre as duas linguagens deve ser interpretada. Ele cita um fato interessante que eu já citei aqui: de que a alocação de memória em ambientes gerenciados é eficiente e tão rápido quanto C++ (o que compensa um pouco a desalocação mais lenta feita pelo GC). Além disso eles explicam que, como eu também já disse, a medida que a tecnologia da VM .NET for evoluindo a implementação em C# se tornará mais rápida e eficiente. Só que, como eu também já disse, eu vivo no presente e não no futuro. :-)

    Em 22/02/2005 07:59 - Comentários (0)

    Aulas de C++: como ensinar?

    Estou estudando uma proposta para dar aulas de C++, e estou pensando em uma didática interessante para as aulas. Tive uma conversa com o Wanderly Caloni sobre isso e quero deixar aqui registrado. Caso alguém tenha uma sugestão ou idéia sobre o assunto, coloque nos comentários desse post. Minha idéias ficaram bem claras nesse chat, o que dispensa maiores explicações. Vamos ao chat:

    Today, 09:42 Rodrigo Strauss:
    como assim aulas teóricas?

    Today, 09:43 Wanderley:
    vc falando e uma lousa

    Today, 09:44 Rodrigo Strauss:
    impossível ensinar programação assim.

    Today, 09:44 Rodrigo Strauss:
    Vai ser eu e um projetor, um aluno por micro

    Today, 09:44 Wanderley:
    mmmmmm...

    Today, 09:44 Wanderley:
    dai facilita MUITO

    Today, 09:44 Wanderley:
    ainda mais q o instrutor vai saber tirar duvidas eheheehheheh

    Today, 09:44 Rodrigo Strauss:
    sim, e vai ser assim

    Today, 09:44 Rodrigo Strauss:
    to pensando em adotar uma estratégia diferente

    Today, 09:45 Rodrigo Strauss:
    li uma coisa no comp.lang.c++.mod e fiquei pensativo

    Today, 09:45 Rodrigo Strauss:
    todo mundo q ensina C++ começa pela parte difícil

    Today, 09:45 Rodrigo Strauss:
    ponteiros, strcpy, etc

    Today, 09:45 Rodrigo Strauss:
    estou pensando em já começar usando std::string

    Today, 09:45 Rodrigo Strauss:
    e passando tudo por valor

    Today, 09:48 Wanderley:
    dai vai ser vb! =P

    Today, 09:51 Rodrigo Strauss:
    e qual o problema? Não é mais lógico ensinar primeiro a parte fácil e depois a difícil?

    Today, 09:59 Wanderley:
    em ambas as formas existem vantagens e desvantagens. no caso de comecar pelo mais facil, a desvantagem eh esconder desde o comeco detalhes essenciais para programar em C

    Today, 10:00 Rodrigo Strauss:
    não é esconder, é só começar pelo mais fácil. E eu vou dar aulas de C++, vou deixar claro pra eles que não é aula de C

    Today, 10:02 Wanderley:
    separar C de C++? mmmmmmmmmm...
    =P

    Today, 10:03 Rodrigo Strauss:
    vou explicar as diferenças e similaridades e dizer que linguagem C é mais específica

    Today, 10:04 Wanderley:
    seilah. penso q sao coisas indivisiveis pq uma eh parte do todo

    Today, 10:04 Rodrigo Strauss:
    sim... mas como vc mesmo já disse, nós não sabemos programar em C. O C tem várias limitações e diferenças q não estamos acostumados

    Today, 10:06 Rodrigo Strauss:
    essas aulas vão ser muito boas pra mim, pq vão me fazer estudar mais coisas e mais termos. Se vc me perguntar o q é polimorfismo eu não sei explicar

    Today, 10:06 Rodrigo Strauss:
    vou ter q comprar o livro do Stroustrup urgente

    Today, 10:06 Wanderley:
    geralmente a pratica acaba sendo mais fixada na mente de quem realmente programa =P

    Today, 10:07 Rodrigo Strauss:
    EU CONSEGUI! Escrevi Stroustrup certo sem consultar!
    Stroustrup, Stroustrup, Stroustrup!

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:
    Bjarne Stroustrup

    Today, 10:07 Rodrigo Strauss:

    Today, 10:09 Rodrigo Strauss:
    o seu Stroustrup é em portuga, né?

    Today, 10:16 Wanderley:
    eiuaheiuahieuaeihauhiauehiaueaiehiahuei
    eh, sim
    ta afim?

    Today, 10:16 Rodrigo Strauss:
    mais um clique na amazon e eu compro o meu

    Today, 10:17 Rodrigo Strauss:
    e to pensando em comprar esse tb:

    Today, 10:17 Rodrigo Strauss:
    http://www.amazon.com/exec/obidos/tg/detail/-/0201749629

    Today, 10:21 Wanderley:
    q baum!! =D

    Today, 10:22 Rodrigo Strauss:
    to quase comprando... mas eu tenho gasto uma puta grana com livros ultimamente

    Today, 10:22 Rodrigo Strauss:
    é sempre um bom gasto, mas é um gasto. E o bom é que livros de C++ nunca ficam desatualizados

    Today, 10:27 Wanderley:
    =D

    Today, 10:28 Rodrigo Strauss:
    vc já leu o blog do thiago adams?

    Today, 10:29 Wanderley:
    naum. qual o link?

    Today, 10:33 Rodrigo Strauss:
    http://planeta.terra.com.br/informatica/thiago_adams/codigo.htm

    Today, 10:33 Rodrigo Strauss:
    pena q não tem RSS

    Today, 14:13 Wanderley:
    onde vc viu a msg sobre ensinar c++ comecando pelo mais facil?

    Today, 14:14 Rodrigo Strauss:
    eu li uma mensagem no comp.lang.c++.moderated sobre um cara dizendo q o problema é q em C++ as pessoas começar pelo contrário, pelas coisas mais difíceis

    Today, 14:14 Rodrigo Strauss:
    vc não acha q ponteiro é uma ferramenta muito poderosa pra dar nas primeiras aulas?

    Today, 14:15 Wanderley:
    acho

    Today, 14:15 Wanderley:
    na primeira aula se dá variaveis e construções simples de programação

    Today, 14:15 Rodrigo Strauss:
    não é melhor que o cara que está começando use std::string ao invés de fazer um buffer overflow esperando pra acontecer?

    Today, 14:16 Wanderley:
    nao enquanto ele nao entender o funcionamento e os riscos de um buffer overflow

    Today, 14:17 Rodrigo Strauss:
    sim, mas acho q isso pode ser ensinado depois

    Today, 14:17 Wanderley:
    mas a lista do c++.moderated eh enorme
    vc nao tem o link?

    Today, 14:17 Rodrigo Strauss:
    a maioria usa char* e não sabe o q é um buffer overflow

    Today, 14:18 Rodrigo Strauss:
    eu li ontem num post sobre c#, mas não lembro onde

    Today, 14:18 Rodrigo Strauss:
    esse cara não vai saber o q é um buffer overflow, mas pelo menos ele vai correr menos riscos

    Today, 14:18 Wanderley:
    a maioria faz isso pq nao aprendeu todos os conceitos. soh os necessarios pra sair programando...

    Today, 14:19 Wanderley:
    por isso q eu disse q eh vblizar o aprendizado: evitando riscos escondendo perigos

    Today, 14:20 Rodrigo Strauss:
    eu acho q é jogar o cara no fosso com leões só depois que ele conseguir matar um cachorro.

    Today, 14:21 Wanderley:
    qualquer abordagem eh valida qdo se trata de ensinar C ou C++ (ja li isso em algum livro eheheeh). mas existem vantagens e desvantagens em todas. eh vivendo, programando e aprendendo

    Today, 14:22 Rodrigo Strauss:
    minha experiência tem dito que fazer coisa low-level a toda hora por qualquer coisa prejudica o desenvolvimento. "Humble thyself, reuse".

    Today, 14:23 Rodrigo Strauss:
    se eu usar std::string não vou perder tempo com corrupção de memória. E SE FICAR LENTO, eu otimizo

    Today, 14:24 Wanderley:
    eu entendi. funciona. mas vc nao pode dizer q uma pessoa sabe c++ se ela nao consegue explicar o que eh uma string em C

    Today, 14:39 Rodrigo Strauss:
    claro, isso eu concordo

    Today, 14:39 Rodrigo Strauss:
    meu ponto é só que ela deve começar pelo mais fácil

    Today, 14:47 Wanderley:
    isso eh questao de gosto

    Today, 14:47 Wanderley:
    de objetivo tb

    Today, 14:47 Wanderley:
    ja q eles vao mexer com mfc, eh bom q se acostumem com a ideia de classe string, por ex

    Today, 14:47 Rodrigo Strauss:
    eu acho q é questão de didática e motivação

    Today, 14:47 Rodrigo Strauss:
    eu acho C difícil, imagina pra quem não sabe programar

    Today, 14:48 Rodrigo Strauss:
    é muito mais fácil alguém q nunca programou entender um
    cout << "numero " << 5 << endl;
    do que
    printf("numero %d\r\n",5);

    Today, 14:49 Wanderley:
    o Ritchie ensina muito bem as nuances da linguagem em seu livro. eh claro q a pessoa ja tem q ter um minimo de conceitos

    Today, 14:49 Wanderley:
    muito mais facil entender intuitivamente. mas nao tecnicamente

    Today, 14:50 Rodrigo Strauss:
    as pessoas tem uma visão distorcida de C++, acham q é muito complicado. Se usar STL não é muito complicado.

    Today, 14:51 Wanderley:
    nao se nao der erro

    Today, 14:52 Rodrigo Strauss:
    sim... e algumas coisas no STL tb são phoda. como tempo de vida de um interator

    Today, 14:54 Wanderley:
    mas, claro, se vc usa religiosamente como manda o figurino, tudo funciona

    Today, 14:56 Rodrigo Strauss:
    se vc usar char* como manda o figurino nunca dá buffer overflow

    Today, 14:59 Wanderley:
    aih q entra o grau de dificildade =P

    Today, 15:00 Rodrigo Strauss:
    sim. como é mais fácil usar o std::string sem fazer besteira me parece mais óbvio ensinar isso primeiro

    Today, 15:00 Rodrigo Strauss:
    do mesmo jeito que eu recomendo VB para iniciantes por ser mais fácil

    Today, 15:00 Wanderley:
    pior q nao eh. depende do gosto e/ou objetivo
    eu recomendo basicao

    Today, 15:01 Rodrigo Strauss:
    eu vou pensar bastante nisso. Quero criar uma geração que não use C só por costume (como a gente faz), e use realmente C++ e STL sempre (como eu quero fazer daqui pra frente)

    Today, 15:02 Wanderley:
    ohhhhhhhhhh
    nisso eu apoio. leia o livro do Stroustrup. ele eh um categorico dessa ideia

    Today, 15:04 Rodrigo Strauss:
    sim... no site dele ele diz que um livro de C++ que começa com printf já começa mal...

    Today, 15:05 Wanderley:
    ehehehehhehehehehhe. o legal eh q ele eh categorico, mas nao eh xiita, q fala q C++ deve ser usado soh com orientacao a objetos. eh o criador, neh. dai eh outra coisa

    Today, 15:09 Rodrigo Strauss:
    O Criador...

    Today, 15:09 Wanderley:
    ehehehehhehehehehheh

    Today, 15:15 Rodrigo Strauss:
    comprei o Stroustrup!

    Today, 15:15 Wanderley:
    =D

    Today, 15:15 Rodrigo Strauss:
    Esse chat merece um post.
    Autoriza?

    Today, 15:15 Wanderley:
    agora vc ouvirah a palavra tb. e logo estara pregando a palavra =P

    Today, 15:16 Wanderley:
    se vc acha q merece, vai la

    Today, 15:16 Rodrigo Strauss:
    A palavra do Criador, sem apóstulos, sem intermediários. É como se "God himself" tivesse escrito a Bíblia

    Today, 15:17 Wanderley:
    eiuhaieuhaiuehiauheiauhieua eh vero. sem interpretacoes erroneas. eh o proprio!

    Em 23/02/2005 14:36 - Comentários (3)

    MandrakeSoft compra a Conectiva

    Direto do Slashdot: A européia Mandrakesoft, responsável pelo famoso "Mandrake Linux", comprou a brasileira Conectiva.

    Esse é sem dúvida um evento muito importante para o mercado de software brasileiro. E com tamanho interesse do governo brasileiro pelo Linux, era mesmo de se esperar que alguma empresa estrangeira do setor arrumasse uma boa forma de entrar no nosso mercado.

    Em 24/02/2005 13:41 - Comentários (0)

    Otimização e Pessimização

    Eu li em algum lugar que "otimização precoce é pior do que nenhuma otimização", e a cada dia que passa eu tenho visto que isso é realmente verdade. Qual o motivo de otimizar um programa que você nem terminou? Na verdade você não sabe exatamente o que vai trazer problemas de desempenho. Então, na realidade, você não está fazendo uma otimização, e sim uma pessimização. Existem alguns conceitos básicos de desempenho que devemos seguir, mas não é justificável levarmos duas vezes mais tempo para fazer algo porque queremos bom desempenho. Faça primeiro, do modo mais rápido e correto possível, com o código simples e claro, e deixe para otimizar somente a parte problemática, se existir uma. Não tente resolver um problema que ainda não existe.

    É muito comum programadores C++ terem preocupações excessivas com otimizações precoces e poucas preocupações em fazer um código seguro, que funcione, e feito em tempo hábil. Por isso que temos tantos buffer overflows e coisas assim. Não seria melhor usarmos std::string para tudo e parar de nos importar se ela faz uma (ou duas) cópias da string no retorno? Nós vivemos no mundo das VMs, do .NET e das linguagens script. Um código C++ que copia uma string desnecessariamente ainda será dezenas de vezes mais rápido do que tudo que existe por aí. E você ainda poderá otimizar se quiser (eles não).

    Faça um programa que funcione, de uma maneira simples e com um código claro. Depois que tudo estiver funcionando, otimize o que ficou mais lento. Se você faz uma otimização precoce (usando ponteiros e memcpy irresponsavelmente, por exemplo), existe uma possibilidade de, além de não ficar tão mais rápido, você criar um buffer overflow esperando para acontecer. Se você escrever um código simples e PSICOLOGICAMENTE lento, você pode fazer a otimização depois, e testar especificamente aquele memcpy para se certificar que nada de errado vai acontecer. Com a otimização precoce, o seu memcpy suicida será testado junto com o resto do software, e não receberá a devida atenção. Mas o teste que será feito depois da otimização será específico para esse memcpy, e é muito mais provável que os erros sejam encontrados.

    Hoje a velocidade os processadores é da ordem de gigahertz, podemos nos dar ao luxo de nos preocupar em otimizar as partes interessantes e fazer de forma simples e clara as partes menos interessantes. Mas não esqueça: já que não vai otimizar e está disposto a queimar alguns nanosegundos a mais, pelo menos faça o código de forma clara. Se seu medo é que o próximo programador a mexer no código fale que você faz código lento, coloque um comentário explicando que você tem mais o que fazer do que otimizar uma função que lê do registro e só é chamada uma vez...

    PS: Sim, eu sou um programador C++.

    Em 25/02/2005 23:43 - Comentários (7)

    Testando o ASP.NET 2.0

    Essa é para os interessados em teste de software (ou para os que tem inveja de equipes que tem infraestrutura de testes). Scott Watermasysk, um membro da equipe do ASP.NET, fez um post explicando com um bom nível de detalhamento como são feitos os testes do ASP.NET 2.0. Lá ele descreve o software para gerenciamento de testes que eles desenvolveram (que inveja...) e o software para controle de bugs. O software de testes gerencia os casos e cenários a serem testados e o ambiente de teste, incluindo instalação automática de software para montagem dos ambientes.

    Esse post me fez lembrar uma coisa: quando eu fiz entrevista para trabalhar na Microsoft US (um dia eu conto essa história aqui), eu descobri que a Microsoft é uma das poucas empresas que tem desenvolvedores especializados em testes (eles chamam de Software Design Engineer in Test). São desenvolvedores 100% dedicados a fazer softwares para testar softwares, e para produzir maravilhas como essa que testa o ASP.NET 2.0.

    Além desse post, existem alguns vídeos da Sara Ford no Channel9 Developers Division.

    Em 01/03/2005 19:11 - Comentários (0)

    Cante comigo

    Imagine (with apologies to John Lennon)

    Imagine there's no requirements. It's easy if you try
    Just a bunch of coders, reachin' for the sky
    Imagine all the people, coding for today
    Imagine there's no schedules. It isn't hard to do
    No silly project deadlines, no one supervising you
    Imagine all the people, coding hand in hand

    You may say I'm an extremer but I'm not the only one
    I hope someday you'll join us and make coding lots more fun.

    Imagine oral documentation. I wonder if you can
    No need for UML diagrams. Just words passed, man to man
    Imagine just refactoring, playing in the sand

    You may say I'm an extremer, but I'm not the only one
    I hope someday you'll join us and make coding lots more fun.

    Mais uma paródia de Imagine, retirada do artigo da Wikipedia sobre Agile Software Development. Mande isso para aquele seu gerente dilbertiano que acha que RUP é a oitava maravilha do universo. Você pode dar sorte e ele ter um enfarte. :-)

    Em 03/03/2005 09:01 - Comentários (0)

    Mark Lucovsky vai para o Google

    Pode parecer mais um grande engenheiro de software sendo contratado pelo Google, mas para mim significa o início de uma nova era. Para quem não sabe, Lucovsky é um dos engenheiros originais do Windows NT, que veio da DEC junto com Dave Cutler para desenvolver o novo sistema operacional da Microsoft. Naquela época (1989) o projeto era fazer um kernel modular (e não um micro kernel como alguns dizem) que rodasse em um protótipo de processador da Intel (o N10) e suportasse basicamente a API do Presentation Manager do IBM OS/2. Depois de um tempo, esse novo sistema operacional (já batizado de NT, sem o "Windows" antes) foi portado para plataforma Intel e Alpha. Além disso, a API do Presentation Manager foi substituída pela API Win32, que foi criada para aproveitar o boom do Windows 3.0.

    Muitos dos componentes do kernel do Windows NT/2000/XP/2003 foram feitos por Lucovsky. Na verdade toda vez que você executa um programa, você está rodando um código feito por ele (ele foi responsável pela parte do kernel que gerencia processos e threads). Ele foi responsável também pelo gerenciamento do controle de versão desses projetos gigantescos (como ele explica nessa apresentação). Mais algum dúvida sobre a importância dele para Microsoft?

    Vamos agora à explicação do porquê eu acho que estamos no início (sim, no início) de uma nova era. Lucovsky escreveu um post mês passado sobre como a Microsoft não é mais a melhor em fazer, entregar e distribuir software. E a visão dele é muito interessante: quando alguém na Amazon (ou no Google) faz uma modificação no software deles, milhões de pessoas têm acesso a essa modificação na mesma hora. Quando a Microsoft faz uma modificação, esse modificação precisa de um download, muitas vezes da instalação de uma runtime (.NET), precisa que o usuário aceite um contrato, faça ativação, etc.

    Lembra quando eu falei sobre a "Plataforma Google"? O Google mostra cada vez mais que está investindo nessa plataforma e ela é realmente o core da empresa. O mecanismo de buscas é, na realidade, o software mais importante que roda sobre essa plataforma.

    As contratações do Google são todas na direção do que a empresa já faz, e não na criação de algo revolucionariamente novo. Contratando especialistas em sistemas operacionais (como Lucovsky e Pike) não significa que eles irão lançar um sistema operacional. Contratando especialistas em browsers (como Goodger, do Firefox) não significa que eles NECESSARIAMENTE lançarão um browser (Só mesmo o bobinho e inocente John "idéias não-tão-geniais" Dvorak para pensar assim). Essas são as plataformas que eles usam (o cluster Linux e Ajax), quanto mais eles entenderem delas mais sucesso eles terão. Se uma fábrica de automóveis contrata especialistas em aço, não significa que ela necessariamente vai entrar no ramo de siderurgia...

    Em 04/03/2005 10:16 - Comentários (4)

    Lista de libs C++, GCC-XML e divagações sobre computação distribuída

    Encontrei duas coisas interessantes sobre C++ hoje. A primeira foi uma página com uma lista de libs C++ disponíveis. Esse lista contém links para dezenas de libs, desde as mais conhecidas (como a ACE) até libs para sequenciamento biomolecular (!?). Vale a pena perder um tempinho navegando na lista.

    A segunda é o GCC-XML, uma versão (talvez "versão" não seja o termo correto) do GCC que ao invés de compilar o programa, gera um XML com a estrutura do programa. Estou desenvolvendo um framework para computação distribuída nas horas vagas, e nas últimas semanas "criei" um formato de IDL em XML. Basicamente você cria uma arquivo XML com as definições das interfaces e seus métodos, e um programinha (feito em C#) gera o código C++ para proxy/stub. Eu podia usar DCOM, mas minha infra-estrutura precisa ser flexível e extensível (e multiplataforma se possível). Isso envolve comunicação de diversas formas (TCP/IP, MSMQ, MQSeries, FileMapping, ISAPI, etc) e não ter problemas com firewall. Pode parecer que eu estou refazendo a roda (refazendo o DCOM mais precisamente), mas acho que esse framework se parecerá mais com o Indigo.

    Voltando à IDL em XML: eu cheguei a pensar em usar YACC/LEX para gerar um parser para MS-IDL, mas achei que seria muito trabalhoso. No final das contas, cheguei à conclusão que um formato XML seria bem mais interessante, pois me permitiria colocar informações extras na IDL/XML (como comentários e links para documentação) sem alterar a estrutura do programa gerador - que nada mais faz do que carregar um XmlDocument e usar XPath para encontrar as interfaces e métodos. Além disso, seria mais fácil manter compatibilidade com os geradores antigos, já que usando XPath eu automaticamente ignoro qualquer propriedade ou entidade que eu não conheça.

    Usando o GCC-XML eu posso até gerar o XML diretamente de uma classe C++, e "passá-lo" por um XSLT para gerar um XML no formato do meu gerador. Com isso eu poderia colocar os metadados das classes (inclusive sobre variáveis privadas) dentro do programa, e ter informações precisas em run-time, exatamente como em managed code. Seria possível até fazer um marshal-by-value automático, como acontece com as classes [Serializable] em .NET. Isso funcionaria como um TypeLibrary COM vitaminado, com informações extensas sobre as classes e dados que elas encapsulam.

    Eu poderia também fazer um Attribute Provider para o Visual C++ 7.1. Eu também poderia fazer um compilador e criar uma runtime. Ah, e eu poderia ... mmmm ... esquece... Eu sou um programador solitário, e essas divagações - apesar de interessantes - levam bastante tempo para implementar...

    Em 07/03/2005 16:53 - Comentários (3)

    Artigo sobre WinDbg parte 3: a missão

    Já está no ar a terceira parte da minha série de artigos sobre o WinDbg. Nessa parte eu explico o que são os symbols, para que servem, e porque eles facilitam tanto as nossas vidas.

    Em 13/03/2005 23:23 - Comentários (0)

    Algumas coisas deveriam funcionar, mas não funcionam

    O trecho a seguir foi retirado do documento "MSMQ Best Practices":

    Asynchronous notifications using WithEvents in Visual Basic can be a powerful feature. The idea of running code only in response to an event is quite attractive. However, note the following:

    • Events can get lost, and you should periodically reenable notification.
    • Multiple clients will be notified in the event of a single message. This problem is common; the application ceases to respond to user input. To fix this, ensure that all subsequent receives have timeouts set for the MQReceiveMessage function.

    Note como o documento é bem específico. Eu traduziria esse trecho assim: "Os eventos podem se perder de acordo com a fase lunar, é prudente reabilitar as notificações em um intervalo que você se sinta confortável, de acordo com a sua intuição".

    Em 15/03/2005 08:48 - Comentários (1)

    Alguém ainda procura convites para o GMail?

    Achei um site que tem +-500 mil convites do Gmail para doação. Eu testei, realmente funciona e você recebe o convite na hora. Apesar de estar quase abrindo para o povão, uma conta do GMail agora cai bem, nem que seja para usá-la como um disco virtual de 1 GB.

    Em 15/03/2005 22:11 - Comentários (4)

    Não tem jeito, a Microsoft não respeita os programadores VB

    O suporte "standard" da Microsoft para o VB6 está chegando ao fim. Considerando a quase total incompatibilidade entre o VB6 e o VB.NET, a comunidade VB está se mobilizando (de forma tardia) para tentar reverter a situação, inclusive tentando que a Microsoft suporte o VB6 (que eles chamam de "Classic Visual Basic") e faça atualizações para ele. Foi criada uma petição, que pode ser assinada no site classicvb.org.

    Até agora, a petição foi assinada por mais de 2300 desenvolvedores, incluindo 224 MVPs. O mais engraçado que é não valeu nada, a Microsoft não fez nada. Se 1 MVP tem tanta importância como ele dizem, porque 200 deles não valem nada? Não estou dizendo que a Microsoft deveria simplesmente criar o "Classic Visual Basic", mas acredito que eles mereciam mais atenção. A única reação da Microsoft foi uma entrevista que o Somasegar (cujo cargo é "Corporate Vice President - Developer Division") deu ao CNET. Achei as considerações do Somasegar ridículas e as desculpas completamente esfarrapadas:

    • A Microsoft não pretende atualizar ou lançar uma nova versão do "migration wizard". Isso realmente é uma questão de (falta de) respeito.
    • Fazer com que o "Classic Visual Basic" rode na IDE do VS.NET é "tecnicamente não plausível". Espero que isso queira dizer um "nós não queremos fazer", porque se isso significa dificuldade técnica, a Microsoft já não é mais a mesma.
    • O Visual Basic 8 (VS 2005) será muito melhor em RAD (Rapid Application Development). Será que ele realmente entendeu do que se trata ou está mesmo fugindo do assunto? RAD por RAD, os programadores VB6 podem muito bem mudar para o Delphi, já que ele tem um RAD ótimo. Do que adianta ficar desenhando formulários multicoloridos em tempo recorde se a linguagem é incompatível? Estamos falando em compatibilidade com os milhões de sistemas desenvolvidos em VB6 até hoje.
    • No Visual Studio 2005, recursos conhecidos do VB6 serão trazidos de volta, como o "Edit and Continue". Mais uma consideração ridícula. Tiraram esse recurso do VB e agora estão jogando confete por colocar de volta. É como roubar algo de alguém e devolver como um presente, 4 anos depois.

    Mesmo que muitas pessoas considerem o VB6 "feio" e limitado, ele era (não sei se ainda é) a ferramenta de programação Microsoft mais usada. Existem milhões de linhas de código em VB pelo mundo, e elas ficaram estagnadas para sempre. Tudo isso porque a Microsoft está dando importância ao "puritanismo técnico" típico de programadores C++ ao invés de ouvir os clientes. "Eu devo dar o peixe ou ensinar a pescar? Achamos melhor direcionar nosso esforços para trazer os clientes para o 'novo mundo'", disse o Somasegar. Na boa, esse é um bom método para tratar crianças, não milhões de programadores. Acho que está na hora do pessoal do VB aprender com o pessoal do FoxPro como fazer para ganhar o respeito da Microsoft sem ser programador C, C++ ou C#...

    Não estou dizendo que a Microsoft deveria simplesmente fazer o que esses 230 MVPs querem (ou deveria?). Só acho que ela deveria dar mais atenção à reivindicação, e tentar arrumar a situação. Eles não são usuários domésticos, são programadores que mantêm sistemas de bancos, instituições financeiras e empresas gigantescas. Será que a Microsoft pode se dar ao luxo de tratá-los como moleques que não sabem o que querem? Ah, apesar de dar a entrevista, o Somasegar não escreveu uma palavra sobre isso no blog dele.

    [Atualização 17/03/2005: O Somasegar resolveu se mexer, e escreveu um post sobre isso. Repetindo que a grande resposta da MS ao assunto será criar um "VB Upgrade Center" na MSDN...]

    Em 16/03/2005 22:50 - Comentários (9)

    C++ com sanidade: usando ATL em aplicações

    Muita gente costuma dizer que em C++ programa-se muito para pouco resultado. Todo mundo assume que esse "resultado" roda bem mais rápido, mas já é outra coisa... Eu acredito que é possível manter a sanidade e a vida social programando em C++, só é preciso ter um pouco mais de conhecimento e um pouco mais de cuidado. Pretendo escrever bastante sobre isso se eu tiver tempo.

    A lenda sobre o fato de programar em C++ ser trabalhoso, vem principalmente do fato de que as pessoas usam C++ como um C melhorado. Conheço vários programadores C++ que não usam STL e que ainda ficam manipulando char* a toda hora, em pleno ano de 2005 (eu fazia isso até o ano passado). Apesar do C++ ser um superset da linguagem C, ele tem dezenas de melhorias que permitem escrever um código mais organizado, simples e conciso.

    Eu tenho estudado muito sobre qualidade de código, incluindo sobre como escrever um código claro e com menos bugs. Quanto mais eu estudo, mais eu vejo como isso é possível, e como C++ é uma ferramenta maravilhosa para esse fim. C++ segue o conceito usado para aproximar uma linguagem do nível ideal: fazer as coisas simples serem simples, e as coisas complexas serem possíveis. Muitas linguagens de alto nível que existem fazem o simples ser simples, mas fazem o complexo ser complicado demais e cheio de gambiarras e interops.

    A API Win32 não é lá muito sã. Para criar uma janela em Win32 puro (como Petzold), é necessário uma centena de linhas de código. E tudo que você ganha é uma janela branca e muito feia. Mas não podemos esquecer que a API Win32 ainda herda da API do Windows 1.0, que foi feita para linguagem C, usando os conceitos de programação da época. O mais engraçado é que ainda usam esse conceito hoje em dia... Alguém já tentou enviar uma mensagem para o MSMQ usando a API para C? É coisa para quem não tem mais o que fazer. Apesar de gostar bastante de Win32, eu assumo que é um pouco por masoquismo e por orgulho de ter conseguido aprender isso... :-)

    Escolhi ATL para começar esse assunto porque, além de ser uma biblioteca que eu gosto muito, ela ajuda a trazer um pouco de sanidade à programação Win32. E ao contrário da MFC, não é um framework que você é obrigado a seguir. MFC é bom e tem seu espaço, mas vou explorar a ATL porque ela permite o uso "avulso" e por ser menos usada em Win32.

    ATL (Active Templte Library) é basicamente uma biblioteca de templates usada para facilitar o desenvolvimento de componentes COM. Além de cumprir de modo soberbo esse papel, ela também tem diversas classes (muitas delas desconhecidas) para facilitar a programação Win32, tornando nossa vida muito mais fácil. A ATL, ao contrário do MFC, não cria dependência de nenhuma DLL. É só incluir os headers e sair usando. Se você fizer um disassembly de um executável RELEASE que usa ATL, verá que ela é um "thin layer", e que muito da ATL "desaparece" (vira inline) depois da otimização.

    Hoje vamos ver um exemplo de uso da ATL para leitura de arquivos e para buffers:

    
    #define UNICODE
    #define _UNICODE
    
    #include <atlbase.h>
    #include <atlstr.h>
    #include <atlfile.h>
    #include <atlmem.h>
    
    int wmain(int argc, WCHAR* argv[])
    {
       HRESULT hr;
       ATL::CAtlFile hFile;
       ATL::CString strFileName;
       ATL::CHeapPtr<char> pBuffer;
       unsigned __int64 iSize;
       
       strFileName = L"c:\\boot.ini";
    
       //
       // abrindo o arquivo. Bem parecido com o ::CreateFile, mas com parâmetros defaults
       //
       hr = hFile.Create(strFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    
       if(FAILED(hr))
       {
          MessageBox(NULL, ATL::CString("Erro ao abrir ") + strFileName, L"Erro", MB_ICONERROR);
          return hr;
       }
    
       hFile.GetSize(iSize);
    
       //
       // usando o CHeapPtr como buffer. Vamos alocar, e ele desaloca no destrutor
       //
       pBuffer.Allocate(iSize + 1);
    
       //
       // vamos ler o conteúdo do arquivo, usando o CHeapBuffer
       // Como ele sobrecarrega os operadores de conversão, podemos usá-lo
       // como se fosse mesmo um ponteiro para um buffer
       //
       hFile.Read(pBuffer, iSize);
    
       //
       // Vamos colocar um \0 no final para fechar a string
       //
       pBuffer[iSize] = '\0';
       
       //
       // vamos usar MessageBoxA (ANSI) pq nosso programa é UNICODE
       // Não se esqueça que os arquivos texto geralmente são ANSI
       //
       MessageBoxA(NULL, pBuffer, "Conteúdo do arquivo", MB_OK);
    
    
       //
       // Não precisamos desalocar o buffer
       // não precisamos fechar o handle do arquivo
       //
       
       return 0;
    }
    

    Note que o código usa UNICODE, já que não temos necessidade de suportar Windows 9x nesse exemplo. Note que esse código é mais claro do que o código que usa Win32 diretamente, e com mais facilidades. Não precisamos fechar o HANDLE para o arquivo e não precisamos desalocar o buffer usado. Eu costumo usar o "ATL::" para ser notório quais objetos são da ATL. Isso evita confusão com o objetos parecidos que existem na MFC.

    Em 23/03/2005 21:50 - Comentários (0)

    Eu acho que sei C++

    Eu sempre achei que conhecia bem C++, afinal, trabalho com isso faz vários anos. Já fiz drivers (sim, usando C++), programas em WTL, uso bastante ATL, etc. Sempre me considerei "fluente" em C++. Meu interesse em C++ tem crescido mais e mais ultimamente, e tenho buscado ampliar meus horizontes. Com esse objetivo, comprei dois livros do Herb Sutter ( Exceptional C++ e More Exceptional C++, muito bons), o livro do Stroustrup, tenho usado muito STL, estudado Boost e generic programming. Foi então que eu descobri que o C++ moderno está um pouco longe do que eu aprendi a 7 anos atrás, e apesar de fluente em C++, eu ainda tenho muita coisa para aprender.

    Se você é programador C++, faça um teste. Dê uma lida no comp.lang.c++.moderated e veja se você consegue entender as discussões. Dê uma olhada no blog do Thiago Adams e veja se você compreende tudo que ele escreve. Se você entende tudo, você realmente entende de C++. Eu até entendo tudo isso, mas programar "C++ moderno" só está virando parte do meu cotidiano agora. Mesmo assim, eu não tenho experiência nem conhecimento para implementar muitas das libs que fazem parte do Boost. Foi tão mais fácil conhecer tudo de VB6... :-)

    (Para quem não sabe o que é Boost: é uma espécie de extensão extra-oficial para a Standard Template Library do C++. É formada por libs que podem ser propostas por qualquer um, é só passar pelo crivo da lista de discussões do Boost. Dez libs do Boost serão incluídas no próximo padrão C++.)

    Outra coisa que eu acabei percebendo é que, apesar de o C# e o Java serem baseados em C++, eles estão muito distantes do C++ moderno. É só dar uma olhada em alguma lib do Boost e isso que eu estou falando ficará muito claro. É possível que elas se aproximem um pouco mais com a implementação de generics, mas só o tempo vai dizer. Essas linguagens são baseadas no C++ de 10 anos atrás.

    Eu vou começar a dar aulas de C++ in company semana que vem. Uma das coisas que eu pretendo fazer com as minhas aulas é formar alunos que não tenham o vício que eu (nós) sempre tive(mos): usar o C++ como um C melhorado. Como eu já escrevi em post anteriores, não é preciso fazer programação low level só porque se sabe como fazer. É como usar um canhão para matar moscas. Da mesma forma que eu recorro ao C# para tarefas que ele é melhor do que o C++ (XML, por exemplo), devemos saber reusar as coisas que já estão feitas e deixar de lado o "eu não sei direito como isso funciona, não fui eu que fiz". Quantas vezes eu não fiz um thin wrapper para alguma coisa em Win32, só para descobrir depois que já tinha uma classe igual na ATL...

    Isso me fez lembrar de uma coisa muito interessante: a STL ajuda muito a resolver o problema do "não fui eu que fiz". Como os containers são intercambiáveis entre si, é possível usar usar um std::map hoje, e depois trocá-lo por um hash map do Google, ou até mesmo implementar um. O padrão de interface/implementação disponível em Java/COM/CORBA/.NET ajuda nesse sentido, mas tem o problema de ser amarrado ao tipo do dado (ou boxing/unboxing desenfreado). Como a STL é toda baseada em templates e generic programming, esse problema quase some.

    Bom, chega de divagações de sábado a noite...

    Em 26/03/2005 23:37 - Comentários (0)

    C++ com sanidade: fazendo a IDE do Visual Studio.NET trabalhar para você

    Uma das coisas mais irritantes que acontecem durante o DEBUG de uma aplicação ATL, é o fato do VS.NET insistir em fazer debug das sobrecargas de operadores dos "SmartTypes" do ATL. Quando você usa "Step Into" (F11) para fazer debug de uma função, você é obrigado a passar por todas as sobrecargas de operadores antes de chegar na função. Como um pedaço de código fonte vale mais do que (e*pi)^3 palavras, vamos ao que interessa:

    HRESULT DoTheFlufflers(BSTR str, VARIANT v, IUnknown* pUnk)
    {
       MessageBox(NULL, str, L"BSTR", MB_OK);
    
       if(v.vt == VT_BSTR)
          MessageBox(NULL, v.bstrVal, L"VARIANT", MB_OK);
       else
       {
          MessageBox(NULL, 
                     L"Será que você poderia, por obséqio, colocar uma string nessa Variant?", 
                     L"VARIANT", 
                     MB_OK);
          return E_UNEXPECTED;
       }
    
       //
       // dã!
       //
       pUnk->AddRef();
       
       pUnk->Release();
    
       return S_OK;
    }
    
    int main()
    {
       HRESULT hr;
       CComBSTR str;
       CComVariant v;
       CComPtr pUnk;
    
       str = L"Eu sou uma string legal, podemos ser amigos?";
       v = 0xDEADBEEF;
    
       hr = CoCreateInstance(CLSID_FLUFFLERS, NULL, CLSCTX_ALL, IID_IUnknown, (void**)&pUnk);
    
       if(FAILED(hr))
       {
          OutputDebugString(L"Eu desisto...");
          //
          // pena que não funciona em UserMode...
          //
          // KeBugCheckEx(0xFFFFFFFF,0,0,0,0);
          return hr;
       }
    
       DoTheFlufflers(str, v, pUnk);  // <<-- Muito chato fazer Step Into nessa função...
    
       return 0;
    
    }
    

    Quando você usar o F11 sobre a instrução "DoTheFlufflers(str, v, pUnk);", o VS.NET vai entrar primeiro em "ComPtrBase::operator T*()", depois em "CComBSTR::operator BSTR()", e só depois em "DoTheFlufflers". Se essa função fosse de um objeto COM (algo como "pUnk->DoTheFlufferization(str, v, pUnk)"), você ainda teria chamada de "ComPtrBase::operator ->" antes das outras. Isso realmente é muito chato.

    Mas há uma solução para esse problema no VS.NET: Existe a chave [HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio7.1\\NativeDE\\StepOver], onde você pode colocar nomes de funções que não terão StepInto. É só criar valores string com um id numérico e com valor "um_regular_expression=NoStepInto". Para ignorar todos os métodos de CComPtr, use algo como ".+CComPtr.+=NoStepInto". A minha configuração por enquanto é essa:

    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio7.1\\NativeDE\\StepOver]
    "100"=".+CCom.*Ptr.+=NoStepInto"
    "99"=".+CComBSTR.+=NoStepInfo"
    "98"=".+CComVariant.+=NoStepInfo"
    "97"=".+CAtlMap.*=NoStepInfo"
    "96"=".+CAutoPtr.+=NoStepInto"
    

    Os valores são avaliados em ordem inversa. Essa configuração pode ser modificada sem reiniciar o VS.NET, parece que ela é recarregada a cada sessão de debug. Para maiores informações sobre esse assunto, dê uma olhada nessa mensagem de alguém da equipe do C#, que mandou um CTRL+C, CTRL+V da única documentação que existe sobre esse recurso: o código fonte da IDE do Visual Studio.NET.

    Em 30/03/2005 22:56 - Comentários (3)

    Nova mudança de emprego

    Depois de pensar e ponderar bastante, resolvi aceitar a proposta de emprego que recebi no começo do mês: uma oportunidade para trabalhar como Arquiteto de Sistemas, em um grande banco brasileiro (não posso revelar o nome ainda). Meu trabalho será integrar e melhorar a arquitetura dos diversos sistemas COBOL existentes na empresa, além de "mentoring" para os novos desenvolvedores. A intenção do banco é montar uma equipe com uma nova geração de desenvolvedores e arquitetos COBOL, todos antenados nas novas tecnologias e sabendo usar como ninguém os novos paradigmas e best practices na área de desenvolvimento de sistemas.

    Além da renumeração muito boa, a oportunidade me interessou principalmente pelo grau de desafio: trazer novas tecnologias (como generic programming, DataSets e WebServices) para o mundo COBOL, revitalizando o mainframe como parte essencial na lógica de negócios da empresa. Isso incluirá a construção de alguns drivers para mainframes (escritos em COBOL), coisa inédita no Brasil e que ajudará bastante a enriquecer o meu curriculum. Outra coisa que interessou muito é que eu terei total autonomia para fazer pesquisas e testes nessa área, já que eles acreditam que isso pode gerar muitos dividendos para banco, tanto financeiros como na parte de eficiência no atendimento e nas operações.

    Pretendo contar minhas experiências nessa nova empreitada aqui no blog, o que talvez signifique uma redução na quantidade de posts com os assuntos mais comuns (C++ e mercado de software). Mas acredito que os novos assuntos agregarão muito mais informação a todos que lêem esse blog, já que provarão que qualquer tecnologia existente pode se adaptar aos paradigmas modernos.

    Em 01/04/2005 13:15 - Comentários (2)

    Mais libs C++, agora vindas do Google e da Adobe

    O Google e a Adobe resolveram disponibilizar algumas de suas libs C++ sob licenças open source. As duas libs são compatíveis com STL, o que faz com elas possam ser facilmente integradas com a maioria das aplicações C++.

    O Google disponibilizou projetos em diversas áreas, incluindo medição de performance e debug. Mas o que realmente me interessou foram os hash maps que eles fizeram. Eles são especializações (no sentido de funcionamento, não de polimorfismo) do std::map, e têm as mesmas assinaturas. Os mapas foram criados para terem vantagens em campos específicos quando comparados com o std::map. O sparse_hash_map, por exemplo, é um pouco mais lento do que o std::map, mas consome 1/3 de memória. O dense_hash_map consome 10% mais memória do que o std::map, mas é entre 5 e 10 vezes mais rápido! E como as funções são as mesmas do std::map, é fácil trocá-lo pelos mapas especializados de acordo com a necessidade, um typedef deve resolver. Dê uma olhada na página que compara a performance dos mapas, é muito interessante.

    Já a Adobe disponibilizou uma biblioteca C++ razoávelmente grande. Podemos encontrar desde um mini framework para relacionamento entre entidades (descrição muito vaga, para mais detalhes veja a página do projeto) até alguns containers STL muito úteis ou interessantes (como um container forest). Tem também um template que encapsula variáveis de diversos tipos, o adobe::value_t - que é milhares de vezes mais seguro que um void pointer e "mais C++" do que um VARIANT. Com ele é possível ter um std::list que contém ao mesmo tempo strings, inteiros e tipos definidos pelo usuário.

    Em 04/04/2005 16:51 - Comentários (0)

    C++ com sanidade: usando ATL em aplicações, parte 2

    A ATL - como eu já disse antes - cobre diversas áreas da programação Win32, não somente COM. Continuando com a minha série "C++ com sanidade", vou mostrar aqui o acesso ao registro usando ATL. Esse pequeno programa mostra um MessageBox com os HotFixes que estão instalados, lendo essas informações do registro.

    #define UNICODE
    #define _UNICODE
    
    #include <atlbase.h>
    #include <atlstr.h>
    
    int main()
    {
       LONG l;
    
       ATL::CRegKey regKey;
       ATL::CString strKey, strBuffer;
       DWORD dwLen;
       static const wchar_t* wzRegPath = 
             L"SOFTWARE\Microsoft\Windows NT\CurrentVersion\HotFix";
    
       l = regKey.Open(HKEY_LOCAL_MACHINE, wzRegPath, KEY_READ);
       
       if(l != ERROR_SUCCESS)
          return HRESULT_FROM_WIN32(l);
    
    
       strBuffer = L"HotFixes instalados:\r\n\r\n";
    
       for(DWORD dwCurrentKey = 0 ; ; ++dwCurrentKey)
    
       {
          ATL::CRegKey regSubKey;
          ATL::CString strFixDescription, str;
          
          dwLen = MAX_PATH;
          l = regKey.EnumKey(dwCurrentKey, strKey.GetBuffer(dwLen), &dwLen);
    
          strKey.ReleaseBuffer();
    
          if(l != ERROR_SUCCESS)
             break;
    
          //
          // agora que pegamos o nome da chave, vamos pegar o valor "Fix Description"
          //
          str.Format(L"%s%s", wzRegPath, strKey.GetString());
    
          l = regSubKey.Open(HKEY_LOCAL_MACHINE, 
                             str,
                             KEY_READ);
    
          //
          // se o Windows falou que existe, tem que existir. A não ser que o usuário
          // seja muito rápido e consiga apagar a chave no meio da enumeração :-)
          //
          ATLASSERT(l == ERROR_SUCCESS);
          if(l != ERROR_SUCCESS)
             continue;
    
          //
          // primeiro vamos saber qual o tamanho da string
          //
          dwLen = 0;
          l = regSubKey.QueryStringValue(L"Fix Description", NULL, &dwLen);
    
          //
          // se não tem "fix description", vamos ignorar
          //
          if(l != ERROR_SUCCESS && l != ERROR_MORE_DATA)
             continue;
    
          l = regSubKey.QueryStringValue(L"Fix Description", 
                                         strFixDescription.GetBuffer(dwLen), 
                                         &dwLen);
          strFixDescription.ReleaseBuffer();
    
          if(l != ERROR_SUCCESS)
             continue; // xi... vamos ignorar e passar para o próximo
    
          strBuffer.AppendFormat(L"%s - "%s"\r\n", 
                                 strKey, 
                                 strFixDescription.GetString());      
       }
    
       MessageBox(NULL, strBuffer, L"Windows HotFixes", MB_ICONINFORMATION);
    }
    

    Para compilar esse código no Visual C++, use um projeto "Win32 Console Project", e não um "ATL Project". Você pode usar as classes da ATL em qualquer programa, é só adicionar os headers (no caso do ATL::CRegKey, só o atlbase.h é suficiente).

    Não se esqueça que minha intenção não é fazer um tutorial sobre as classes da ATL, e sim, mostrar que essas classes existem e dar exemplos práticos de como elas podem facilitar a sua vida, ajudando-o a escrever um código mais claro e menos sujeito à erros. Para mais informações, RTFM

    Em 05/04/2005 22:57 - Comentários (0)

    Tinha um bug no caminho, no caminho tinha um bug

    O código C++ abaixo contém diversos bugs, e se comporta de forma estranha durante a execução (faça o teste em DEBUG). Quais são os bugs e por que eles acontecem?

    //
    // coloque esses include's no stdafx.h, se preferir
    //
    #include <iostream>
    #include <vector>
    
    //
    // não consigo fugir do meu vício de programação Windows...
    //
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    //
    // vamos usar uma classe para facilitar a mudança
    //
    class CTest1
    {
    public:
    	DWORD dwValue;
    	std::string strValue;
    };
    
    class CTest2
    {
    private:
    	CTest1* m_pTest1;
    public:
    	CTest2(DWORD dw, const std::string& str)
    	{
    		m_pTest1 = new CTest1();
    
    		m_pTest1->dwValue = dw;
    		m_pTest1->strValue = str;
    
    	}
    	~CTest2()
    	{
    		delete m_pTest1;
    	}
    
    	CTest1* GetTest1() const
    	{
    		return m_pTest1;
    	}
    
    };
    
    int main()
    {
    	std::vector vecTest2;
    
    	//
    	// vamos colocar um CTest2 no vetor, passando
    	// os valores para o construtor
    	//
    	vecTest2.push_back(CTest2(100, "1bit"));
    
    	//
    	// agora vamos pegá-lo e ver os valores
    	//
            std::cout << "dwValue = "  << vecTest2[0].GetTest1()->dwValue  << std::endl;
            std::cout << "strValue = " << vecTest2[0].GetTest1()->strValue << std::endl;
    
    	return 0;
    }
    

    No próximo post eu explico o bug. E em posts posteriores vou sugerir algumas formas de resolver o problema (sendo uma delas usando ATL, é claro!).

    Em 06/04/2005 10:17 - Comentários (0)

    Eu não acredito...

    pra acreditar? Será que nunca mais precisaremos responder (e perguntar) nos fóruns e listas de discussão? :-)

    Em 07/04/2005 23:00 - Comentários (1)

    Por que o bug?

    Nós tínhamos um bug, que acontecia quando inseríamos (push_back) um objeto de uma determinada classe dentro de um container STL. Quando líamos o valor da variável, ela não correspondia ao valor do objeto inserido no container, e a runtime do C++ gerava um assert dizendo que estávamos chamando delete para um objeto mais de uma vez.

    O problema nesse caso, foi causado por algo que o C++ não costuma fazer: um código gerado pelo compilador, algo que não foi você que fez. Nesse caso, o copy constructor. O copy constructor é um construtor especial, que é chamado quando um objeto é copiado. Isso acontece quando você atribui um objeto a outro, retorna um objeto de uma função, ou passa um objeto para uma função como valor. Um trecho de código vale mais que (pi^10) palavras:

    class X
    {
    public:
      int i;
    };
    
    X func(X obj)
    {
      X localx;
    
      // mais uma cópia
      localx = obj;
    
      localx.i = obj.i;
    
      // oh, estamos copiando novamente!
      return localx;
    }
    
    int main()
    {
      X x1, x2;
     
      x2.i = 10; 
    
      // copiando...
      x1 = func(x2);
    
      return 0;
    }
    

    Na função "func" do exemplo acima, existem 3 operações de cópia: uma quando passamos x2 como parâmetro, outra quando atribuimos o parâmetro obj a localx, e outra na hora de retornar localx. Nessas situações, o copy constructor é chamado para copiar o objeto em questão. Como no nosso exemplo não temos um copy constructor definido, o compilador gera um automaticamente. Olhe como fica a nossa classe X com um copy constructor, equivalente ao que é gerado pelo compilador:

    class X
    {
    public:
      //
      // se definirmos um copy constructor, o compilador não gerará mais
      // o construtor default. Então vamos fazê-lo
      //
      X()
      {}
    
      //
      // copy constructor, que tem a sintaxe [tipo(const tipo& param)]
      // esse copy constructor é equivalente ao gerado pelo compilador
      //
      X(const x& v)
      {
        i = v.i;
      }
      int i;
    };
    

    Para nossa classe X, o copy constructor não gera problemas. Agora, vamos ver o copy constructor equivalente ao gerado pelo compilador para nossa classe com bug:

    class CTest2
    {
    private:
      CTest1* m_pTest1;
    public:
       ...  
    
      //
      // copy constructor equivalente ao gerado pelo compilador
      //
      CTest2(const CTest2& v)
      {
        m_pTest1 = v.m_pTest1;
      }
    
      ...
    
      ~CTest2()
      {
        delete m_pTest1;
      }
    };
    

    Note que m_pTest1 é a única variável membro de CTest2. Então a única coisa que é feita é copiar o valor dessa variável (que é um ponteiro). Note que - isso é importante - o construtor não é rodado no caso de cópia de objeto. Sendo assim, o objeto cópia não terá um ponteiro alocado com new, mas sim, a cópia do ponteiro do objeto do qual ele foi copiado. Assim, tentaremos chamar delete para o mesmo ponteiro, mas nas duas instâncias de CTest2 - o que gera o assert que falei.

    Se você está se perguntando onde é feita a cópia no código do exemplo do bug, repare que eu criei um objeto temporário diretamente ao invés de criar um objeto:
      //
      // criamos um objeto temporário do tipo CTest2, chamando
      // o construtor para inicializá-lo
      //
      vecTest2.push_back(CTest2(100, "1bit"));
    

    O construtor do nosso objeto temporário é executado logo antes da chamada da função (push_back), e o destrutor é chamado logo após o retorno da função. A função push_back espera uma referência para o objeto (const CTest2&), então não é feita a cópia durante a passagem de parâmetros. Mas o objeto é copiado ao ser inserido no vector<>, o que faz com que o copy constructor gerado seja chamado, e copie o valor do ponteiro.

    Uma das possíveis soluções é criarmos um copy constructor para nossa classe com bug, fazendo com que um novo objeto CTest1 seja criado durante a cópia. Assim, cada classe pode chamar delete para o seu ponteiro. Nossa classe ficaria assim:

    class CTest2
    {
    private:
      CTest1* m_pTest1;
    public:
       ...  
    
      CTest2(const CTest2& v)
      {
        m_pTest1 = new CTest1();
    
    
        //
        // por falar em copy constructor, essa instrução chamará o copy constructor
        // da classe CTest1, copiando todos os membros
        //
        *m_pTest1 = v.m_pTest1;
      }
    
      ...
    
      ~CTest2()
      {
        delete m_pTest1;
      }
    };
    

    Nossa solução é eficaz nesse caso. Mas e se precisássemos que as duas cópias usassem o mesmo ponteiro? Aguarde os próximos posts.

    Em 09/04/2005 12:43 - Comentários (4)

    Mais explicações sobre copy constructors

    Aquele post sobre copy constructors gerou para mim uma dúvida existencial: por que o compilador os gera automaticamente? Não é lá muito comum que os compiladores C++ gerem código.

    Depois procurar no FAQ do Stroustrup e não encontrar, resolvi perguntar aos gurus do comp.lang.c++.moderated. Se eles não soubessem, nem o próprio Stroustrup saberia, e eu continuaria até o fim dos meus dias com o peso da dúvida sobre os ombros. As respostas foram muito interessantes.

    Em 11/04/2005 16:35 - Comentários (3)

    Alguém pode me explicar o que é isso nas estatísticas do meu site?
     Operating SystemsHitsPercent
    Windows880994 %
    Unknown3473.7 %
    Linux1721.8 %
    CPM220.2 %
    Macintosh210.2 %

    CP/M???? Alguém anda usando CP-500 para acessar meu site?? :-)

    Em 11/04/2005 19:49 - Comentários (8)

    Essa estatística é mais do que compreensível
     BrowsersGrabberHitsPercent
    MS Internet ExplorerNo701374.6 %
    FirefoxNo137214.6 %
    Unknown?5385.7 %
    MozillaNo1861.9 %
    NetscapeNo1341.4 %
    OperaNo1111.1 %
    KonquerorNo210.2 %
    SafariNo210.2 %

    Eu achei que essa grande quantidade de acessos usando Firefox era isolada, mas essa porcentagem vem crescendo a cada mês. Eu sei que meu site é direcionado para um público mais técnico, mas mesmo assim fico feliz. Sou usuário do Firefox desde a versão 0.1, quando ele ainda se chamava Phoenix.

    Mesmo os microsoftianos mais fanáticos que se recusam a largar seu browser fraquinho devem agradecer muito à equipe do Firefox. Foram eles que conseguiram fazer a Microsoft se mexer e ressuscitar a equipe do IE (mesmo que a nova equipe tenha uns caras de pau).

    Em 12/04/2005 22:45 - Comentários (0)

    Por que um programador explica teoria musical melhor do que um músico?

    Eu já estudei teoria musical (com algum esforço eu leio uma partitura) e toco violão, guitarra e bateria. Já li várias coisas sobre musicalidade e teoria musical, mas nada tão claro e bem explicado quanto a explicação do Eric Lippert.

    (Eric Lippert é um programador da Microsoft que já apareceu em vários vídeos do Channel9 e trabalhou na equipe do VBScript)Por que um programador explica teoria musical melhor do que um músico? Ou será que eu entendo melhor por ser um programador, e pelo fato dele explicar música como um programador?

    Em 13/04/2005 16:52 - Comentários (0)

    Adobe compra a Macromedia

    Adobe compra a Macromedia por US$ 3,4 bilhões.

    Traduzindo: A empresa líder no ramo de software para mídia tradicional (Acrobat [Reader], PhotoShop, Illustrator, etc) compra a empresa líder no ramo de software para mídia online (Flash, Shockwave, DreamWeaver, Fireworks). Resta saber o que será feito com os softwares que "fazem as mesmas coisas", como GoLive e Fireworks, Illustrator e FreeHand.

    Em 18/04/2005 13:55 - Comentários (3)

    Trabalhando como PJ ou como CLT

    Eu trabalho atualmente como PJ (pessoa jurídica, emitindo nota fiscal), mas já trabalhei como CLT. A maioria das pessoas costuma discutir as vantagens e desvantagens das duas modalidades, muitas vezes sem saber a real implicação de cada uma. Vou mostrar alguns fatos sobre as duas modalidades, mas nada que esgote todos os detalhes do assunto.

    PJ

    • Custo para empresa: somente o custo da nota fiscal e mais algum benefício que ela resolver dar. Eu conheço uma empresa que contrata como PJ, mas oferece férias remuneradas (você emite uma nota mas não trabalha) e 13º (você emite uma nota com o dobro do valor em dezembro). Eu tenho férias remuneradas, mas não tenho 13º, e isso muda de empresa para empresa. A maioria das consultorias (cuidado com elas) não oferece nada.
    • Custos para o "funcionário": O custo é de 13,33% por nota fiscal emitida (IRRF (4,80%), COFINS (3%), PIS (0,65%), CSLL (2,88%), ISS para empresa de software em São Paulo (2%)) mais o custo do contador (entre R$ 100,00 e R$ 260,00 por mês), para uma empresa LTDA. Alguns impostos são recolhidos mensalmente (como o ISS) e outros trimestralmente. Uma parte dos impostos trimestrais são retidos pela empresa pagadora de acordo com o valor da nota, senão você mesmo paga. Resumindo: é uma bagunça generalizada. Não vou começar um discurso criticando esse modelo tributário estúpido, remendado e sem sentido porque eu prometi para mim mesmo que não falaria de política no blog. Arrume um bom contador (de preferência por indicação) que resolva isso para você.
    • Salário: Geralmente maior do que quando se é CLT. Uma parte do que a empresa (note que eu disse uma parte) economiza com encargos é repassado ao salário. Varia entre 20% e 100% maior do que CLT.
    • Benefícios: Quase nada. Ao invés de ter o governo como um pai, dizendo o que você deve fazer com o que você ganha, você recebe tudo em dinheiro. Fica por sua conta fazer uma previdência privada, pagar plano de saúde, pagar sua alimentação, etc. Fazer uma previdência privada e ter dinheiro guardado são duas coisas muito importantes para quem trabalha como PJ. Se você não sabe administrar seu dinheiro, talvez seja melhor ser CLT e deixar o governo cuidar dele. Afinal, o governo é especialista em cuidar do nosso dinheiro (e pegar uma boa parte dele).
    • Garantias: Você tem as garantias que o contrato de prestação de serviço te dá. Não tem garantias da CLT como seguro desemprego, multas rescisórias, indenizações, aviso prévio etc. Geralmente é algo muito mais simples do que a mão pesada da CLT: "eu trabalho, você me paga, e só". Você pode ter desde nenhuma garantia, até 3 meses de notificação para cancelar o contrato. Se no contrato não diz nada sobre tempo de notificação para rescisão, a empresa pode te demitir hoje, dizendo que amanhã você não precisa mais aparecer. Mas, da mesma forma, você pode arrumar um emprego melhor e dizer que você pode começar amanhã (na prática costuma-se dar uma ou duas semanas de notificação). É recomendado pedir ajuda de um advogado para interpretar e ajustar o contrato.
    • Liberdades: Assim como as garantias, depende do contrato, mas geralmente são maiores do que o regime CLT - tanto para o "funcionário" quanto para o contratante. Você também tem a liberdade de ter a previdência e o plano de saúde que você quer. No caso da previdência, além das previdências privadas serem bem mais baratas, você pode pagar para ter um benefício maior do que o limite de R$ 2400,00 do INSS. Nesse regime a empresa tem facilidade para contratar e facilidade para demitir. Esse é um dos motivos pelos quais os profissionais desatualizados (e sindicalistas) não gostam do regime PJ, é mais fácil ser demitido por incompetência ou incapacidade. Mas também é muito mais fácil sair da empresa, pois não é preciso se preocupar com todas as burocracias da CLT (aviso prévio, homologação, exame médico, etc). Em um mercado dinâmico como o nosso eu acho isso importante. Conheço programadores que fecham projetos de 3 a 6 meses, a burocracia disso pela CLT seria um inferno. Como um PJ ganha por hora trabalhada, não existe o desconto do descanso remunerado. Quando você falta, deixa de receber somente aquelas horas, que podem ser compensadas outro dia.

    CLT

    • Custo para empresa: Entre 130% e 230% do valor do seu salário. Isso mesmo, até 230% do seu salário! Por isso que as empresas relutam em contratar, porque o custo é excessivamente alto e a burocracia também. Como a burocracia é alta, a empresa também tem um alto custo administrativo.
    • Custos para o funcionário: Eu fiz uma simplificação da fórmula para calcular o salário líquido para quem ganhar mais que o limite do IR (2.326,00 e paga 27,5% de IR). A fórmula é (0,725 * salário bruto) + 238,00, e ela dá uma diferença de até R$ 10,00 para mais ou para menos. Mas é ótima para dar uma idéia do valor do salário e suficientemente simples para ser decorada. Pelo modo normal você tem que calcular INSS, limite de INSS, IR, etc. Note que isso não conta o IR que for restituído na declaração e que, além os descontos previstos nessa fórmula, a empresa pode descontar vale-transporte, vale-refeição e o plano de saúde.
    • Salário: Geralmente menor do que quando se é PJ, já que a empresa gasta bastante dinheiro com impostos e benefícios. Além do salário em dinheiro, esses benefícios devem ser levados em conta na hora de calcular o salário.
    • Benefícios: Geralmente muitos, a empresa passa a ser praticamente um pai. Você é obrigado a pagar previdência (o que seria bom se o custo/benefício da previdência do governo não fosse ruim), e muitas vezes recebe vale transporte, vale refeição, plano de saúde, etc. Apesar de ser aparentemente muito bom para o funcionário, isso aumenta bastante o seu custo e não permite o remanejamento desse dinheiro. Por exemplo, um funcionário que vai almoçar em casa não pode receber vale refeição em dinheiro, e um funcionário que já tem plano de saúde porque está como dependente do pai também não pode receber o valor do plano de saúde da empresa em dinheiro.
    • Garantias: Um mês de aviso prévio antes de ser demitido, a empresa é obrigada a pagar 15 dias do seu salário em caso de acidente (o resto é por conta da previdência). Você tem direito ao FGTS (que pode ser usado para compra de um imóvel), seguro desemprego, licença saúde e licença maternidade. Esses seguros são úteis para quem consegue pagar suas contas com o limite do benefício do INSS (R$ 2400,00). A garantia de emprego por ser CLT é uma ilusão, a empresa pode demitir da mesma forma. Como a burocracia e os custos para demissão são altos, a empresa evita contratar porque ela sabe que quando precisar demitir precisará passar por tudo isso.
    • Liberdades: Você tem o direito de trabalhar :-).

    Note que não existe um regime melhor do que o outro, tudo depende do caso e da situação. Existem empresas que contratam como CLT e você recebe 17 salários por ano. O salário de um PJ é quase sempre maior do que um CLT, e você tem mais liberdade. É necessário fazer as contas e ver o que vale mais a pena.

    Eu prefiro trabalhar como PJ porque acho importante ter liberdade, e prefiro pagar meu próprio plano de saúde e pagar minha própria previdência. Esse regime está longe do ideal, mas ainda acho o menos pior. Mesmo assim, se eu recebesse uma proposta CLT que fosse muito boa eu aceitaria. Tudo depende das condições e do mercado. Essa é a minha opinião pessoal, e não vale para todas a pessoas. Lembre-se que eu falei que alguém que trabalha como PJ deve ser razoavelmente organizado, e pagar sua própria previdência e plano de saúde. A CLT tem garantias muito importantes (exames médicos e seguros para o caso de doença), mas a tamanha burocracia dificulta muito as contratações. Chegamos a um ponto em que, apesar do imposto para PJ ser alto, ele ainda é menor do que o da CLT. Esse é o retrato do absurdo: é mais barato ter uma empresa do que ser um funcionário.

    Não se esqueça que eu sou só um programador. Confirme esses dados com seu advogado ou contador antes de tomar alguma decisão.

    Em 19/04/2005 15:53 - Comentários (63)

    Resolvendo o bug usando um smart pointer feito em casa

    Lembra do bug? Solucionei o problema criando um copy constructor (construtor chamado quando um objeto é inicializado com o valor de outro) para a nossa classe CTest2, de forma que em caso de cópia um novo objeto fosse criado. Isso resolveu o nosso problema para essa classe específica.

    Minha segunda solução é criar um smart pointer (template que se comporta exatamente com o ponteiro original) que faça a cópia. Assim, não é mais necessário implementar o copy constructor só porque a classe tem um ponteiro como membro. Se o objeto apontado por esse ponteiro deve ter o tempo de vida igual ao do objeto que o contém (o que é o nosso caso), isso funciona como um luva.

    Nosso smart pointer além de gerenciar cópia, ainda desaloca o objeto automaticamente. Sim, a mesma coisa que muitas runtimes fazem ao custo do seu programa ficar muito mais lento... Bom, vamos ao código, porque ele sempre vale mais do que 6378 palavras:

    #include <iostream>
    #include <vector>
    #include <string>
    
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    //
    // nosso template para copiar o objeto apontado
    //
    template <typename T>
    class CopyPointer
    {
    public:
       T* p;
    
       CopyPointer()
       {
          p = NULL;
       }
    
       ~CopyPointer()
       {
          Free();
       }
    
       void Alloc()
       {
          Free();
          p = new T();
       }
    
       void Free()
       {
          if(p)
          {
             delete p;
             p = NULL;
          }
       }
    
       void Attach(T* pT)
       {
          if(pT == p)
             return;
    
          Free();
          p = pT;
       }
    
       T* Detach()
       {
          T* tempp = p;
          p = NULL;
    
          return tempp;
       }
    
       //
       // copy constructor. Quando for copiado dessa forma,
       // vamos copiar o objeto ao invés de copiar o ponteiro
       //
       CopyPointer(const CopyPointer<T>& c)
       {
          //
          // isso vai chamar o copy constructor de T
          //
          p = new T(*c.p);
       }
    
       //
       // isso permite que haja cópia a partir de um ponteiro raw, não
       // só pelo nosso smart pointer
       //
       CopyPointer(const T* pT)
       {
          p = new T(*pT);
       }
    
       CopyPointer& operator=(T* pT)
       {
          if(p == pT)
             return *this;
    
          if(p)
             *p = *pT;
          else
             p = new T(*pT);
    
          return *this;
       }
    
       CopyPointer& operator=(const CopyPointer<T>& pT)
       {
          if(p == pT.p)
             return *this;
    
          if(p)
             *p = *pT.p;
          else
             p = new T(*pT.p);
    
          return *this;
       }
    
       operator T*() const
       {
          return p;
       }
    
       T* operator->() const
       {
          return p;
       }
    
       bool operator!() const
       {
          return (p == NULL);
       }
    
       bool operator<(T* pT) const
       {
          return p < pT;
       }
    
       bool operator==(T* pT) const
       {
          return p == pT;
       }
    };
    
    
    class CTest1
    {
    public:
       CTest1() : dwValue(0)
       {}
    
       DWORD dwValue;
       std::string strValue;
    };
    
    
    
    class CTest2
    
    {
    private:
       CopyPointer<CTest1> m_pTest1;
    public:
    
       CTest2()
       {
          m_pTest1.Alloc();
       }
       
       //
       // Não preciso mais de um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
    
       CTest2(DWORD dw, const std::string& str)
       {
          m_pTest1.Alloc();
    
          m_pTest1->dwValue = dw;
          m_pTest1->strValue = str;
       }
    
       CTest1* GetTest1() const
       {
          return m_pTest1;
       }
    
    };
    
    int main(int argc, char* argv[])
    {
       std::vector<CTest2> vecTest2;
       
       CTest2 t1, t2;
    
       t1.GetTest1()->dwValue = 50;
       t1.GetTest1()->strValue = "baba";
    
       //
       // chamando o copy constructor
       //
       CTest2 t3 = t1;
    
       //
       // chamando operator=
       //
       t2 = t3;
    
       
       //
       // vamos colocar um CTest2 no vetor. Isso faz uma cópia
       //
       vecTest2.push_back(CTest2(100, "1bit"));
    
       //
       // agora vamos pegá-lo e ver o seu valor
       //
       std::cout << "dwValue = "  << vecTest2[0].GetTest1()->dwValue  << std::endl;
       std::cout << "strValue = " << vecTest2[0].GetTest1()->strValue << std::endl;
    
    
       return 0;
    }
    

    Só não tenho certeza do "const correctness" do meu smart pointer. Alguém se habilita?

    Em 01/05/2005 19:54 - Comentários (1)

    Trabalhamos no ambiente correto?

    Minha profissão é "Desenvolvedor de Software", "Analista Programador" - ou programador mesmo, no coloquial. Sim, no coloquial, porque minha profissão envolve muito mais do que programar um computador. Meu trabalho envolve fazer reuniões para definição de projetos, reuniões para resolver problemas em servidores, procurar soluções para os mais diversos problemas e estudar sobre tecnologias que podem ser usadas para resolver esses diversos problemas. Além disso, envolve transformar todos os meus estudos e conhecimentos em linguagens e tecnologias em uma solução. Dessas várias atribuições que eu falei, só as reuniões envolvem várias pessoas interagindo e dialogando. Todas envolvem pensar e resolver problemas. Ou seja: a grande maioria do meu trabalho envolve raciocínio, e conseqüentemente, concentração.

    Em todas as bibliotecas (de livros, não de software) que eu entrei até hoje, era proibido conversar e fazer barulho. Tudo isso porque os freqüentadores estavam lá para ler, estudar ou resolver problemas. Não é exatamente isso que eu faço no dia-a-dia? E por que a biblioteca é silenciosa e todos os lugares que eu trabalhei até hoje eram mais barulhentos do que um galinheiro? TODOS os lugares que eu trabalhei eram barulhentos, alguns mais, outros menos. Já trabalhei em empresas de software, provedores de Internet, bancos, bolsas de valores. Além disso já fiz entrevista em várias empresas e pude ver como era o ambiente. A coisa mais evoluída que eu vi foi separar os desenvolvedores do pessoal de suporte telefônico. Mesmo assim, todos os desenvolvedores ficavam amontoados na mesma sala. Em algumas empresas, a mesa de reuniões era NA MESMA SALA do desenvolvimento. Se houvesse uma reunião do seu lado, paciência. Minha única solução sempre foi a que a maioria usa: fones de ouvido e música.

    Será que nossa mesa minúscula (muitas vezes compartilhada) com um computador é o melhor lugar para fazer o que nós fazemos? Por que em Universidades conceituadas (não sei como é nas UNICACAs) cada professor tem sua sala? Qual a diferença de necessidade de concentração entre um professor da USP e um desenvolvedor tentando fazer debug de um algoritmo de load balance? As empresas geralmente organizam as mesas dos desenvolvedores da mesma forma que organizam as mesas dos auxiliares administrativos. E o gerente que solta a pérola "Nosso software deve ser portado para Linux nem que isso leve 15 dias" tem uma sala exclusiva. Os gerentes - que geralmente são os que menos trabalham - são os que geralmente têm sala exclusivas. Será que isso está certo?

    Quem assiste o Channel9 da Microsoft ou lê sobre as condições de trabalho lá deve ter visto que eles dão UMA SALA PARA CADA DESENVOLVEDOR. Será que esse é um dos motivos pelos quais eles são a Microsoft e as outras empresas de software não passam de outras empresas de software? Tudo que eu queria era uma sala só para mim, com um sofá de 3 lugares para poder deitar e pensar (ou até dormir um pouco...). É muito difícil pensar na reengenharia de um cluster sentado em uma cadeira desconfortável e com uma mesa minúscula. Uma lousa e um pouco de silêncio seriam muito bem vindos.

    Bom, enquanto eu não abro minha empresa de software, continuo com meu fone companheiro...

    Em 04/05/2005 22:01 - Comentários (13)

    WinDbg Live Programming: Fazendo um log das chamadas à MessageBox

    Nesse exemplo, nós faremos uma espécie de log de todas as chamadas para MessageBox que um processo fizer. Toda vez que um MessageBox for chamado, a string passada será passada também para OutputDebugString. Dessa forma, é possível usar o DebugView da Sysinternals para salvar isso em um arquivo (ou somente ver no WinDbg mesmo).

    Para fazer isso, vamos criar uma "função" em tempo real (em assembly), e redirecionar o fluxo do programa para ela toda vez que o MessageBoxExW for chamado. No Windows NT (NT/2000/XP/2003), a função MessageBox nada mais faz do que chamar MessageBoxEx, que é quem realmente faz o trabalho. Além disso, quando chamamos MessageBoxA (quando você compila seu programa em ANSI, MessageBox é um define para MessageBoxA), ela só "traduz" sua string para UNICODE e repasa para versão W. Sendo assim, fazendo nosso hook em MessageBoxExW vamos pegar todas as variantes.

    Chega de teoria e vamos à escovação de bits:

    A primeira coisa que precisamos fazer é alocar memória para a nossa "função". Isso pode ser feito usando o comando ".dvalloc" do WinDbg, que aloca memória no espaço de endereçamento do processo. Vamos alocar 1kb de memória:

    0:000> .dvalloc 1000
    Allocated 1000 bytes starting at 00230000
    

    Agora temos a nossa memória. Vamos salvar o endereço dela no pseudo-registrador $t0 do WinDbg. O WinDbg possui 20 pseudo-registradores ($t0 até $t19) para usos como esse. Para isso, usaremos o comando "r", que altera o valor de um registrador:

    0:000> r $t0 = 0x00230000
    

    Como as strings enviadas para o MessageBox não necessariamente são terminadas com CRLF, vamos colocar um na nossa memória para podermos dar uma quebra de linha na string que enviaremos ao OutputDebugString. Fazemos isso usando o comando "ezu" para gravar uma string UNICODE na nossa memória (cujo endereço está em $t0). Depois de fazer isso, vamos usar o comando "db" para ver a memória, pasando L0xF no final para dizer que só queremos ver 0xF bytes:

    0:000> ezu $t0 "\r\n"
    
    0:000> db $t0 L0xF
    00230000  0d 00 0a 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    

    Agora que já temos nosso "ENTER" na memória, vamos à parte que interessa: escrever o código. Vamos escrever o código logo depois da string que acabamos de gravar. Como a string é UNICODE, seu tamanho em bytes é 6 (dois caracteres UNICODE mais o 00 00 no final). Vamos gravar o início do nosso código no pseudo registrador $t1 e escrevê-lo para chamar o OutputDebugString, passando o ponteiro da string que veio em EAX como parâmetro. Aqui vamos usar o comando "A" para começar a escrever o código assembly e depois usaremos o comando "U" para verificar se o disassembly ficou como queríamos:

    0:000> r $t1 = $t0 + 0x6
    
    0:000> a $t1
    00230006 push @eax
    push @eax
    00230007 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    0023000c push 0x00230000
    push 0x00230000
    00230011 call kernel32!OutputDebugStringW
    
    call kernel32!OutputDebugStringW
    00230016 int 3
    int 3
    00230017 
    
    0:000> u $t1
    00230006 50               push    eax
    00230007 e8aaf54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    0023000c 6800002300       push    0x230000
    00230011 e8a0f54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    00230016 cc               int     3
    00230017 0000             add     [eax],al
    00230019 0000             add     [eax],al
    0023001b 0000             add     [eax],al
    

    Duas coisas devem ser notadas no trecho acima. A primeira é que vamos chamar o OutputDebugString duas vezes, uma para string recebida em EAX e outra para o nosso CRLF. O ideal seria concatenar isso em um novo buffer, mas para uma aplicação single threaded isso funciona sem problemas. A segunda coisa é que coloquei um "int 3" no final do código. Isso é um breakpoint, e caso o processador passe do meu código (eu vou redirecionar isso antes com um BP), ele vai parar no breakpoint forçado e eu posso resolver o problema. O código depois do "int 3" deve ser ignorado, é o disassembly de "00 00 00 00".

    Nosso próximo passo é fazer o WinDbg redicionar o fluxo do programa para a nossa função toda vez que o MessageBoxExW for chamado. Para isso vamos usar o suporte que o BP (comando para breakpoint) nos dá para executar comandos toda vez que um breakpoint for atingido. Vou mostrar o comando primeiro e explicar depois:

    0:000> bp user32!MessageBoxExW "r $t2 = @eip ; r eax = poi(@esp+8) ; r eip = $t1 ; g"
    

    Os comandos na string depois do BP serão executados assim que o breakpoint for atingido. Note que são vários comandos separados por ";". Vamos à explicação:

    • r $t2 = @eip: Vamos salvar o ponteiro de instrução do processador (EIP) em $t2. Tem um "@" antes para dizer ao WinDbg para interpretar isso como um registrador, e não tentar procurar symbols para isso;
    • r eax = poi(@esp+8): Esse é fácil :-) Vamos colocar o apontado do segundo parâmetro da pilha no registrador EAX. Quando chegamos numa função, o primero parâmetro está em ESP+0x4, o segundo em ESP+0x8, etc. Dessa forma estaremos colocando em EAX o ponteiro para a string que foi passada no segundo parâmetro da MessageBoxEx;
    • r eip = $t1: Aqui vamos colocar o endereço da nossa "função" em EIP. Isso faz com que o processador comece a executar as instruções que estão na nossa "função".
    • g: Hey, Ho, Let's Go!

    Resumindo: Quando chegarmos em MessageBoxExW, vamos colocar a string em EAX, salvar a instrução atual em $t2 e mandar o processador para a nossa "função".

    Agora que já fizemos o redirecionamento para nossa "função", precisamos fazer com que o processador volte para a MessageBoxExW depois de executar nosso código, fazendo com que o programa siga seu fluxo normalmente. Para isso, vamos usar a mesma técnica que usamos no primeiro breakpoint, modificar o EIP:

    0:000> bp $t0 + 0x16 "r eip = $t2 ; g"
    

    Se você chamar um "U $t0 + 0x16", vai ver que esse é o endereço do nosso "int 3", que vem logo depois do nosso código. Aqui nós estamos colocando um breakpoint que será atigindo logo depois da segunda chamada à OutputDebugString. A única coisa que temos que fazer nesse momento é restaurar o EIP usando o conteúdo antigo dele que salvamos em $t2 e deixar o programa seguir seu curso. Com isso teremos o nosso log.

    Pronto, isso deve funcionar. Abra no WinDbg qualquer programa que chame um MessageBox, siga os passos acima, e veja as mensagens mostradas no prompt do WinDbg. Teste também a visualização com o DebugView.

    Isso tudo funcionou porque OutputDebugString é __stdcall (ou seja, a função chamada que restaura a pilha). Se a função fosse __cdecl (quem chamou restaura a pilha), o nosso assembly ficaria um pouco maior. A única API Win32 que eu sei que é __cdecl é a wsprintf, porque ela recebe parâmetros dinamicamente.

    Segue o log completo dos comandos usados para isso:

    0:000> .dvalloc 1000
    Allocated 1000 bytes starting at 00230000
    
    0:000> r $t0 = 0x00230000
    
    0:000> ezu $t0 "rn"
    
    0:000> db $t0 L0xF
    00230000  0d 00 0a 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    
    0:000> r $t1 = $t0 + 0x6
    
    0:000> a $t1
    00230006 push @eax
    push @eax
    00230007 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    0023000c push 0x00230000
    push 0x00230000
    00230011 call kernel32!OutputDebugStringW
    call kernel32!OutputDebugStringW
    00230016 int 3
    int 3
    00230017 
    
    0:000> u $t1
    00230006 50               push    eax
    00230007 e8aaf54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    0023000c 6800002300       push    0x230000
    00230011 e8a0f54f79       call    KERNEL32!OutputDebugStringW (7972f5b6)
    00230016 cc               int     3
    00230017 0000             add     [eax],al
    00230019 0000             add     [eax],al
    0023001b 0000             add     [eax],al
    
    0:000> db 0x230000
    00230000  0d 00 0a 00 00 00 50 e8-aa f5 4f 79 68 00 00 23  ......P...Oyh..#
    00230010  00 e8 a0 f5 4f 79 00 00-00 00 00 00 00 00 00 00  ....Oy..........
    00230020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    00230070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    
    0:000> bp user32!MessageBoxExW "r $t2 = @eip ; r eax = poi(@esp+8) ; r eip = $t1 ; g"
    
    0:000> bp $t0 + 0x16 "r eip = $t2 ; g"
    

    E aqui vai o fonte do programa simples que eu fiz para testar:

    #define WIN32_LEAN_AND_MEAN
    #define UNICODE
    #define _UNICODE
    
    #include <windows.h>
    
    
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
     	wchar_t wzBuffer[128];
    	DWORD dwInterval = 1000, dwCount = 10;
    
    
    	for(DWORD a = 0 ; a < dwCount ; a++)
    	{
    		wsprintf(wzBuffer, L"mensagem %d", a);
    
    		MessageBox(NULL, wzBuffer, L"LOG", MB_OK);
    		Sleep(dwInterval);
    
    	}
    
    	return 0;
    }
    

    Para maiores informações veja o help do WinDbg (RTFM). Boa escovação de bits!

    Em 06/05/2005 11:42 - Comentários (2)

    Série sobre programação de Drivers no CodeProject

    Um programador de drivers com boa alma escreveu uma série de artigos sobre o assunto no Code Project. Aos interessados:

    Driver Development Part 1: Introduction to Drivers
    Driver Development Part 2: Introduction to Implementing IOCTLs
    Driver Development Part 3: Introduction to driver contexts
    Driver Development Part 4: Introduction to device stacks
    Driver Development Part 5: Introduction to the Transport Device Interface

    Destaque para a parte 5, que explica como fazer um cliente TDI. TDI (Transport Device Interface) é a interface usada por drivers para fazer comunicação via rede (equivalente ao WinSock, só que em kernel mode). Eu já fiz um filtro de TDI, e descobri da pior forma que TDI é complicado e tedioso. Não bastasse isso, a explicação da MSDN deixa bem a desejar...

    Em 10/05/2005 12:46 - Comentários (1)

    Os códigos que eu tenho que aguentar

    Em todas as empresas que eu já trabalhei, tive que dar manutenção em diversos sistemas, muitas vezes feitos por pessoas que nem trabalham mais na empresa. Eu já passei infinitas horas tentando entender o que foi feito, quase sempre em um código confuso e com nenhum comentário. Agora que eu estou trabalhando com otimização de performance em componentes já existentes, minha situação piorou. Eu preciso antes entender COMPLETAMENTE o que o componente faz, ao invés de entender somente a parte que eu preciso modificar.

    Meu amigo Gilberto já passou por mais empresas do que eu, porque ele aceitava [se sujeitava a] pegar projetos com 4 meses de duração. Depois de algumas discussões sobre os diversos estilos de código que encontramos durante nossa longa jornada (isso deveria ter uma música dos anos 60 como trilha sonora...), ele me apareceu no dia seguinte com essa pérola que vos mostro, entitulada "Os códigos que eu tenho que agüentar".

    Nos exemplos abaixo, programadores com diversos perfis (prático, metódico, enigmático, etc) se propõem a criar uma função que retorne o resultado de (2 + 2). Cada estilo de programador faz de uma forma diferente, como vemos abaixo:

    //
    // Prático
    //
    int soma()
    {
           return 2 + 2;
    }
    
    //
    // Enigimatico
    //
    int soma()
    {
           int a = 1;
           a = a << 1;
           a = (++a)++;
           return a;
    }
    
    //
    // Metódico
    //
    int soma3()
    {
           int a;
           int b;
           int c;
           a = 2;
           b = 2;
           c = a + b;
           return c;
    }
    
    //
    // Aprendeu orientação a objetos ontem
    //
    class cVal
    {
    private:
           int valorInterno;
    public:
           int GetValue()
           {
                   return valorInterno;
           };
           void SetValue(int i)
           {
                   valorInterno = i;
           };
           int Soma(cVal CV)
           {
                   return this->GetValue() + CV.GetValue();
           };
    };
    
    int soma()
    {
    
           cVal a,b;
           a.SetValue(2);
           b.SetValue(2);
           return a.Soma(b);
    }
    
    //
    
    // Macaco velho
    //
    int soma()
    {
           return 4;
    }
    
    //
    // Experiente
    //
    static const int Soma2_2 = 4; //2 + 2 = 4
    
    
    //
    // STL maníaco
    //
    #include <vector>
    #include <functional>
    #include <numeric>
    
    
    template<typename T>
    int supersoma(T p1, T p2)
    {
     typedef T _MyType;
     return std::plus<_MyType>()(p1,p2);
    }
    
    template<typename T>
    int supersoma2(T p1, T p2)
    {
     std::vector<T> v;
    
     v.reserve(2);
     v.push_back(p1);
     v.push_back(p2);
     return std::accumulate(v.begin(), v.end(), 0, std::plus<T>());
    }
    
    template<typename T, bool b>
    int supersoma3(T p1, T p2)
    {
    	return b ? supersoma<T>(p1,p2) : supersoma2<T>(p1,p2);
    }
    
    template<typename T, bool b>
    class SuperSoma4
    {
     public:
      int operator()(T p1, T p2)
      {
        return supersoma3<T,b>(p1,p2);
      }
    
    };
    
    static const bool qual_somal = false;
    
    int soma()
    {
     // Stroustrup e Alexandrescu sejam louvados
     return SuperSoma4<int, false>()(2,2);
    }

    Eu dei minha contribuição, com o exemplo "STL Maníaco". Se você conhece algum perfil que eu não mostrei, sinta-se à vontade para colocar um exemplo nos comentários.

    Em 11/05/2005 14:43 - Comentários (10)

    Alguém viu as especificações do XBOX360?

    É de babar, e de pensar quando eu vou ter um micro com esse poder no meu desktop (ou quantas semanas o pessoal do Linux vai demorar para portar e hackear). Olhem só alguns detalhes da máquina:

    Xbox 360 System Performance Specifications

    Custom IBM PowerPC-based CPU
    • Three symmetrical cores running at 3.2 GHz each
    • Two hardware threads per core; six hardware threads total
    • VMX-128 vector unit per core; three total
    • 128 VMX-128 registers per hardware thread
    • 1 MB L2 cache
    Memory
    • 512 MB of 700 MHz GDDR3 RAM
    • Unified memory architecture

    Algo me diz que todos os projetos de computação distribuída (inclusive o meu se eu tiver tempo) serão portados para o XBOX360, já que os videogames são mais baratos do que os computadores, e nesse caso específico mais poderoso que um PC comum. Isso também prova que os computadores poderiam ser mais baratos, já que um computador com essas especifícações custa muito mais que um videogame (eu duvido que esse XBOX vá custar mais do que US$ 399,99).

    Além disso, teremos um videogame multicore e com suporte a 6 threads simultâneas, o que fará que os desenvolvedores de jogos comecem a explorar o uso de múltiplas threads, o que não é feito hoje. Assim, como os jogos para XBOX são feitos em DirectX (C++ e COM) e "quase compatíveis" com um PC, os jogos feitos para aproveitar o poder do XBOX também aproveitarão o poder dos PCs com múltiplos processadores e com multicores/hyperthreading, o que acaba por melhorar a qualidade dos jogos para PC também. Atualmente ter dois processadores não melhora o desempenho da maioria dos jogos, já que eles são single threaded.

    Para mais detalhes, veja a especificação completa liberada pela Microsoft. O último videogame que eu tive foi um SuperNES, a muuuuito tempo atrás. Acho que esse eu vou ser obrigado a comprar...

    Em 13/05/2005 08:08 - Comentários (8)

    Problemas no carro? Instale o último Service Pack

    Nos Estados Unidos, a nova moda são os carros híbridos gasolina-eletricidade. Além de não ser necessário ligá-los na tomada (as baterias são carregadas com a energia cinética dos freios e pelo próprio motor a gasolina), eles economizam bastante gasolina e são potentes, já que o motor elétrico é ligado para "dar uma força" quando o carro exige mais potência.

    Mas esse mês os proprietários do modelo Toyota Prius tiveram um estranho problema: o motor era desligado quando o carro estava em alta velocidade. Em condições normais de uso, os motores são ligados e desligados dependendo da situação: em baixas velocidades, só o motor elétrico é usado, e quando o carro está parado o motor a gasolina chega a ser desligado. Quando a velocidade aumenta, o motor a gasolina assume a responsabilidade. E tudo isso é controlado por um software, que a Toyota também licenciou para as outras montadoras (li isso na Wired do mês passado). O problema do desligamento do motor em alta velocidade é devido a um bug nesse software, o que fez a fabricante convocar os seus donos para atualizarem o software, em uma das autorizadas. Ou seja: levar o carro à uma oficina para o mecânico instalar um patch, um service pack.

    Não bastasse os carros mais modernos estarem usando um computador de bordo (sujeitos a vírus), temos mais essa. E aquela piada de programador que fala que para o carro funcionar é só desligar e dar partida novamente está virando realidade...

    Em 16/05/2005 23:27 - Comentários (1)

    Não sou o único na contra mão do MsMarketing

    Pérola que eu li em um newsgroup um dia desses: "Não sei como, nos dias de hoje, ainda existem pessoas que escrevem uma nova aplicação Windows usando Win32 ou MFC ao invés de usar Windows Forms". Como eu sei que existe uma imensa massa de programadores que definitivamente não sabem o que falam, eu resolvi não discutir. Já ouvi tantas pessoas que acreditam que qualquer coisa pode ser feita em .NET, que hoje em dia eu prefiro não discutir e continuar com o meu Visual C++.

    O primeiro problema disso é a definição de conceito e o entendimento da arquitetura das coisas. Falar que não usar Windows Forms não faz sentido é completamente sem sentido, já que o Windows Forms não passa de um wrapper sobre a Win32 API. Além disso, a MFC não passa de outro wrapper para a Win32 API, só que com mais de 10 anos de mercado e MUITO mais maduro do que o Windows Forms (que ainda tem vários bugs). Eu sempre achei o Windows Forms aquém das minhas expectativas para desenvolvimento de aplicativos Win32, mas acredito que a escolha da ferramenta sempre deve ser feita com calma e pesando todos os fatores, como experiência, tempo para desenvolvimento e alcance do aplicativo. É inviável fazer um aplicativo para download em .NET, já que só a runtime dá uns 25 MBs. (Não me venha com esse papo de que isso está melhorando, que mais pessoas tem o Framework instalado, etc. A grande maioria da base instalada não tem Framework e nem sabe o que é isso).

    Semana passada encontrei mais algumas pessoas que compartilham da minha opinião. A primeira é a equipe que desenvolveu o newsreader Sauce. Ele foi totalmente desenvolvido em .NET, como muitos dos leitores RSS que existem hoje. Depois de não conseguir fazer o aplicativo ficar mais rápido e consumir menos memória, eles decidiram abandonar o .NET e refazer em "old unmanaged code". A nova versão ainda está em beta, mas é bem mais rápida do que a versão .NET e consome metade da memória. Fiz algumas medições entre a versão .NET e nativa (feita em C++ Builder, Delphi ou Kylix), e como dados concretos valem mais do que c palavras, vamos a eles:

    Tempo de inicialização
    .NETNativo
    366
    252
    121
    165
    Consumo de memória (Private Bytes)
    .NETNativo
    45.492 KB24.144 KB

    O argumento que todos os .NETers sacam assim que são "atacados" é que a aplicação usada não foi bem escrita, que não segue os best practices de alocação de memória, não usa corretamente IDisposable, bla, bla, bla. Se é tão difícil assim fazer uma aplicação com boa performance e consumo de memória razoável em .NET, é melhor continuar usando C++. Pelo menos em C++ a regra de alocação/desalocação de memória é clara, e não depende de saber quem precisa ou não de Dispose(). Além disso, tem o argumento barato de que as máquinas vão ficar mais rápidas, que a memória vai ficar barata, que o Bush vai recobrar a sanidade algum dia e que em um futuro não muito distante todos os micros terão Framework instalado. Eu desenvolvo aplicativos hoje, para clientes de hoje, que têm máquinas de hoje. Eu acho que o Windows Forms vai ficar muito melhor no futuro (Avalon), mas não posso viver lidando com coisas em fase experimental e SOs que ainda estão em alpha. Quem já tem experiência no mercado sabe que a migração para o Longhorn - ou a instalação da runtime do Avalon em todas as máquinas - deve demorar alguns anos.

    Outra pessoa que compartilha da minha opinião chama-se Mark Russinovich. Para quem vive em outro mundo e não o conhece, ele além de escrever os programas do site SysInternals é o escritor do livro Windows Internals, a bíblia sagrada da arquitetura Windows. Ele escreveu um post sobre isso, e como esperado, foi impiedosamente atacado pelo cardume de Piranhas.NET. Devido ao grande número de comentários negativos, ele explicou novamente o que já tinha explicado e as Piranhas.NET não tinham lido. Compartilho da mesma opinião: .NET é bom para componentes e maravilhoso para Web (ASP.NET é muito bom). Mas Windows Forms aumenta muito o consumo de CPU e memória sem trazer grandes vantagens (traz até alguns problemas). E wrapper por wrapper eu prefiro meu WTL. :-)

    Em 23/05/2005 14:31 - Comentários (27)

    Tem gente que leva isso a sério demais...

    Em 25/05/2005 19:38 - Comentários (4)

    O outro lado da moeda

    Um usuário Linux fez uma avaliação do Windows XP Home, e chegou a conclusão que ele está quase bom para o uso em desktops. Apesar de ter sido colocado na sessão de humor do site, todas as críticas e considerações que ele faz têm fundamento, e mostra que em alguns pontos o Linux com KDE ou Gnome é bem melhor do que o Windows. Eu achei interessante pelo fato da comparação estar considerando como base o Linux, e não o Windows. E o melhor de tudo: o "avaliador" soube elogiar os pontos fortes do Windows, sem simplesmente falar mal como faz a "banda podre" do Linux.

    Em 28/05/2005 15:43 - Comentários (10)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: Win32

    Outros posts dessa mesma série:   ATL     COM     MFC

    Win32 API: API (Application Programming Interface) das versões do Windows que são 32 bits (95, 98, 98SE, Millenium, NT, 2000, XP, 2003, Longhorn, etc). Nada mais é do que as funções que o sistema operacional exporta para serem usadas pelas aplicações. Cada sistema operacional tem a sua API, e a runtime do C e C++ é implementada em cada plataforma usando essa API. Por exemplo, o malloc da linguagem C acaba chamando HeapAlloc ou VirtualAlloc da API. Para mais detalhes, veja a documentação na MSDN. Código fonte sempre vale mais do que h palavras:

    #define UNICODE
    #define _UNICODE
    #define WIN32
    #define _WINDOWS
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    //
    // programa Win32 tem como entry point (função inicial) a função WinMain
    //
    int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
    {
      WCHAR* wzBuffer;
      HANDLE hHeap;
      
      hHeap = GetProcessHeap();
    
      //
      // usando a função HeapAlloc da API para alocar memória
      //
      wzBuffer = (WCHAR*) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 256);
    
      //
      // wsprintf também é da API
      //
      wsprintf(wzBuffer, L"TickCount: %d", GetTickCount());
    
      MessageBox(NULL, wzBuffer, L"Mensagem", MB_OK);
    
      //
      // se eu usasse ATL eu não precisava fazer isso...
      //
      HeapFree(hHeap, NULL, wzBuffer);
    
      return 0;
    }
    

    Para mais informações sobre Win32, veja o meu post "FAQ: Programação Win32 em C/C++"

    Se você não tiver o Visual C++, você pode fazer download do Microsoft Visual C++ Toolkit Compiler e do Microsoft Platform SDK. A compilação em linha de comando fica assim:

    C:Temp>cl win.cpp /link user32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
    
    win.cpp
    Microsoft (R) Incremental Linker Version 6.00.8447
    Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
    
    /out:win.exe
    user32.lib
    win.obj
    

    Depois ainda teremos COM, ATL, WTL e MFC. Alguém tem mais alguma sugestão?

    Em 31/05/2005 10:10 - Comentários (7)

    Mega-super-ultra liquidação de livros importados na Pearson

    Interessado em comprar um livro de programação importado e pagar uma mixaria? Esse fim de semana (4 e 5 de junho de 2005) vai haver uma liquidação de livros importados na Pearson (ex Makron), com livros que já estão encalhados faz algum tempo. E o bom é que nas duas listas dos encalhados (aqui e aqui) estão, entre outros, os clássicos The Design and Evolution of C++, Essential COM e o Effective STL. Não vou entrar no mérito do motivo que faz livros bons como esses estarem encalhados em uma editora, o importante é ter a oportunidade de comprar essas pérolas a preço de banana. Essa liquidação começa sábado, dia 4 de junho, as 10 da manhã, e eu pretendo estar na porta da editora 9:30.

    Estou escrevendo esse post propositalmente no início da madrugada de sábado, para que haja menos probabilidade de eu ter que sair no tapa com alguém por um único exemplar do "Design and Evolution of C++". Eu sou pregador da disseminação e multiplicação do conhecimento, mas quando eu acho clássicos como esses por 20 reais, eu não resisto e ligo o "evit bit" :-) Além disso, seria interessante saber se alguém lê meu blog aos fins de semana, ou se o acesso fora do horário comercial é feito somente por pessoas que conhecem minha fama internacional como especialista em placenta baixa...

    [Atualização (05/06): Foi uma porcaria. Eu marquei uns 20 livros da lista, só consegui comprar o "Essencial COM". O pessoal da editora aceitou encomendas de pessoas de outros estados e venderam muitos livros ANTES de começar o feirão. Eu já fiz minha reclamação... Ah, e eu descobri que existem pessoas que lêem o blog (leia-se "abrem o RSS reader") nos fins de semana. :-)]

    Em 04/06/2005 00:00 - Comentários (4)

    C++ com sanidade: resolvendo o bug usando Boost

    Seguindo a série de possíveis correções para o bug da cópia de um objeto que contém um ponteiro, sugiro agora a correção que eu acho mais fácil e apropriada: usar o shared_ptr do Boost. Essa proposta segue a filosofia C++: fazer tudo com o máximo de simplicidade e reusando as bibliotecas existentes ao máximo.

    Para quem não conhece, o Boost é um conjunto de bibliotecas (a maioria de templates) para C++, que é vista pelos usuários como um extensão da biblioteca padrão (namespace std). Essas bibliotecas vão de smart pointers à regular expression, de algoritmos para manipulação de strings à templates metaprogramming. Algumas das bibliotecas do Boost (acho que 10) serão adicionadas ao próximo padrão C++, nomeado provisóriamente como C++0x (0x será o ano de publicação, o último padrão é o C++98).

    Como todos já sabem, um trecho de código sempre vale mais do que 100.000.000.000 palavras:

    #include <iostream>
    #include <boostshared_ptr.hpp>
    
    //
    // costumes Win32...
    //
    #ifndef DWORD
    #define DWORD unsigned int
    #endif
    
    
    class CTest1
    {
    public:
       CTest1() : dwValue(0)
       {}
    
       ~CTest1()
       {
          // só para termos certeza do que está acontecendo...
          std::cout << "CTest1::~CTest1() | " << (void*)this << std::endl;
       }
    
       DWORD dwValue;
       std::string strValue;
    };
    
    
    
    class CTest2
    {
    private:
       //
       // transformando o ponteiro em um shared_ptr resolve o bug
       //
       boost::shared_ptr<CTest1> m_pTest1;
    public:
    
       CTest2() 
          : m_pTest1(new CTest1)
       {}
    
    
       //
       // Não preciso um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
    
       CTest2(DWORD dw, const std::string& str)
          : m_pTest1(new CTest1)
       {
          m_pTest1->dwValue = dw;
          m_pTest1->strValue = str;
       }
    
       ~CTest2()
       {
          std::cout << "CTest2::~CTest2() | " << (void*)this << std::endl;
       }
    
       //
       // essa versão é a mais recomendada
       //
       const boost::shared_ptr<CTest1>& GetTest1()
       {
          return m_pTest1;
       }
    
       //
       // vamos renomear. Eu poderia passar o ponteiro diretamente,
       // mas prefiro seguir as regras de ownership do shared_ptr
       // Isso chama-se "safe programming", mais do que possível em C++
       //
       CTest1* GetTest1_old() const
       {
          return m_pTest1.get();
       }
    
    };
    
    int main(int argc, char* argv[])
    {
       std::vector<boost::shared_ptr<CTest2> > vecTest2;
    
       boost::shared_ptr<CTest2> t1(new CTest2), t2(new CTest2(100,"putz grila"));
    
       //
       // agora nossas variáveis são ponteiros
       //
       t1->GetTest1()->dwValue = 50;
       t1->GetTest1()->strValue = "baba";
    
       //
       // vamos criar um objeto e fazer uma cópia 
       // do CONTEÚDO de um dos ponteiros
       //
       CTest2 tv3 = *t1;
    
       //
       // chamando operator=, copiando o valor
       //
       *t2 = tv3;
    
       //
       // vamos atribuir um ponteiro ao outro.
       // Note que como usamos um shared_ptr, o conteúdo do ponteiro t1 será
       // automaticamente liberado, e não haverá leak
       // "olhe mamãe, sem o garbage collector"
       //
       t1 = t2;
       
       //
       // vamos colocar um t1 no vetor. Isso coloca o ponteiro,
       // e não faz cópia do objeto
       //
       vecTest2.push_back(t1);
    
       //
       // vamos colocar t2, que é o mesmo ponteiro de t1
       //
       vecTest2.push_back(t2);
    
       BOOST_ASSERT(t1 == t2);
       BOOST_ASSERT(t1.get() == t2.get());
       BOOST_ASSERT(vecTest2[0] == vecTest2[1]);
    
       //
       // agora vamos pegá-lo e ver o seu valor
       //
       std::cout << "dwValue = "  << vecTest2[0]->GetTest1()->dwValue  << std::endl;
       std::cout << "strValue = " << vecTest2[0]->GetTest1()->strValue << std::endl;
    
       return 0;
    }
    

    Outros post da série:
    Tinha um bug no caminho, no caminho tinha um bug
    Por que o bug?
    Resolvendo o bug usando um smart pointer feito em casa

    Em 06/06/2005 08:11 - Comentários (0)

    Troca-troca de arquitetura

    Primeiro a Microsoft troca a arquitetura do XBOX para o IBM PowerPC. Agora, durante uma apresentação no Apple's Worldwide Developer Conference, Steve Jobs anuncia que a próxima geração de Macs usará plataforma Intel. Sim, a Apple vai usar processadores Intel. Quando eu vi os boatos semana passada, achei que poderiam ser infundados, mas agora está confirmado. Além dessa grande mudança, Steve Jobs ainda anunciou que um software da Apple (chamado Rosetta) vai permitir que os programas compilados para PowerPC rodem no Intel sem maiores problemas. Como sempre, toda revolução vem acompanhado de algum software...

    Como esperado, não será possível usar oficialmente o MacOS X como qualquer PC "made in Santa Efigênia", pois isso seria um tiro na cultura Apple (Oficialmente não será possível, mas quantas semanas vai levar pra alguém resolver isso?). Eu li que algum lugar que a diferença da Apple para as outras empresas é que ela não vende hardware nem software, ela vende computadores. Ela vende algo que você tira da caixa e liga na tomada, como você faz com um eletrodoméstico. E isso faz realmente sentido, e ela prova que esse enfoque funciona. É mais fácil para os leigos de plantão colocarem um iMac para funcionar do que programar um vídeo cassete. Quando passar alguns anos eles lançam um micro com um design novo e todo mundo troca, igual fazemos com eletrodomésticos. A estratégia deles é muito interessante.

    Além desse fato, o pessoal da Debian anunciou que, depois de 3 anos de desenvolvimento, a versão Stable do Sarge (3.1) está finalmente pronta e disponível para download. Só falta agora a 3DRealms anunciar o lançamento do Duke Nukem Forever...

    Em 06/06/2005 17:08 - Comentários (8)

    Windows vendido a US$ 1

    A Microsoft acabou de fechar um acordo com o governo da Indonésia para que ele pague US$ 1,00 por cópia de Windows pirata instalado. Isso parece uma multa baixa, mas na realidade essas cópias passarão a ser legalizadas (não sujeitas à fiscalização de pirataria), o que transforma o acordo em uma venda de licenças de Windows a US$ 1,00. A única condição é que o governo começe a comprar software legalizado daqui pra frente. O governo brasileiro precisa descobrir o que fazer para conseguir esse preço também...

    Em 10/06/2005 08:16 - Comentários (0)

    Eu não visto a camisa da empresa

    Acho que todo mundo já deve ter escutado (ou até usado) a expressão "vestir a camisa da empresa". Isso geralmente é o que o seu gerente diz quando ele quer que você faça hora extra de graça no fim de semana. É uma maneira usada para te pressionar, como que dizendo que, já que você não se preocupa com a empresa, ela também não vai se preocupar com você. Mas tem um detalhe importante que ele não percebe: a sua empresa já não se preocupa com você.

    Eu comecei a trabalhar na área relativamente cedo. Com 15 anos eu era monitor da finada Data Control, e com 16 anos eu estava trabalhando de diagramador para um jornal de uma cidade pequena. Depois disso, já com 17 anos, eu comecei a fazer softwares e arrumar computadores. Hoje eu sei que isso foi muito bom para mim, pois eu já comecei a ser enganado desde cedo. Além de acreditar nas promessas que me faziam e nessa estória de "vestir a camisa da empresa", eu trabalhei muito de graça para pessoas que se aproveitavam da minha total falta de experiência com negociações. Hoje eu vejo que isso não tem nada a ver com a idade, porque eu conheço vários programadores com quase 30 anos que têm o mesmo problema. E vejo também profissionais de outras áreas também passarem pelo mesmo problema, inclusive o de trabalhar de graça quando esse não era o combinado.

    Uma das coisas que eu sempre digo é que salário não é, e nunca foi sinônimo de competência. O sujeito que ganha mais é aquele que melhor sabe negociar seu salário e que cede menos às pressões para que seu salário seja abaixado. Existem inúmeras pessoas que reclamam que "eu programo melhor do que ele e ele ganha mais do que eu". Infelizmente é exatamente assim que as coisas funcionam, porque as empresas só valorizam as pessoas que tem um bom conhecimento técnico quando elas mesmas começam a se valorizar, e não se rebaixam mais a trabalhar por salário baixo. Para muitas empresas não existe um salário para cada competência, existe o mínimo possível que a empresa consegue te pagar. Quem já fez entrevista ou trabalhou em várias consultorias (as grandes inclusive) sabe bem o que estou falando.

    Eu não visto a camisa de empresa nenhuma pelo simples fato de que nenhuma empresa até hoje vestiu a minha camisa. E olha que eu não peço muito, só que me deixem trabalhar em paz. É tão simples deixar um programador feliz... Dê-lhe uma máquina boa, pague-o em dia conforme o combinado, e o deixe trabalhar. Espero que algum dia as empresas percebam (com algumas grandes já perceberam) a quantidade de talentos e de produtividade que eles jogam fora por causa da forma que eles tratam os programadores (e funcionários em geral). Em duas empresas que eu trabalhei acontecia sempre a mesma coisa: de dois em dois anos pelo menos metade dos programadores pediam demissão. Profissionais muito bons, que saiam da empresa porque não tinham a mínima condição de trabalho. Será que os donos eram tão burros a ponto de não perceber a quantidade de dinheiro e potencial que eles estavam perdendo?

    A primeira coisa que eu pergunto para os funcionários de uma empresa quando eu entro: "Vão me deixar trabalhar? Ou eu vou ter que ficar lidando com briguinhas políticas ridículas e babaquices afins?". Sou, como eu disse, igual à grande maioria dos programadores, a única coisa que eu peço é poder trabalhar em paz (Como eu estou conseguindo fazer no meu emprego atual. Por isso que estou trabalhando aqui até hoje, mesmo sabendo que a APINFO está cheia de oportunidades.).

    Esse jeito de tratar profissionais tem um nome, cunhado pelo meu sábio amigo/sócio Gilberto: "Sindrome do super-administrador". Muitos administradores de empresa se acham mais competentes a medida que eles conseguem extrair o máximo de trabalho dos funcionários pagando o MSP ("menor salário possível"). Eu conheço várias empresas que, se pudessem, descontariam dos funcionários a CPMF retida no pagamento dos salários. Se você acha que eu estou exagerando, você provavelmente não tem muita experiência na área ou é muito sortudo. A boa notícia é que a medida que você troca de empresas e se depara com super-administradores, você aprende a lidar com eles e se esquivar dos truques mais comuns.

    PS: Antes que alguém pergunte, eu não sou comunista, sou um capitalista convicto e presto serviço para bolsa de valores com muito orgulho. Eu só acho que, mediante a tudo que eu escrevi, dá para entender porque as empresas de software brasileiras são pequenas e muitas vezes inexpressivas em nichos de mercado que tem potencial de crescimento de 20% ao ano...

    PS2: Alguém pode me dizer o que quer dizer esse "PS" que usamos nas observações?

    Em 13/06/2005 09:01 - Comentários (44)

    Acessando os newsgroups brasileiros da Microsoft de uma forma que funciona

    O antigo fórum da MSDN brasileira foi incorporado à infraestrutura de newsgroups da Microsoft Corp, e agora usa a mesma interface web, além de suportar o protocolo NNTP. Isso é muito bom, já que a responsabilidade de manter isso rodando deixa de ser da Microsoft brasileira, que é muito ocupada tentando empurrar .NET para as empresas a todo custo, e tem dificuldades até para manter um site no ar. O problema é que, além de muitas pessoas trabalharem em lugares onde o NNTP é bloqueado, a interface web do news da Microsoft é incompatível com Firefox (as palavras acentuadas ficam trocadas).

    A solução é usar o Google Groups. De lá você pode ler e responder os posts, receber as mensagens ou o resumo por e-mail (como em listas de dicussão), além de todas as facilidades de busca que o Google provê.

    Em 17/06/2005 16:20 - Comentários (6)

    Os contos que as empresas contam

    Eu já trabalhei em várias empresas e consultorias, e fiz entrevistas em tantas outras. Em algumas empresas que eu trabalhei me prometeram coisas que não cumpriram. Além disso, muitas das empresas onde fiz entrevista tentaram me fazer as mesmas propostas, que eu tinha certeza que não seriam cumpridas. Achei interessante mostrar aqui alguns desses "truques" usados pelas empresas para levar vantagem na negociação, e torço para que isso ajude alguns programadores/consultores a não passarem pelas mesmas situações que eu e alguns amigos passamos.

    Como um trecho de código vale mais do que... ops, sem código hoje. Vamos então aos truques mais conhecidos:

    O velho truque dos três meses: Esse é um dos truques mais velhos e conhecidos. Você pede um salário de R$ 3500,00 por mês. Aí, já quando tudo parece estar combinado e fechado, a pessoa que te contrata diz: "Então vamos fazer assim, eu te pago R$ 3000,00 nos primeiros três meses, enquanto a gente se conhece, e depois eu começo a te pagar os R$ 3500,00". E você, apesar de contrariado, aceita, já que quer mesmo esse emprego. Só que depois do terceiro mês, ao conferir seus rendimentos, você vê que o valor combinado (que eles chamam de aumento) não foi depositado, você recebeu só os mesmos R$ 3000,00. Você passa uma semana tentando encontrar alguém para reclamar, e quando acha, essa pessoa diz que foi um erro do pessoal da contabilidade e que isso não vai se repetir no próximo mês. Mas nesse mês as coisas ficarão desse jeito.

    Por ser detalhada, essa história pode parecer uma experiência pessoal minha, mas não é. Conheço algumas pessoas que tiveram exatamente o mesmo problema em empresas diferentes. Não caia no velho truque dos três meses, não aceite propostas para entrar por um salário mais baixo e aumentar depois. A empresa que propõe isso mostra não ser séria e você corre o grande risco de não receber a quantia que pediu. Seja duro nas negociações, não esqueça que a empresa precisa do seu trabalho. Você não está pedindo um emprego, a empresa está pedindo para você trabalhar para ela por causa das suas qualificações.

    "Oportunidade de cargo de liderança": Essa é a preferida das meninas de RH de consultoria. Esse é o argumento mais usado quando elas querem te colocar em uma barca furada, como trabalhar em um lugar que fica a 100 km da sua casa. Quando elas já esgotaram todos os argumentos, elas falam que "eu sei que é longe, mas e se existir uma possibilidade de você ser colocado em um cargo de liderança? Isso será muito bom para o seu curriculum". E quando você pergunta de quanto é essa possibilidade, ela te diz que a possibilidade existe, mas não pode garantir nada. Talvez isso seja verdade, e você vire o líder da área de meta-estatística tecnológica, que é a única área da empresa que só tem um funcionário - você! Olha que bom, você será líder de você mesmo, pelo menos você terá 100% de aprovação dos seus subordinados... :-)

    Esse truque nunca colou comigo, porque eu prefiro programar do que pegar um cargo de liderança em uma empresa que eu não conheço. E nenhuma empresa que presta coloca um programador sem experiência em liderança para liderar uma equipe. Mas eu já vi esse truque funcionar com várias pessoas que queriam experimentar o gostinho de mandar nos outros ao invés de programar alguma coisa. E já vi vários cargos serem inventados para que muitas pessoas fossem líderes ou gerentes de alguma coisa.

    Oportunidade de crescer junto com a empresa: Esse também é um argumento bastante usado para te pagar um salário baixo. O contratante te diz que a empresa está em franco crescimento, mas que não pode te pagar um salário bom agora. Mas ele diz que, se você aceitar esse salário ridículo, você terá a oportunidade única de acompanhar o crescimento da empresa e crescer junto com ela. Mas quando você pergunta de prazos para aumentos, ele repete que você "estará crescendo junto com a empresa". E ainda fala que o crescimento da empresa será proporcional ao seu esforço - que, é claro, nunca será suficiente. Pense bem: você apostaria no crescimento de uma empresa que você nem conhece? Se você acha que vale a pena, aceite a proposta, mas saiba bem os riscos que você está correndo. Eu não costumo aceitar esse tipo de coisa porque eu acho que, se a empresa quer crescer, ela deve investir. E um dos investimentos necessários é contratar profissionais bons e experientes, coisa que não custa barato. Não consigo acreditar no crescimento de uma empresa que faz de tudo para pagar MSP ("menor salário possível").

    Passei alguns dias tentando me lembrar de outros truques, mas não me lembrei de nenhum outro que fosse tão importante quanto esses (talvez o de contratar como estagiário, mas não acho que tenha tenta relevância para quem já está a algum tempo no mercado). Isso prova como o arsenal de truques das empresas é limitado, e como as pessoas caem sempre nas mesmas histórias. Já sabe: nada de três meses de experiência (você já tem vários), nada de cargo de liderança (você é um programador, lembra?) e nada de crescer junto com uma empresa que não quer investir em você. Valorize-se e fuja dos super-administradores.

    Em 20/06/2005 22:57 - Comentários (34)

    Google Maps: agora eu vejo minha casa!

    O Google Maps agora também tem fotos de satélite do Brasil! Apesar de não mostrar direções e endereços, ainda assim é divertido procurar pontos conhecidos no mapa do satélite. Você pode entrar em maps.google.com e digitar "São Paulo", que você vai cair em um ponto entre o Rio Tietê e o Centro da cidade. Depois é só arrastar o mapa com o mouse e ir ajustando o zoom na barra que aparece no canto superior esquerdo do mapa.

    Alguns pontos interessantes:

    Em 23/06/2005 14:11 - Comentários (9)

    Resolvendo o bug usando CComPtr

    Eu resolvi o bug de diversas maneiras até agora. Eu mudei o código para contorná-lo, criei um smart pointer feito em casa e usei o boost::shared_ptr (melhor solução até agora). Agora eu proponho mais do que uma solução, proponho um re-arquitetura: transformar todos os objetos em objetos COM. Isso resolve nosso problema, porque em COM todos os objetos são acessados através de ponteiros para interfaces, e as regras para gerenciamento de tempo de vida desses ponteiros são bem claras. Esses objetos, apesar de serem "objetos COM", não estão disponíveis para a runtime do COM como de costume, são todos objetos privados. Futuramente, poderemos facilmente exportar esses componentes em uma DLL, para que eles sejam acessíveis através de outras ferramentas que suportam COM, como VB, Delphi ou mesmo .NET.

    Nesse exemplo, eu segui as regras básicas do COM: todas as funcionalidades são acessíveis através de interfaces, todos os objetos herdam e implementam IUnknown e o tempo de vida de um objeto é gerenciado por chamadas a IUnknown->AddRef() e IUnknown->Release(). Para implementar IUnknown e gerenciar o tempo de vida dos objetos eu optei por usar ATL, por tornar a implementação bem mais simples.

    Vamos ao código, ele vale muito:

    #define UNICODE
    #define _UNICODE
    
    // "descomente" isso se usar um projeto do VC com "precompiled header"
    // #include "stdafx.h"
    
    
    //
    // só precisamos disso para usar ATL
    //
    #include <atlbase.h>
    
    ATL::CComModule _Module;
    
    #include <atlcom.h>
    
    
    //
    // isso evita erros na conversão de ponteiros COM
    // eu expliquei isso em http://www.1bit.com.br/content.1bit/weblog/cpp_comma_op
    //
    #define IC(pp) (static_cast<IUnknown*>(*pp),(void**)pp)
    
    //
    // Essa será nossa interface. Antes que você pergunte,
    // SIM, C++ TEM SUPORTE A INTERFACES
    // é só definir todos os métodos como virtual e colocar um "=0"
    // no final para dizer que não é implementado
    //
    struct __declspec(uuid("D0C2F56E-704E-45ce-B6F8-7E9D0F7F8723"))
    ITest1 : public IUnknown  // toda interface COM herda de IUnknown
    {
    	virtual HRESULT get_dw(DWORD* pdw) =0;
    	virtual HRESULT set_dw(DWORD dw) =0;
    	virtual HRESULT set_bstrValue(BSTR str) =0;
    	virtual HRESULT get_bstrValue(BSTR* pbstr) =0;
    };
    
    
    class CTest1 : 
    	public CComObjectRootEx<CComSingleThreadModel>, // implementação ATL de IUnknown
    	public ITest1
    {
    private:
    	DWORD m_dwValue;
    	//
    	// nossa string agora será um BSTR. Vamos usar CComBSTR para não
    	// nos preocuparmos com gerenciamento de memória
    	//
        CComBSTR m_bstrValue;
    	
    public:
    	
    	CTest1() : m_dwValue(0)
    	{}
    	
    	//
    	// Para fazer um objeto COM o ATL só precisa que você herde de
    	// CComObjectRootEx (ou similares) e coloque um mapa de interfaces
    	//
    	BEGIN_COM_MAP(CTest1)
    		COM_INTERFACE_ENTRY(ITest1)
    	END_COM_MAP()
    	
    	
    	//
    	//  implementação de ITest1
    	//
    	HRESULT get_dw(DWORD* pdw)
    	{
    		if(!pdw)
    			return E_POINTER;
    		
    		*pdw = m_dwValue;
    		
    		return S_OK;
    	}
    	
    	HRESULT set_dw(DWORD dw)
    	{
    		m_dwValue = dw;
    		return S_OK;
    	}
    	
    	HRESULT set_bstrValue(BSTR str)
    	{
    		m_bstrValue = str;
    		
    		return S_OK;
    	}
    	
    	HRESULT get_bstrValue(BSTR* pbstr)
    	{
    		if(!pbstr)
    			return E_POINTER;
    		
    		*pbstr = m_bstrValue.Copy();
    		
    		return S_OK;
    	}
    
    	//
    	// vamos facilitar nossa vida
    	//
    	typedef ATL::CComCreator<CComObject<CTest1> > Creator;
    };
    
    
    //
    // Interface ITest2
    //
    struct __declspec(uuid("7BA53C86-5B50-4b69-ACC4-652E60FE2FC9"))
    ITest2 : public IUnknown  
    {
    	virtual HRESULT Init(DWORD dw, BSTR str) =0;
    	virtual HRESULT GetTest1(ITest1** ppTest1) =0;
    };
    
    
    class CTest2 : 	
    	public CComObjectRootEx<CComSingleThreadModel>, // implementação ATL de IUnknown
    	public ITest2
    {
    private:
    	//
    	// vamos usar o CComPtr para não nos preocuparmos com
    	// gerenciamento de tempo de vida do objeto COM
    	//
    	CComPtr<ITest1> m_pTest1;
    public:
    
    	BEGIN_COM_MAP(CTest2)
    		COM_INTERFACE_ENTRY(ITest2)
    	END_COM_MAP()
    
       //
       // Não preciso um destrutor para desalocar o ponteiro.
       // E nem precisei usar um linguagem mais limitada ou uma runtime lenta
       //
       
       //
       // implementação de ITest2
       //
       
       HRESULT Init(DWORD dw, BSTR str)
       {
    	   HRESULT hr;
    
    	   hr = CTest1::Creator::CreateInstance(NULL, __uuidof(ITest1), IC(&m_pTest1));
    	   
    	   if(FAILED(hr))
    		   return hr;
    
    	   m_pTest1->set_dw(dw);
    	   m_pTest1->set_bstrValue(str);
    
    	   return S_OK;
       }
    
       HRESULT GetTest1(ITest1** ppTest1)
       {
    	   *ppTest1 = NULL;
    
    	   if(m_pTest1.p == NULL)
    		   return E_UNEXPECTED;
    
    	   return m_pTest1.CopyTo(ppTest1);
       }
    
       typedef ATL::CComCreator<CComObject<CTest2> > Creator;
    };
    
    int main(int argc, char* argv[])
    {
    	CComPtr<ITest1> pTest1;
    	CComPtr<ITest2> pTest2;
    	CComBSTR bstr;
    	DWORD dw;
    
    	CTest1::Creator::CreateInstance(NULL, __uuidof(ITest1), IC(&pTest1));
    	CTest2::Creator::CreateInstance(NULL, __uuidof(ITest2), IC(&pTest2));
    
    	bstr = L"Oi mamãe, eu sou uma string";
    	dw = 10;
    
    	pTest1->set_bstrValue(bstr);
    	pTest1->set_dw(dw);
    
    	pTest2->Init(dw, bstr);
    
    	//
    	// se você não fizer isso antes de reusar a variável para um parâmetro
    	// OUT, o CComPtr vai disparar um ASSERT, pq isso criaria um leak.
    	// Quando você atribui NULL à um CComPtr é o equivalente do VB 
    	// a "Set p = Nothing" (libera o objeto)
    	//
    	pTest1 = NULL;
    
    	pTest2->GetTest1(&pTest1);
    
    	//
    	// o mesmo problema com o CComBSTR. Precisamos liberá-lo antes de reusar
    	// para um parâmetro OUT. Ok, ainda tem que fazer algum gerenciamento de 
    	// memória manualmente.
    	//
    	bstr.Empty();
    
    	pTest1->get_bstrValue(&bstr);
    	pTest1->get_dw(&dw);
    	
    	return 0;
    }

    Para compilar esse projeto, não crie um projeto ATL no Visual C++, senão você terá uma DLL. Crie um projeto "Win32 Console Application", ou compile isso em linha de comando com o Visual C++ Toolkit.

    Em 27/06/2005 10:00 - Comentários (3)

    Onde está seu Garbage Collector agora?

    Você achava que usando .NET nunca mais teria problemas com leaks? Veja isso:

    GC incorrectly handles large freed objects

    All Windows Forms that leak two GDI handles

    NumericUpDown and DomainUpDown leak

    Massive GDI-Leak in ListView in Windows-Forms (1.1SP1)

    Em 30/06/2005 13:30 - Comentários (9)

    FAQ: Começando a programar em C++

    Todas as semanas eu recebo a mesma pergunta pelo formulário de contato: "Quero começar a programar em C++ mas não sei como". Eu não fico aborrecido com isso, pelo contrário, fico bastante feliz. É muito bom saber que uma quantidade considerável de pessoas quer evoluir e passar para o lado poderoso da força. :-)

    Eu pretendo montar, com vários posts, algo que possa ser agrupado em um FAQ para ajudar essas pessoas a se converterem. Quero que esse movimento se transforme numa conversão em massa dos programadores para C++, mostrando a luz às pessoas que estão limitadas dentro de uma vida onde não se pode gerenciar sua própria memória. E assim, mudar meu país para melhor!

    (Se você costuma ler o que eu escrevo já sabe que não pode me levar muito a sério. Mas, como diz o Alfred, o mais fácil é fazer como a Globo: explique tudo detalhadamente e não deixe nada nas entrelinhas, alguém pode não entender. Então, lá vai: Isso foi uma brincadeira.)

    Quero me converter para o lado poderoso da força, mas essa história de C e C++ me confunde. O que é o que?
    A linguagem C foi criada no início dos anos 70 para ser usada na programação do UNIX. Cansados de fazer tudo em Assembly, os programadores resolveram criar uma linguagem que fosse estruturada e que permitisse programação low-level ao mesmo tempo. Assim nasceu a linguagem C.

    /*
     Isso está em C
    */
    
    #include <stdio.h>
    
    int main()
    {
      int a = 15;
      int b = 20;
      char buffer[128]; /* uma string é um array (ou vetor) de bytes terminado por ASCII zero */
    
      strcpy(buffer, "A variável [a] é ");
    
      if(a > b)
        strcat(buffer, "maior");
      else
        strcat(buffer, "igual ou menor ");
    
      strcat(buffer, " que a variável [b]\r\n");
     
      printf(buffer);
    
      return 0;
    }
    

    O C++ é uma evolução da linguagem C, e foi criada por Bjarne Stroustrup. Nessa evolução foi adicionado à linguagem C o conceito de orientação à objetos, que virou moda naquela época (começo dos anos 80) e é muito usado hoje em dia. Além disso, o próprio C++ foi evoluindo no decorrer da década de 80 e 90, com a adição de recursos como templates e a STL (que veremos depois). O C++ tem tudo que a linguagem C tem (99,8% de compatibilidade) e mais toda a evolução. Todo compilador C++ que eu conheço compila código C sem problemas.

    //
    // Isso está em C++
    //
    
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      int a = 15;
      int b = 20;
      string buffer; // string é um objeto
    
      buffer = "A variável [a] é ";
    
      if(a > b)
        buffer += "maior";
      else
        buffer += "igual ou menor ";
    
      buffer += " que a variável [b]\r\n";
     
      cout << buffer;
    
      return 0;
    }

    O exemplo feito em C, é compilado sem problemas por qualquer compilador C++, já que ele é também um código C++ válido.

    Preciso aprender C antes de aprender C++?
    Não, não e não. Eu recomendo que você aprenda C++ direto, sem passar pelo C, já que hoje em dia o C++ é mais usado. Se algum dia você precisar fazer algo em C, é só estudar as limitações do C em relação ao C++. As estruturas básicas de controle (if, while, switch...) são as mesmas, o que muda é que a linguagem C não suporta todos os recursos do C++, com suporte à programação orientada a objetos, bibliotecas, templates, etc. Mesmo assim, em C++ você pode usar as bibliotecas do C sem problemas.

    Você pode me recomendar algum livro para aprender C++?
    O único livro para iniciantes que eu conheço e achei bom é o Beginning Visual C++ Programming. Se você é iniciante, qualquer material pode te ajudar, desde um livro de C até um tutorial de Internet. Depois que você já estiver com alguma prática em C++, leia o The C++ Programming Language do Stroustrup, que é o guia definitivo sobre o assunto, e que eu recomendo até para os programdores C++ experientes. Eu prentendo recomendar mais livros a medida que o FAQ for avançando.

    Todo mundo diz que C++ é muito complicado. Isso é verdade?
    Você é um homem ou um rato? C++ requer mais estudo por ser uma linguagem completa e poderosa, mas não é nada que um ser humano normal não consiga aprender. Tem gente que acha que usar o Microsoft Word é complicado. Esqueça o que os outros dizem e estude aquilo que você tem vontade.

    A moda agora é .NET e Java, será que vale a pena estudar C++?
    Vale. Como as pessoas estão indo para as linguagens mais fáceis, os profissionais de C++ são mais valorizados. Afinal, quando se precisa de algo 10 vezes mais rápido, alguém precisa fazer. Não se esqueça que praticamente todos os softwares comerciais que existem são feitos em C ou C++ (Windows, Office, SQL Server, Oracle, Photoshop, CorelDRAW, Linux, Visual Studio, o próprio .NET e todas as VMs Java, etc, etc, etc).

    Eu já ouvi falar que o Java e o C# são versões melhoradas do C++. Isso é verdade?
    Se você acha que uma versão mais limitada de alguma coisa é uma melhoria... O Java e o C# são baseadas na linguagem C++, tirando muitos recursos que, apesar de poderosos, causavam confusão ou dificuldade. Foi feita uma simplificação e um nivelamento por baixo (ok, pelo meio) para atender às necessidades mais comuns. Por serem mais simples, essas duas linguagens são indicadas para aplicativos tecnicamente mais simples, como os que manipulam bancos de dados, controlam regras de negócios e fazem entradas de dados. As duas runtimes (JVM e .NET) têm algumas vantagens sobre o C++, como gerenciamento automático de memória, independência relativa de arquitetura e mais facilidade para leitura dos metadados (enumerar as classes de um DLL, por exemplo). Mas têm a desvantagem de consumirem bem mais memória, serem mais lentas (isso pode melhorar com o tempo) e terem limitações técnicas que as impedem de desenvolver certos tipos de software (como device drivers).

    Onde posso achar mais informações sobre C++?

    Wikipedia - Linguagem C
    Wikipedia - C++
    Bjarne Stroustrup's C++ FAQ
    Bjarne Stroustrup's C++ Style and Technique FAQ
    Google: "C++ tutorial" (antes que alguém pergunte...)

    Em 02/07/2005 12:43 - Comentários (94)

    A evolução da informática em fotos e screenshots

    Um site muito bom para quem quer conhecer a história da informática é o GUIdebook. Capaz de arrancar suspiros saudosistas de qualquer um que trabalha na área a mais de 5 anos, ele conta a história da informática usando fotos e screenshot de aplicativos e sistemas operacionais.

    É interessante ver como as interfaces gráficas e a informática demorou para se popularizar no Brasil (talvez pela reserva de mercado...). Uma das partes que eu achei muito interessante é a que tem diversas fotos do Apple Lisa, que tinha uma interface gráfica já bem parecida com os PCs atuais. Até o Office é muito parecido:

    O detalhe mais importante é que, prestando atenção no splash do Lisa, você vê que ele foi lançado em 1983. Além disso, consultando a Wikipedia, vemos que ele rodava com um Motorola 68000 (o mesmo do Mega Drive) de 5 MHz e com 1 MB RAM. É engraçado pensar que a funcionalidade das interfaces gráficas não evoluiu tanto assim (só estão bem mais bonitas), e fazendo um comparativo com os requisitos mínimos para o Longhorn, podemos ver o que realmente evoluiu...

    Um dos recursos mais interessantes desse site é a linha do tempo dos sistemas operacionais, vale realmente perder um tempo explorando a evolução do Windows e das outras interfaces.

    Esse assunto me fez lembrar de alguns programas antigos que eu não encontrei lá. Alguém lembra do VGA Copy?

    Ah, alguém lembra como se chamava aquele programa que tocava arquivos MOD diretamente no speaker do PC, tinha um modo com três grandes luzes coloridas e que a tela fazia um scroll estranho?

    Em 05/07/2005 08:45 - Comentários (6)

    FAQ: Programação Win32 em C/C++

    Continuando o meu projeto de projeto de FAQ C++, vou falar hoje sobre Win32.

    O que é Win32 e para que serve?
    Já expliquei isso em outro post.

    Ouvi falar que Win32 é complicado, isso é verdade?
    Isso tem lá seu fundo de verdade, alguns assuntos são extremamente complicados, os mais usuais nem tanto. De qualquer forma, essa complicação tem motivo: como a Win32 API é o acesso mais baixo nível que se pode ter ao sistema (considerando user-mode), ela deve possibilitar que você a use para fazer tudo que é possível no Windows. Por isso, muitas vezes, você chama uma função de 8 parâmetros mais só passa 2. Esses outros 6 parâmetros são usados em situações não muito comuns, mas que devem ser cobertas pela API. Além disso, como essa API é C (e não C++), não é possível fazer overload das funções com versões mais simples para os usos mais simples, como é feito muitas vezes nas classes do .NET Framework e em bibliotecas C++.

    Vou exemplificar isso usando a famosa função CreateProcess como exemplo. Essa função pede 10 parâmetros, mas em 99% dos casos você só usa o LPTSTR lpCommandLine. Além dos parâmetros que você passa como NULL, existem alguns que você é obrigado a preencher, como o LPSTARTUPINFO lpStartupInfo, e o LPPROCESS_INFORMATION lpProcessInformation. Veja um exemplo adaptado da MSDN:

    #include <windows.h>
    #include <stdio.h>
    
    void main()
    {
        STARTUPINFO si;
        PROCESS_INFORMATION p;
    
        //
        // dica: muitas estruturas no Windows têm uma variável membro chamada
        // cb ou cbSize. Esse membro deve ser preenchido com o sizeof()
        // da estrutura ANTES de chamar a função. Se você não fizer isso a função 
        // retornará um erro. Esse é um erro comum de iniciantes (eu apanhei muito...)
        //
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
    
        ZeroMemory(&p, sizeof(p));
    
        //
        // Abrindo o Bloco de Notas
        //
        if( !CreateProcess( NULL,   // Sem módulo 
            "C:WINDOWS\\notepad.exe", // linha de comando para o executável 
            NULL,
            NULL,
            FALSE,            // O processo criado não herdará os handles
            0,
            NULL,
            NULL,
            &si,              // Ponteiro para STARTUPINFO
            &p )             // Ponteiro para PROCESS_INFORMATION 
        ) 
        {
            printf( "Erro em CreateProcess: 0x%X", GetLastError() );
            return;
        }
    
        //
        // O CreateProcess preencheu as estruturas e colocou handles nelas
        // como não vamos usar os handles, precisamos fechá-los...
        //
        CloseHandle(p.hProcess);
        CloseHandle(p.hThread);
    }
    

    O que eu preciso saber antes de estudar Win32?
    Você precisa ter prática em programação C/C++. A API Win32 é exportada como funções C, mas é usada a partir do C++ sem problemas.

    Quais livros de Win32 você recomenda?
    Primeiramente: eu indico que você leia os livros que vou indicar :-) Eu acho muito mais difícil estudar Win32 com tutoriais e lendo a MSDN. Não acho que exista muito matérial para iniciantes na Internet, e, como eu já disse, vale muito mais a pena comprar um livro e ler do que ficar procurando na Internet.

    O primeiro livro que você deve ler é o Programming Windows do Charles Petzold, considerado a Bíblia definitiva sobre o assunto. Ele já está na quinta edição, e é essencial para que você entenda como a GDI do Windows funciona. Ele é hardcore do meio pra frente (eu não tive paciência para ler a parte de toolbar), se você não tiver paciência leia até o capítulo 11 (página 566). Isso já será suficiente para entender como as coisas funcionam no Windows, e será muito útil MESMO QUE VOCÊ USE Windows Forms (afinal, WinForms nada mais é do que uma camada .NET sobre a GDI).

    Nossa, Win32 é complicado mesmo... 100 linhas para fazer uma janelinha branca e feia? Existe algo mais fácil?
    Existe. Hoje em dia, quase ninguém mais usa API diretamente ("à la Petzold" como é conhecido). A GDI é complexa pelo mesmo motivo que eu já expliquei, ela é muito flexível e é um dos motivos do sucesso do Windows. Poder e flexibilidade sempre têm um custo, que geralmente é a complexibilidade.

    Para resolver isso, foram criadas, por diversas empresas, bibliotecas que encapsulam a Win32 API para facilitar o seu uso (o .NET Framework é uma delas). A biblioteca provida pelo Microsoft Visual C++ é a MFC (Microsoft Foundation Classes), que encapsula uma boa parte da Win32 API em objetos C++. Além disso, a IDE do Visual C++ possui diversos wizards que geram classes e código e facilitam muito a programação. Usar dialogs (forms) em MFC é quase tão fácil quanto VB6. Existem outras bibliotecas, como a VCL/CLX do Delphi e do C++ Builder e o WTL da Microsoft (que é a que eu mais gosto).

    Leia algo sobre MFC depois de ler o Petzold. Pode ser qualquer livro, entendendo como funciona o Win32 e a GDI MFC não será um problema.

    O livro do Petzold cobre extensivamente a GDI (gráficos). E o resto?
    Depois de ler o Petzold e algo sobre MFC, leia o Advanced Windows do Jeffrey Richter, outro clássico. Pronto. Isso já é suficiente para você ser um programador Win32. Só 3 livros :-)

    Meus amigos dizem que a API Win32 está ultrapassada e não vale a pena estudar isso. É verdade?
    Não, não é. Você anda convivendo demais com Piranhas.NET. Explique para os seus amigos que TODOS os aplicativos para Windows hoje são feitos em Win32, já que ela é o Windows. No Windows Longhorn (previsto para 2006/2007) ALGUMAS NOVAS APIs serão disponibilidas como classes .NET. O Windows vai ficar cada vez mais .NET, mas isso deve levar no mínimo uns 5 anos. Não se esqueça que o .NET foi lançado em 2001 e a maioria dos aplicativos comerciais ainda são feitos em C e C++, usando a API Win32 (que por enquanto é a API do Windows)

    Meus amigos disseram que existe mais material de estudos e livros falando de .NET do que de Win32. É verdade?
    Não é verdade que existem mais livros sobre .NET. A API Win32 existe desde de 1993, e uma procura por livros na Amazon retornará vários livros. Procure "MFC" na Amazon, depois procure "Windows Forms" e veja a diferença. Além disso, no Code Project tem muito mais coisa útil em MFC do que em .NET.

    Você me deixou em dúvida. Se o .NET é tão ruim assim, então eu devo abandoná-lo e fazer tudo em C++?
    Primeiro: Eu nunca disse que o .NET é ruim. Na verdade eu acho ele muito bom para resolver os problemas que ele se propõe. Mas, tem seus problemas como qualquer API/Framework/Arquitetura. O grande problema é que no Brasil as pessoas acreditam no pessoal de marketing da Microsoft sem pensar se isso resolve o problema, e acham que .NET resolve todos os problemas. Nada resolve todos os problemas.

    Como eu já disse, hoje tudo é C++ e Win32 e vai levar anos (talvez mais de uma década) para que o .NET seja realmente a plataforma de programação do Windows. Você acha mesmo que a Microsoft, Adobe/Macromedia e outros vão refazer todos os aplicativos em C#? Além disso, C++ ainda é a coisa mais portável que existe (em termos de código fonte), e esse é o motivo dele ter sido usado pela Mozilla Foundation (Firefox, Thunderbird, etc), pela Adobe (Photoshop), etc. E se isso conta, em Linux quase tudo é C e C++, o kernel (C), Gnome (C), KDE, (C++), etc. O fator multiplataforma do .NET ainda é incerto, e depende de implementações de terceiros (como o Mono).

    Eu já escrevi um artigo sobre perspectivas para o NET a quase 1 ano atrás, e acho que ela continua a mesma. As duas arquiteturas são importantes, não troque o .NET pelo Win32, aprenda os dois e use o que for melhor para cada caso. Só não fique sentado esperando o .NET virar a plataforma oficial do Windows, você vai perder vários anos de mercado e da sua vida.

    Em 11/07/2005 09:37 - Comentários (28)

    Estudo prova que 1/3 dos estudos médicos estavam errados

    CNN: "Research: Third of study results don't hold up"

    Journal of American Medical Association: "Contradicted and Initially Stronger Effects in Highly Cited Clinical Research"

    O único jeito é continuar estudando...

    Em 14/07/2005 07:40 - Comentários (0)

    1º aniversário do 1bit.com.br !

    Nem parece que faz tanto tempo assim, mas a exatamente um ano atrás eu escrevi o primeiro post do blog. Um ano escrevendo sobre C++, Win32 e falando mal do .NET :-)

    Antes de começar esse site, eu tinha me convencido que já estava na hora de compartilhar tudo que eu aprendi com as pilhas de livros e com as madrugadas que passei programando. O projeto original era fazer um site junto com o Alfred Gary Myers, com artigos dos dois. Aí eu comecei a fazer o layout do site e escrever alguns artigos. Como ele acabou não escrevendo nada, eu resolvi aproveitar todo o trabalho e fazer um site só meu :-) O Alfred acabou criando um blog no TheSpoke - que muitos dizem ser o melhor de lá, mas como sou amigo dele eu não posso falar - e depois começou a escrever para a Fórum Access.

    O começo de um site é um paradoxo interessante de se lidar. Você escreve, só que não tem leitores, mas se você não escrever, nunca vai conseguir leitores. Hoje em dia o site tem uma média de 230 visitantes únicos por dia, segundo o Awstats. O número de acessos chegou a 370 no dia que eu enviei um link para o Slashdot. Como esperado, uma boa parte do bandwidth é consumida por joselitos que colocam o seus leitores de RSS para verificar o site a cada 5 minutos, mas por enquanto isso não me causa problemas. Como o site tem poucas imagens, ele é bem leve para carregar e eu ainda estou usando só 800MB por mês (o limite do meu plano é 2GB).

    Uma coisa legal de fazer quando se tem um site é acompanhar o que as pessoas procuram no Google para chegar até o seu site. Desde que o site começou, o que mais faz as pessoas pararem aqui é a busca por informações sobre "placenta baixa", por causa do artigo "Ouça o que eu digo: não ouça ninguém", onde eu uso um caso de diagnóstico de placenta baixa para dizer como é importante obter informações antes de tomar um decisão. Esse mês, pela primeira vez na história do site, a procura por informações sobre o Google Maps passou a procura por placenta baixa.

    Como parte das comemorações desse primeiro ano, eu escrevi o artigo sobre "Como ser um programador". Primeiro eu escrevi o "Como ser um BOM programador", e espero que ele tenha ajudado algumas pessoas a perceber como são simples os requisitos para ser um bom programador. Eu tentei com esse novo artigo ajudar as pessoas que querem virar programadores mas não sabem por onde começar.

    Parabéns para mim e para todos os leitores! Agora vamos seguir com mais um ano de escovação de bits :-)

    Em 20/07/2005 23:00 - Comentários (11)

    Windows Longhorn se chamará Windows Vista

    Windows Vista ? Não era mais fácil chamar de Windows 2015? Parece que a Microsoft contratou as mesmas pessoas que criam nomes para carros para criar nomes para softwares. Eu quero só ver qual será no nome do Office 12. Será que será Office Vista também ou vão inventar algo tão estranho quanto isso?

    Agora é só esperar as piadinhas infames que vão aparecer. Espero que esse Windows não estrague a vista... :-)

    Editado em 22/07/2005, 17:30:

    Site Oficial do Windows Vista

    Em 22/07/2005 09:13 - Comentários (11)

    A otimização do Visual C++ faz toda a diferença

    Estou fazendo um parser de IDL para meu projeto de computação distribuída. Nessa primeira fase, eu interpreto o aquivo IDL e preencho várias estruturas que descrevem as interfaces e seus métodos.

    Todo esse projeto está sendo feito em C++ padrão ISO, sem usar recursos específicos do Windows. Eu uso std::string em todo o programa, e o tratamento de erro é feito usando exceções. Para fazer o parse do arquivo eu usei o boost::tokenizer, que eu gostei muito. Você passa um std::string (ou dois iterators, início e fim) com o conteúdo do arquivo e ele te retorna um iterator forward-only para você percorrer os tokens do arquivo. Além disso você precisa informar quais serão os separadores de token, mas é só isso:

    #include <iostream>
    #include <boost/tokenizer.hpp>
    
    #include <string>
    
    using namespace std;
    using namespace boost;
    
    
    int main()
    {
       string str = "int main() { std::cout << "1bit.com.br" << std::endl; }";
       tokenizer<char_separator<char> > Tokenizer(string());
       
       Tokenizer.assign(str,char_separator<char>("tr " , "n"*,;:{}/\[]()"));
       
       //
       // percorre todos os tokens
       //
       for(tokenizer<char_separator<char> >::const_iterator i = Tokenizer.begin() ;
           i != Tokenizer.end();
           ++i)
       {
           cout << *i << "n";
       }
    }
    

    Depois de algumas noites e alguns fins de semana programando, eu finalmente terminei uma versão usável do meu parser. No final, eu coloquei um contador de tempo para medir se eu precisava ou não otimizar o código, e para comparar com o MIDL da Microsoft. Esse é o resultado que eu obtive com a versão debug (Athlon XP 2600+, Visual C++ 7.1) :

    // versão debug
    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Parsing import file spbase.idl
    Finish (7625 ms)
    

    Mais do que 7 segundos, muito lento. Foi então que eu resolvi mudar para versão Release e configurar as otimizações com carinho. Veio então o resultado:

    // versão release
    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Parsing import file spbase.idl
    Finish (265 ms)
    

    28 vezes mais rápido!! Todo o "overhead" de usar std::string desapareceu. Vendo o disassembly do código gerado, muitos dos métodos do std::string foram transformados em inline, e era como se eu estivesse usando string C nativa. Isso acaba com as desculpas de pessoas que não usam STL dizendo que ela deixa o programa mais lento. Além do overhead já ser pequeno pela simplicidade da implementação do STL, essa desculpa desaparece na versão Release. Mesmo porque, como o código da STL é simples, o compilador tem mais facilidade para otimizá-lo. Muito mais do que para otimizar a gambiarra complicada que você perderia dias e dias para fazer.

    Vale lembrar que eu me esforcei para fazer o código da forma mais clara e bug-free possível. Nem acesso a arquivos foi otimizado, eu simplesmente li o arquivo inteiro em uma std::string usando getline(f, str, f.widen(EOF)). Eu ainda posso otimizar isso usando FileMapping e passando o ponteiro de início e fim como iterators para o boost::tokenizer.

    Em 26/07/2005 13:53 - Comentários (5)

    Fatos verídicos

    - Quanto tempo você demora pra resolver esse bug?
    - 16 Horas.
    - Tudo isso?
    - Bom, primeiro eu tenho que entender como o sistema funciona.
    - O sistema inteiro?
    - Não necessariamente. Se no meio do estudo eu encontrar o motivo do bug, é só corrigí-lo.
    - Nesse caso, esqueça esse negócio de estudo, vá direto para o motivo do bug. 30 minutos esta bom ou é muito?

    Em 05/08/2005 13:29 - Comentários (6)

    Usando Win32 API para otimizar o I/O, parte 1

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Como eu já disse em um post anterior, estou fazendo um parser de arquivos IDL, usando somente C++ ISO (nada de Win32), STL e Boost. Apesar de estar realmente impressionado com o desempenho do meu parser do jeito que está, eu me propus a testar o nível de otimização de I/O que seria possível usando a API Win32 diretamente. Vou publicar uma sequência de posts contando minhas exeriências com isso, explicando os métodos e ferramentas que eu usei para chegar às conclusões.

    Primeiro, deixe-me explicar algo sobre desempenho (ou performance). Ultimamente eu tenho trabalhado bastante com isso, minha atribuição na empresa onde trabalho é (entre outras) pegar programas, serviços e componentes que já funcionam e melhorá-los. As melhorias são feitas em vários aspectos, tanto melhorias na estabilidade dos serviços quanto melhorias de desempenho.

    Existem vários fatores importantes para se fazer melhoria de desempenho em um software. Conhecimento da linguagem de programação usada (C++ no meu caso) é muito importante, assim como é importante o conhecimento do funcionamento das bibliotecas (como a grande maioria das bibliotecas C++ são pequenas e com o fonte disponível, essas tarefa exige muito estudo de código fonte e pouca adivinhação ou disassembly). Outro fator muito importante é o conhecimento do sistema operacional e quais interações seu programa tem com cada parte dele. Nesse caso específico, o conhecimento do funcionamento do I/O do sistema operacional é muito importante, até para podermos saber quais opções temos para fazer essa melhoria. Um melhoria pode envolver uma modificação - algumas vezes pequena - no método usado para se chegar a um resultado, ou uma mudança completa no enfoque ou no algoritmo.

    Todos os fatores que eu citei são importantes, mas o fator mais importante não é um conhecimento técnico. Chama-se mensuração. Só é possível dizer que alguma coisa foi melhorada se você souber exatamente o estado dela no momento atual, e se você conseguir mensurar exatamente qual o nível de melhoria entre uma modificação e outra. O conceito de melhor é algo muito subjetivo, não resolve o problema. Não basta o software ser "melhor", deve-se saber o "quão melhor" esse software é. E, depois disso, saber se o esforço dedicado a essa melhoria realmente valeu a pena, ou se foi muito trabalho para uma melhoria pequena.

    A estrutura do parser é simples, e da forma que está feito hoje (sem Win32), o código que faz o I/O é muito parecido com o abaixo:

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       fstream f;
       string str;
       
       ...
       
       if(m_bParseAsImportFile)
       {
          if(!OpenIncludeFile(fileName, &f, &m_parsedFileName))
             throw ParseException(string("error opening import file "") + fileName + 
                                  "\"", *this, m_parsedFileName);
    	   ...
       }
       else
       {
          f.open(fileName, ios::in);
    
          if(f.fail())
             throw ParseException(string("error opening file "") + fileName + 
                                  "\"", *this, m_parsedFileName);
    	  ...
       }
       
       //
       // carrega o arquivo inteiro em uma string STL
       //
       getline(f, str, f.widen(EOF));
    	
       //
       // Inicializa o tokeninzer
       //
       m_Tokenizer.assign(str,char_separator("\t\r " , "\n\"*,;:{}/[]()"));
       
       ...  
       
       //
       // Loop que interpreta o IDL
       //
       for(;;)
       {
    	  // ...
       }
    

    Quando um arquivo é interpretado, diversas estruturas - IdlInterface, IdlMethod, IdlParameter, etc - são preenchidas com as informações interpretadas. Isso faz com que o interpretador fique completamente independente do gerador de proxy/stub que consumirá essas estruturas, o que me permite, futuramente, usar outro formato que não o Microsoft IDL.

    Para um arquivo IDL comum, aproximadamente 400kb de texto deve ser lidos, em aproximadamente 10 arquivos. A maioria dos arquivos interpretados fazem parte do Windows Platform SDK, que definem os tipos de dados padrão do COM. A saída do parser é essa:

    Parsing import file oaidl.idl
    Parsing import file objidl.idl
    Parsing import file unknwn.idl
    Parsing import file wtypes.idl
    Parsing import file ocidl.idl
    Parsing import file oleidl.idl
    Parsing import file servprov.idl
    Parsing import file urlmon.idl
    Parsing import file msxml.idl
    Finish (234ms)
    

    No próximo post da série veremos algumas estatísticas sobre o programa da forma que está e começaremos a fazer algumas otimizações.

    Em 07/08/2005 21:58 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 2

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Antes de começar a otimização do I/O do parser, é necessário fazer algumas considerações e algumas medições. A proposta inicial é otimizar o I/O do parser, mas será que vale mesmo a pena?

    Fiz algumas medições para saber quanto tempo o parser demora para ler os arquivos que ele interpreta. Depois de várias medições (aproximadamente 50), cheguei a conclusão de que gastamos, em média, 20% do tempo com I/O. Em situações onde o computador acabou de ser ligado - ou seja, quando os arquivos não estão no Cache Manager do Windows - o tempo de acesso aos arquivos pode chegar a 50% do tempo gasto pelo parser. Não esqueça que só estamos medindo o tempo necessário para ler os arquivos (que como eu já disse, somam aproximadamente 400kb), não gravamos nada.

    Tempo médio gasto com I/O:  20%
    Tempo máximo gasto com I/O: 50%
    

    Já que a interpretação do arquivo em si leva 80% do tempo em média, não valeria mais a pena otimizá-la e de deixar o I/O para depois? Minha resposta é não, e vou explicar os motivos. A interpretação é o core do sistema, e o código que lida com isso foi feito da forma mais clara possível, para facilitar a manutenção e melhorias. E eu não estou disposto a sacrificar a clareza do código para que o parser fique mais rápido. Sendo assim, a otimização seria mais difícil, porque provavelmente envolveria mudanças em algoritmos, além de ser feita com essas requisição (clareza do código) em mente.

    O motivo principal para otimizar o I/O é que, de acordo com o que vimos no post anterior, o I/O corresponde a menos de 4 linhas de código, enquando a classe de parser tem aproximadamente 850 linhas. Essas 4 linhas de código, algumas vezes, chegam a representar 50% do tempo gasto. Sendo assim, o custo-benefício da alteração me pareceu muito satisfatório, já que é fácil otimizar o I/O, afinal, ele é uma parte pontual e fácil de ser isolada e compreendida. A otimização do I/O não vai comprometer a legibilidade do código.

    Conclusão: vale a pena otimizar o I/O. Depois dessas medições, no próximo post começaremos a testar as otimizações possíveis e comprar os resultados.

    Em 11/08/2005 15:27 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 3

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Agora que já tivemos bastante bla-bla-bla e medições, está na hora de fazer alguma otimização. A primeira otimização que faremos consiste em mudar os acessos aos arquivos - que hoje são feitos usando a runtime do C++ - para usar a Win32 API diretamente. Duas modificações serão feitas:

    • Usar CreateFile/ReadFile para ler o conteúdo do arquivo;
    • Usar VirtualAlloc para alocar memória ao invés de alocar do heap (que é o que a std::string faz).

    Como um trecho de código vale sempre mais do que ~340 m/s, vou mostrá-lo antes e explicar depois:

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       HANDLE hFile;
       char* szFileContent;
       DWORD dwFileSize;
       DWORD dwReaded;
    
       if(m_bParseAsImportFile)
       {
          //
          // vamos procurar o arquivos nas pasta determinadas para include
          //
          for(vector::const_iterator i = m_IncludePaths.begin() ; 
              i != m_IncludePaths.end() ; 
              ++i)
          {
             string str = *i + fileName;
    
             hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, 
                                NULL, OPEN_EXISTING, NULL, NULL);
    
             if(hFile != INVALID_HANDLE_VALUE)
                break;
          }
    
          if(hFile == INVALID_HANDLE_VALUE)
             throw ParseException(string("error opening import file \"") + fileName 
                                  + "\"", *this, m_parsedFileName);
    
          dwFileSize = GetFileSize(hFile, NULL);
    
          //
          // ao invés de alocar do Heap (que é para alocações pequenas), 
          // vou alocar direto da memória virtual. O Heap Manager usa 
          // memória virtual para isso, então estaremos 
          // "pulando" um nível de abstração.
          //
          //szFileContent = (char*)HeapAlloc(GetProcessHeap(), NULL, dwFileSize);
    
          szFileContent = (char*)VirtualAlloc(NULL, dwFileSize + (dwFileSize % 4096), 
                                              MEM_COMMIT, PAGE_READWRITE);
    
          //
          // vamos ler o arquivo inteiro de uma vez
          //
          ReadFile(hFile, szFileContent, dwFileSize, &dwReaded, NULL);
          ...
       }
       else
       {
          hFile.Create(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    
          if(hFile.m_h == INVALID_HANDLE_VALUE)
             throw ParseException(string("error opening file \"") + fileName + 
                                  "\"", *this, m_parsedFileName);
    
          dwFileSize = GetFileSize(hFile, NULL);
    
      //szFileContent = (char*)HeapAlloc(GetProcessHeap(), NULL, dwFileSize);
          szFileContent = (char*)VirtualAlloc(NULL, dwFileSize + (dwFileSize % 4096), 
                                              MEM_COMMIT, PAGE_READWRITE);
          
          ReadFile(hFile, szFileContent, dwFileSize, &dwReaded, NULL);
    
          ...
       }
    

    Algumas coisas importantes devem ser notadas nessa implementação. A primeira é que, além de mudar o acesso para usar CreateFile/ReadFile, eu pego antes o tamanho do arquivo, para ler tudo de uma vez. Não sabemos (por enquanto) como a runtime do C++ faz a leitura, mas ler o arquivo todo de uma vez só é a melhor forma. Outro detalhe da implementação é que eu uso VirtualAlloc para alocar memória e não HeapAlloc/new/malloc.

    Antes de explicar o motivo de usar VirtualAlloc, uma explicação sobre gerenciamento de memória no Windows. A função mais low-level para alocar memória em user mode é a VirtualAlloc. Ela aloca memória no endereçamento do processo, com a granulação de uma página de memória (no Windows, em user mode, e em arquitetura x86, uma página de memória é de 4kb). Sendo assim, só podemos alocar múltipos de uma página. Por isso eu coloquei um dwFileSize + (dwFileSize % 4096) no tamanho, para arredondar a quantidade alocada para o primeiro múltiplo de 4kb maior do que a quantidade necessária.

    Como muitas vezes os programas precisam de áreas de memória menores do que 4kb, usar no mínimo esse valor em cada alocação seria um desperdício muito grande. Por isso existem os heaps, que são pools de memória que servem alocações em granulações menores. Apesar de (na maioria das vezes) reduzir o desperdício de memória, o heap é mais lento do que o VirtualAlloc, porque uma alocação envolve algorimos de procura de blocos livres e desfragmentação da memória alocada para o heap.

    O Windows possui gerenciamento de heap nativo, tanto em user mode quanto em kernel mode. Em user mode a função que aloca memória de um heap é a HeapAlloc(HANDLE,DWORD,SIZE_T), onde o primeiro parâmetro é um HANDLE para um heap. Você pode usar o heap default do processo (chamando GetProcessHeap()) ou criar um heap usando HeapCreate(). A runtime do C/C++ usa o HeapAlloc, mas cria um heap separado do heap padrão do processo.

    Usei o VirtualAlloc porque ele é mais rápido do que o heap (já que aloca uma página diretamente e não tem problemas de fragmentação) e não envolve mais esforço de programação. Além disso, o desperdício da memória que ficará entre o que usamos e o fim da página será praticamente desprezível. Leremos aproximadamente 400kb, ou seja, 100 páginas. Mesmo se 100% das páginas forem desperdiçadas (o que é impossível acontecer), estaríamos usando 400kb a mais. Eu medi o desperdício para os arquivos do SDK, e ele é de 5,21% (o que é realmente desprezzzzível, como diria o Patolino).

    Voltando à otimização de I/O, aqui está a medição que eu fiz depois dessas otimizações:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando Win32 API

    Melhoria: 6,21%

    Para primeira tentativa de otimização, 6,21% não é tão mal... Na próxima parte veremos se podemos fazer melhor.

    Em 17/08/2005 18:07 - Comentários (5)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: MFC

    Outros posts dessa mesma série:   Win32     COM     ATL

    MFC: A Microsoft Foundation Classes é um framework e uma biblioteca de classes C++ que encapsula grande parte da API Win32. Ela foi criada pela Microsoft para facilitar a programação em Win32 usando C++, já que a API Win32 é exportada como funções C. Ela é um framework no sentido de que todos os aplicativos MFC devem "se encaixar" nesse framework de alguma forma, seguindo certos requisitos. Ela não foi feita para ser incluída em um projeto só para usar algumas classes, como é o caso do WTL (eu falarei de WTL em outro post).

    Como um trecho de código vale mais do que 2 + 2 palavras, vamos à ele:

    #include <afxwin.h>
    
    //
    // Minha janela principal
    //
    class CMyWindow : public CFrameWnd
    {
    private:
      CButton m_btnTest;
    
      DECLARE_MESSAGE_MAP();
    
    public:
      //
      // essa função não é da MFC, eu a criei para inicializar meus 
      // controles daqui
      //
      BOOL InitializeControls()
      {
        m_btnTest.Create("blah",BS_PUSHBUTTON , CRect(10,10,100,60),this, 1);
        m_btnTest.ShowWindow(SW_SHOW);
    
        return TRUE;
      }
    
      //
      // função que trata o evento de clique do botão
      //
      LRESULT OnButtonTestClick()
      {
        AfxMessageBox("button click!");
        return 0;
      }
    
      //
      // evento de duplo click
      //
      LRESULT OnLButtonDblClk(UINT, CPoint)
      {
        AfxMessageBox("dbl click!");
        return 0;
      }
    
      //
      // evento de resize
      //
      LRESULT OnSize(UINT nType, int cx, int cy)
      {
        CString str;
    
        //
        // vamos colocar a nova altura e largura como título
        // da janela
        //
        str.Format("largura: %d, altura:%d", cx, cy);
    
        SetWindowText(str);
    
        return 0;
      }
    };
    
    //
    // mapa de mensagens da classe CMyWindow
    // aqui colocamos de modo declarativo qual evento será
    // tratado por qual função
    //
    BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
      ON_BN_CLICKED(1, OnButtonTestClick)
      ON_WM_LBUTTONDBLCLK()
      ON_WM_SIZE()
    END_MESSAGE_MAP( )
    
    //
    // classe que representa a aplicação em si,
    // herda de CWinApp
    //
    class CMyApp : public CWinApp
    {
      //
      // Função chamada pela MFC assim que o aplicativo é
      // inicializado. Coloque aqui o que seria colocado
      // no WinMain
      //
      BOOL InitInstance()
      {
        CMyWindow* pMyWindow = new CMyWindow;
    
        //
        // vamos criar nossa janela e mostrá-la
        //
        pMyWindow->Create(NULL, "www.1bit.com.br");
    
        pMyWindow->InitializeControls();
        pMyWindow->ShowWindow(SW_SHOW);
        pMyWindow->UpdateWindow();
    
        m_pMainWnd = pMyWindow;
    
        return TRUE;
      }
    };
    
    
    CMyApp theApp;
    

    A MFC é a principal biblioteca de classes para C++ da Microsoft. Ela vem junto com o Visual C++, e a IDE tem total suporte para ela, facilitando a nossa vida com wizards e "clique com o botão direito e...". Fazer um "Dialog Application" usando MFC e o Visual C++ é quase tão fácil quanto fazer um programa em VB (meus alunos de C++ disseram isso). Como no VB, você desenha um botão visualmente, dá um duplo clique no botão criado e a MFC cria um handler para o evento de clique do botão. Além disso, a MFC contém diversas macros e funções para associar variáveis com controles Win32. Esse recurso é chamado de DDX, e é muito útil para formulários cheios de CheckBoxes e TextBoxes. Eu acho esse um dos recursos mais úteis da MFC, e reduz bastante o tempo de desenvolvimento de programas com essas características.

    A MFC também tem suporte à bancos de dados ODBC ou OLEDB. No caso de OLEDB, um wizard cria classes com propriedades (variáveis membro) equivalentes aos campos do banco de dados. Mas o grande destaque da MFC é "Document/View Model", que provê um framework para aplicativos que usam o conceito de documento (como o Word, CorelDRAW!, etc). Junto com o suporte a serialização, fica bem mais fácil fazer um aplicativo de edição de documentos com open/save, área de transferência e várias views de um mesmo documento.

    A MFC é bastante criticada por não usar recursos modernos do C++ e não ser totalmente type-safe. Apesar das melhorias no quesito type-safe a partir da MFC 7, as críticas ainda persistem. Apesar de tudo isso, a MFC ainda é muito usada, e é possível achar muita informação sobre ela na Internet. A literatura é vasta e existem diversos sites com seções especializadas em MFC (como o Code Project e o Code Guru). Muitas empresas (inclusive brasileiras) desenvolvem aplicativos em MFC, e a finada certificação "Developing Desktop Applications Using Microsoft Visual C++" era toda sobre MFC.

    Usando a MFC você tem a opção de fazer um link estático com a MFC ou usar a MFC em uma DLL (de aproximadamente 1 MB). No exemplo de código acima, o link estático fez o executável ficar com 100kb a mais, mas eu já vi casos onde o executável fica 300kb maior - o que em muitas situações é desprezível.

    Mais informações

    Documentação da MFC na MSDN
    Hierarquia de classes da MFC
    Code Project
    Livros sobre MFC na Amazon

    Em 23/08/2005 12:51 - Comentários (9)

    E tem gente que acha que Dilbert é ficção...

    Em 24/08/2005 08:59 - Comentários (1)

    Usando Win32 API para otimizar o I/O, parte 4

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Conhecer a arquitetura do sistema operacional e de todas as dependências do seu software é essencial na hora de fazer otimização. Muitas vezes, mudanças muito pequenas no código podem melhorar substancialmente um software. Até porque, uma mudança pequena pode causar grandes mudanças no funcionamento de um módulo do sistema operacional ou de alguma biblioteca.

    Além do conhecimento, é importante saber bem o que se está fazendo (e prestar atenção na documentação :-)). Na documentação do CreateFile na MSDN, estão listados diversos flags que você pode passar quando abre um arquivo. No nosso caso específico, o que mais chama atenção é o FILE_FLAG_SEQUENTIAL_SCAN:

    FILE_FLAG_SEQUENTIAL_SCAN A file is accessed sequentially from beginning to end. The system can use this as a hint to optimize file caching. If an application moves the file pointer for random access, optimum caching may not occur. However, correct operation is still guaranteed.
    Specifying this flag can increase performance for applications that read large files using sequential access. Performance gains can be even more noticeable for applications that read large files mostly sequentially, but occasionally skip over small ranges of bytes.

    Usando essa flag, estamos dizendo ao Windows que acessaremos o arquivo do início ao fim, sequencialmente. A função interpreta isso como uma dica, que pode ser usada para otimizar o cache do arquivo. Sabendo como acessaremos o arquivo, o Windows pode "prever" nosso comportamento e ficar preparado para isso.

    Modificando o trecho de código que abre o arquivo, ele ficará assim:

    //
    // chama atual para abrir um arquivo
    //
    hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, 
                       OPEN_EXISTING, NULL, NULL);
    
    //
    // nova chamada para abrir um arquivo
    //
    hFile = CreateFile(str.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, 
                       OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    

    Refeitos os testes de desempenho, temos os seguintes resultados:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando CreateFile
    204,17 ms - versão usando CreateFile com FILE_FLAG_SEQUENTIAL_SCAN

    5,15% - melhoria em relação a versão Win32 sem FILE_FLAG_SEQUENTIAL_SCAN
    11,68% - melhoria em relação a versão inicial (runtime C++)

    Ganhamos mais ~5% de desempenho somente trocando UM flag em UMA chamada. Isso foi quase a mesma melhoria que ganhamos trocando todos os acessos feitos usando a runtime do C++ por chamadas Win32. Conhecimento é poder.

    Em 29/08/2005 11:15 - Comentários (0)

    Aos coitados que ainda precisam usar Visual C++ 6.0 (inclusive eu)

    Seguindo a série de otimização de performance e meu post sobre o otimizador do Visual C++ 7.1, eu resolvi tentar compilar o parser usando o Visual C++ 6.0 Service Pack 5, para fazer uma comparação com o VC7.1. Sim, TENTAR é o termo exato. Apesar de alguns dizerem o que Visual C++ 6 suporta Boost, isso não é bem verdade, não é a primeira vez que eu tenho problemas tentando fazer o Boost compilar no Visual C++ 6.0. Olha só o que eu consegui com minha tentativa:

    --------------------Configuration: spidl_vc6 - Win32 Debug--------------------
    Compiling...
    StdAfx.cpp
    stlport\stl\_alloc.h(354) : fatal error C1001: INTERNAL COMPILER ERROR
            (compiler file 'msc1.cpp', line 1794) 
             Please choose the Technical Support command on the Visual C++ 
             Help menu, or open the Technical Support help file for more information
    Error executing cl.exe.
    
    spidl_vc6.exe - 1 error(s), 0 warning(s)
    
    

    Pode parecer um erro da STLPort, mas eu uso a STLPort em vários projetos VC6 e não tenho problemas. O problema real é que o Boost (que eu uso no parser) é muita areia para o caminhãozinho do VC6.

    A empresa onde eu trabalho ainda usa Visual C++ 6.0, assim como muitas outras. Não é o nosso caso, mas eu já reparei que muitas empresas ainda usam esse compilador velho, desatualizado e fora do padrão por causa da confusão que a Microsoft fez colocando .NET em tudo que existia - tanto que o próximo Visual Studio será "Visual Studio 2005", sem o .NERD no final. Muita gente acha que o Visual Studio.NET é uma suite de ferramentas para desenvolver somente aplicações .NET. Se faz necessário explicar claramente e em letras garrafais:

    O Visual C++ 6.0 é MUITO velho, foi lançado em 1998, antes mesmo do padrão C++ ISO ter sido finalizado.

    O Visual Studio.NET 2003 contém o Visual C++ 7.1, que é o último compilador C++ lançado pela Microsoft. Ele tem uma ótima conformidade com o padrão ISO e tem um otimizador muito bom. Usando o Visual C++ 7.1 seus programas não virarão .NET, serão 100% nativos. O Visual C++ 7.1 (que, novamente, vem junto com o "Visual Studio.NET 2003") importa seus projetos Visual C++ 6.0 sem maiores problemas. Na versão 7.1, tanto a MFC quanto a ATL foram bastante melhoradas, e vale a pena fazer o upgrade. O Windows XP SP2 e o Windows Server 2003 são compilados usando o Visual C++ 7.1, o mesmo que você recebe no Visual Studio.NET 2003.

    O Visual C++ 7.1 (lembre-se, ele é parte do "Visual Studio.NET 2003") tem um flag (/clr) para compilar Managed C++ (o binding C++ para .NET), mas ele é opcional (afinal, é um flag). Usando o Visual C++ 7.1 você continuará gerenciando sua própria memória, aumentará sua produtividade (se você acha que C++ não é produtivo é porque você nunca usou), já que terá um compilador padrão ISO, e poderá usar Boost, Loki, ATL7, MFC7, etc. Ah, e além disso, você continuará fazendo programas muito mais rápidos do que seus amiguinhos do garbage collector... :-)

    Em 01/09/2005 14:39 - Comentários (7)

    Usando Win32 API para otimizar o I/O, parte 5

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Parte 6     Fontes do parser

    Um fator muito importante na hora de melhorar o desempenho de um aplicativo é saber exatamente o que está acontecendo e o que ele está fazendo. Não podemos fazer modificações em um software sem saber se essas modificações vão trazer alguma melhora e sem saber o motivo da melhora. Lembre-se do que eu falei sobre mensuração, precisamos saber exatamente qual foi o grau de melhoria.

    Hoje não vamos otimizar nada, mas vamos descobrir porque a nossa versão ficou mais rápida do que a que usa a runtime do C++. Afinal, nosso código faz exatamente a mesma coisa: ler o arquivo inteiro. Só que nós lemos da melhor forma possível (o arquivo todo de uma vez só), e acreditamos que essa não seja a forma usada pela runtime do C++.

    Para descobrir como a runtime do C++ faz a leitura do arquivo, vamos usar o WinDbg. Com ele vamos saber exatamente quais parâmetros estão sendo passados para as funções da Win32 API. Como a Win32 API é o acesso mais low level que se pode ter em user mode, a runtime do C++ só pode acabar usando a Win32 API no final das contas.

    A função que faz a leitura de um arquivo é a ReadFile (a ReadFileEx é usada somente para leituras assíncronas). Sabendo disso, vamos colocar um breakpoint na função ReadFile e fazer um dump dos parâmetros a cada chamada. Carregue o executável no WinDbg (veja os tutoriais de Windbg caso ache necessário), e coloque o breakpoint na função ReadFile. Depois disso, go, go, go:

    0:000> bp KERNEL32!ReadFile
    0:000> g
    Breakpoint 0 hit
    eax=002f0650 ebx=002f26b8 ecx=002f26b8 edx=002f0081 esi=00000018 edi=0042dfc0
    eip=79736168 esp=0012f7f0 ebp=0012f824 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    KERNEL32!ReadFile:
    79736168 55               push    ebp
    

    Chegamos ao ReadFile, agora precisamos fazer um dump dos parâmetros. Olhando a documentação da MSDN, vemos que o primeiro parâmetro é o HANDLE do arquivo, e o terceiro é a quantidade de bytes lidos. Fazendo dump desses parâmetros conseguimos saber exatamente como a runtime do C++ está fazendo a leitura. Código primeiro, explicação depois:

    0:000> * Informações do handle (primeiro parâmetro)
    0:000> !handle poi(@esp+4) 4
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0:000> * Quantidade de bytes lidos (terceiro parâmetro)
    0:000> dd @esp+0xc L1
    0012f7fc  00001000
    

    A primeira coisa que fizemos foi pegar informações sobre o HANDLE do arquivo. Usamos a extensão !handle, que com a flag 4 no final faz um dump de todas as informações do HANDLE, inclusive nome do arquivo quando cabível (pode ser um HANDLE de evento ou outra coisa...). Pegamos o primeiro parâmetro usando @esp+4 (já falei sobre posição de parâmetros na pilha x86 anteriormente) e colocamos poi() para tratá-lo como um ponteiro e pegar o apontado. Apesar do tipo ser HANDLE e não um ponteiro, HANDLE nada mais é do que um #define para void*. Depois disso usamos o dd (Dump DWORD) para ler o terceiro parâmetro - a primeira coluna é o endereço de memória lido e o L1 no final é para ler somente um DWORD, o default é ler 32 DWORDs. Note que tudo vem em hexadecimal, então 00001000 é na realidade 4096 em decimal, que na realidade é o tamanho de uma página de memória x86 (na realidade, eu já falei sobre alinhamento e paginação de memória nessa série).

    Temos as informações da primeira chamada à ReadFile, mas repetir esses comandos em todas as chamadas é muito trabalhoso. Por isso vamos criar um breakpoint que execute esses comandos para fazer um log de todos os ReadFile chamados. Vamos ao comando:

    0:000> bp kernel32!ReadFile "!handle poi(@esp+4) 4; dd esp+0xC L1 ; g"
    0:000> g
    

    Com esses comandos faremos o dump das informações que queremos e deixaremos o programa seguir (g). Vamos ao resultado:

    0:000> g
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f7fc  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f200  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012ec04  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e608  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
    
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012e00c  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00000001
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f200  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00000001
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00000001
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012ec04  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e608  00001000
    eax=00000000 ebx=7ffdf000 ecx=00000000 edx=00000000 esi=7846ee04 edi=00000000
    eip=7846ee0f esp=0012fe00 ebp=0012fec8 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    ntdll!NtTerminateProcess+0xb:
    7846ee0f c20800           ret     0x8
    

    Pronto, descobrimos que a runtime do C++ (usando getline(f, str, f.widen(EOF))) pede 4096 bytes a cada leitura. Nosso parser fica mais rápido usando Win32 API diretamente porque lemos o arquivo todo de uma vez, além de passar a flag que avisa o Windows que leremos o arquivo sequencialmente. Olhe como fica o mesmo dump feito com a versão Win32:

    0:000> bp kernel32!ReadFile "!handle poi(@esp+4) 4; dd esp+0xC L1 ; g"
    0:000> g
    Handle 3d4
      Name         	\Device\HarddiskVolume1\temp\test.idl
    0012f834  00002cfd
    Handle 3d8
      Name         	...\Microsoft SDK\include\OAIdl.Idl
    0012f1d8  0000ecf9
    Handle 3c8
      Name         	...\Microsoft SDK\include\ObjIdl.Idl
    0012eb7c  00012200
    Handle 3c4
      Name         	...\Microsoft SDK\include\Unknwn.Idl
    0012e520  000013fb
    Handle 3c0
      Name         	...\Microsoft SDK\include\WTypes.Idl
    0012dec4  00009ea7
    Handle 3d8
      Name         	...\Microsoft SDK\include\OCIdl.Idl
    0012f1d8  0000a35b
    Handle 3c8
      Name         	...\Microsoft SDK\include\OleIdl.Idl
    0012eb7c  00006d48
    Handle 3c8
      Name         	...\Microsoft SDK\include\ServProv.Idl
    0012eb7c  00000e93
    Handle 3c8
      Name         	...\Microsoft SDK\include\UrlMon.Idl
    0012eb7c  0001cd2f
    Handle 3c4
      Name         	...\Microsoft SDK\include\MsXml.Idl
    0012e520  00003048
    eax=00000000 ebx=7ffdf000 ecx=00000000 edx=00000000 esi=7846ee04 edi=00000000
    eip=7846ee0f esp=0012fe00 ebp=0012fec8 iopl=0         nv up ei pl zr na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
    ntdll!NtTerminateProcess+0xb:
    7846ee0f c20800           ret     0x8
    

    Agora temos um log bem menor porque só fazemos uma chamada à ReadFile por arquivo. Agora sabemos exatamente o motivo pelo qual nosso I/O é mais rápido.

    Em 08/09/2005 09:27 - Comentários (2)

    WTF?

    Como eu não terminei o novo post da série de performance, não postei nada semana passada, passei o fim de semana todo lendo os PPTs do PDC e não tenho nada de realmente útil para postar, vamos ao besteirol.

    Para reduzir o vazio existencial do meu RSS, vou colocar algumas seleções particulares do "Daily What The Fuck", site que enumera aqueles erros estranhos em softwares e afins, além daquelas imbecilidades que nós somos obrigados a aguentar quando damos manutenção em programas que, quando perguntamos pelo autor, ninguém sabe que foi (ou quem fui). Lembra do "Códigos que eu tenho que aguentar"?.

    Mensagens de erro pra lá de estúpidas
    Parece que alguém sabe contar

    Putz, eu poderia ter feito um post sobre a disponibilização para downloads dos PPTs das apresentações do PDC, assim pareceria um post menos inútil. Agora já foi, estou com preguiça de reescrever...

    Em 19/09/2005 16:15 - Comentários (0)

    Usando Win32 API para otimizar o I/O, parte 6

    Parte Zero     Parte 1     Parte 2     Parte 3     Parte 4     Parte 5     Fontes do parser

    Depois de turbulências, mudanças de endereço e besteiras, é hora de dar continuidade à série sobre otimização de desempenho de I/O. Nos nossos exemplos, eu usei um interpretador de IDL que deve ler o conteúdo do arquivo em questão e vários imports, para mostrar o grau de otimização que é possível usando a API Win32 ao invés da runtime do C++.

    Só para esclarecer: a runtime do C++ não é lenta, é até que bastante rápida. O grande negócio é que qualquer abstração deixa o código mais lento, já que são mais camadas intermediárias para fazer o que realmente se quer, e mais código será rodado para fazer a mesma coisa. É esse o motivo pelo qual, na esmagadora maioria das vezes, um programa feito em Visual C++ é bem mais rápido do que um feito em .NET ou Java. Além de abstrair a API nativa do sistema operacional - que a runtime do C++ também faz - o Java e o .NET abstraem também a arquitetura do computador onde o programa está rodando. A API nativa do Windows é a Win32, qualquer coisa que não use a API diretamente (VB6, Delphi, Java, .NET, [coloque-algo-que-não-seja-C-e-C++-puros-aqui] fica sempre mais lento do que usar a API diretamente. O fato é que as vezes a diferença é muito pequena (como no caso do Delphi) e não vale a pena o trabalho de usar Win32 (que não é pequeno). Na realidade, hoje todo mundo usa alguma abstração sobre Win32, seja ela uma grande abstração (.NET) ou uma pequena abstração (MFC).

    Nessa nossa otimização, iremos usar um recurso do Windows chamado File Mapping. Esse recurso permite mapear um trecho de um arquivo diretamente na memória, e a medida que a memória é lida, o conteúdo do arquivo é colocado nela automaticamente. Para o programa, é como se o conteúdo do arquivo já estivesse na memória, mas na realidade o arquivo é lido a medida que o programa acessa essa memória. Isso evita buffers intermediários, e no final das contas, é mais simples do que alocar memória, ler o arquivo e depois tratar.

    O File Mapping é implementado em kernel mode como um recurso chamado Section. Quando você mapeia um trecho do arquivo na memória (usando MapViewOfFile em user mode), o arquivo não é lido para memória na hora. As páginas de memória são marcadas como inválidas, o que faz com o que sejá disparada uma exceção quando essa memória é acessada. Essa exceção é tratada pelo Memory Manager, que faz a leitura do trecho acessado do arquivo para essa memória, marca a página como válida e retorna o controle para o programa, que agora terá o conteúdo esperado do arquivo nessa memória. O File Mapping também pode ser usado para compartilhar memória entre os programas. O Windows usa esse recurso para carregar executáveis e DLLs na memória, o que faz com que o conteúdo de uma DLL só seja carregado uma vez para todos os processos que estão usando-a.

    Para simplificar o uso do File Mapping no nosso interpretador e para isolar um pouco o código Win32 do nosso código, criei uma classe simples para tratar o File Mapping:

    //
    // Classe que mapeia um arquivo inteiro na memória
    // usando FileMapping
    //
    // Rodrigo Strauss - http://www.1bit.com.br
    //
    class Win32FileMapping
    {
       HANDLE m_hFile, m_hFileMapping;
       DWORD m_dwFileSize;
       void* m_p;
    
       void Clean()
       {
          m_hFileMapping = NULL;
          m_hFile = INVALID_HANDLE_VALUE;
          m_p = NULL;
          m_dwFileSize = 0;
       }
    
    public:
       Win32FileMapping()
       {
          Clean();
       }
    
       ~Win32FileMapping()
       {
          Free();
       }
    
       bool IsValid()
       {
          return m_p && m_hFileMapping && m_hFile != INVALID_HANDLE_VALUE;
       }
    
       void Free()
       {
          if(m_p)
             UnmapViewOfFile(m_p);
    
          if(m_hFileMapping)
             CloseHandle(m_hFileMapping);
    
          if(m_hFile != INVALID_HANDLE_VALUE)
             CloseHandle(m_hFile);
    
          Clean();
       }
    
       //
       // Essa função mapeia o arquivo inteiro na memória
       //
       bool MapFile(const char* fileName)
       {
          Free();
    
          m_hFile = CreateFile(fileName, 
                               GENERIC_READ, 
                               FILE_SHARE_READ, 
                               NULL, 
                               OPEN_EXISTING, 
                               NULL, 
                               NULL);
    
          if(m_hFile == INVALID_HANDLE_VALUE)
             return false;
    
          m_dwFileSize = GetFileSize(m_hFile, NULL);
    
          m_hFileMapping = CreateFileMapping(m_hFile, 
                                             NULL,
                                             PAGE_READONLY, 
                                             0,
                                             0, 
                                             NULL);
    
          if(m_hFileMapping == NULL)
        {
         Free();
             return false;
        }
    
          m_p = MapViewOfFile(m_hFileMapping, FILE_MAP_READ, 0, 0, 0);
    
          return true;
       }
    
       template<typename T>
       T GetStart()
       {
          return reinterpret_cast<T>(m_p);
       }
    
       template<typename T>
       T GetEnd()
       {
          if(!m_dwFileSize)
             return NULL;
    
          return reinterpret_cast<T>( ((unsigned char*)m_p) + m_dwFileSize );
       }
    };
    

    Essa classe mapeia o arquivo inteiro na memória, e retorna os ponteiros de início de de fim através das funções template GetStart() e GetEnd(). Note que o GetEnd() retorna um ponteiro para o primeiro T depois do fim do arquivo. Esse o mesmo conceito usado pelos containers STL, onde o end() retorna um elemento inválido após o último item. O último item de um container é [container.end() - 1] (se, e somente se container.begin() != container.end())

    O boost::tokenizer tem duas opções de inicialização: passar uma string com o conteúdo a ser interpretado, ou um iterator inicial e um iterator final. No nosso caso, o iterator inicial é GetStart() e o final GetEnd(). Não se esqueça que em STL, o conceito iterator é uma generalização de um ponteiro: um objeto que aponte para outro objeto (ou seja, sobrecarregue o operador *) e que (não necessariamente) possa ser incrementado e decrementado (operadores ++ e --) para acessar outros itens (para mais detalhes veja a documentação no site da SGI). Então um ponteiro É um iterator, o que resolve o nosso problema de uma forma bem simples.

    Vamos ao código (é por isso que você está lendo isso, não é?):

    void MidlParser::ParseMidlFile(const char* fileName)
    {
       ...
       Win32FileMapping fileMapping;
    
    
       if(m_bParseAsImportFile)
       {
          //
          // tenta abrir o imported file nas pastas de include
          //
        for(vector<string>::const_iterator i = m_IncludePaths.begin() ;
                i != m_IncludePaths.end() ; 
                ++i)
          {
             string str;
    
             str = *i + fileName;
    
             fileMapping.MapFile(str.c_str());
    
             if(fileMapping.IsValid())
                break;
          }
    
          if(!fileMapping.IsValid())
             throw ParseException(string("error opening import file \"") 
           + fileName + "\"", *this, m_parsedFileName);
    
          //
          // inicializa o tokenizer com os ponteiros do FileMapping
          //
          m_Tokenizer.assign(fileMapping.GetStart<char*>(),
                             fileMapping.GetEnd<char*>(),
                             char_separator<char>("\t\r " , "\n\"*,;:{}/\[]()"));
    
    
          m_ParsedIncludeFiles.push_back(fileName);
    
       }
       else
       {
          fileMapping.MapFile(fileName);
          
          if(!fileMapping.IsValid())
             throw ParseException(string("error opening file \"") 
           + fileName + "\"", *this, m_parsedFileName);
    
          //
          // inicializa o tokenizer com os ponteiros do FileMapping
          //
          m_Tokenizer.assign(fileMapping.GetStart<char*>(),
             fileMapping.GetEnd<char*>(),
             char_separator<char>("\t\r " , "\n\"*,;:{}/\[]()"));
    
          m_parsedFileName = fileName;
    
          ...
       }
    }
    

    Como visto, nosso código não ficou mais complicado por causa do File Mapping. Nossa classe facilitou muito e deixou o código claro, além da inteligente decisão de fazer uma classe simples que resolvesse nosso problema pontual ao invés de tentar fazer um framework genérico para uso avançado de File Mapping. Um péssimo costume de "programadores orientados à objetos" é fazer classes super genéricas que resolvam todas as situações possíveis e imagináveis naquela área (eu sei bem porque eu era assim...).

    Chega de yada-yada, vamos aos números:

    Média de tempo (50 chamadas):

    228,01 ms - versão usando a runtime do C++
    214,68 ms - versão usando CreateFile
    204,17 ms - versão usando CreateFile com FILE_FLAG_SEQUENTIAL_SCAN
    186,55 ms - versão usando File Mapping

    Comparação entre a implementação com File Mapping e as anteriores
    22,22% - Melhoria em relação versão que usa a runtime do C++ (versão inicial)
    15,08% - Melhoria em relação versão que usa CreateFile
    09,44% - Melhoria em relação versão que usa CreateFile com FILE_FLAG_SEQUENTIAL_SCAN

    Nossa melhoria em relação à versão inicial foi de 22%, e as mudanças foram pequenas e pontuais. Nessa última tentativa, fizemos a classe de File Mapping (menos de 90 linhas de código e anos de estudo de C++ e Win32) e modificamos o código que carrega o arquivo (menos de 10 linhas de código e mais de 10 livros de programação depois). O nosso programa de exemplo não é tão I/O intensive, mas nossa otimização melhorou bastante o desempenho em termos pencentuais.

    Mesmo assim, há algo para se pensar: será que valeu a pena? Essa série foi interessante para ilustrar conceitos de otimização de performance e Win32, mas agora chega a hora de explicar mais um conceito: comparação percentual não é suficiente para fazer um estudo sobre uma determinada otimização. 22% é uma otimização considerável, mas e em termos absolutos? Será que 41,46 milisegundos é algo que faça diferença no tempo de compilação? Nosso caso o esforço de programação foi pequeno (o de escrever os posts foi imensamente maior), mas mesmo assim deve ser levado em consideração.

    Quando for mensurar alguma otimização, considere TODOS os parâmetros, inclusive os intangíveis, como o impacto que essa otimização causará no usuário. No caso do nosso interpretador, talvez ~40ms não faça tanta diferença. Mas em uma chamada de componente que roda no backend de uma bolsa de valores e que é chamado milhões de vezes em um único dia, 5ms faz uma diferença MUITO grande.

    Lembre-se: programação é uma arte.

    Em 27/09/2005 10:44 - Comentários (2)

    Quando a Microsoft quer portabilidade, ela usa C++

    O Windows Presentation Foundation - antigo Avalon - ou WPF é a novo "subsistema" de apresentação do Windows. Metade dele foi feito em C#, com o backend baseado em DirectX para conseguir a perfomance necessária. As novidades são muitas. Além de ser managed, essa plataforma possibilita o design declarativo usando XAML (um XML que descreve as janelas/canvas/controles, algo como o SVG), permite integração de vídeo, animação, gráficos vetoriais e possui um sistema de roteamento de eventos entre os controles bem interessante. O WPF será nativo no Windows Vista (antigo "code-named Longhorn") e haverá uma versão que rodará em Windows XP SP2 e Windows Server 2003 SP1.

    No PDC 2005, foi apresentada uma versão do WPF chamada WPF/E, que é a "Windows Presentation Foundation Everywhere". Essa é a versão portável da WPF, que a Microsoft disponibilizará para outras plataformas. Na demostração dessa versão (reduzida) do WPF, o palestrante usou browser Safari, em um Mac, e abriu tranquilamente um arquivo XAML.

    O detalhe interessante é que o WPF/E foi implementada em C++, e não em C#. Afinal, ele tem que ser portável...

    Em 30/09/2005 10:27 - Comentários (0)

    Chernobyl: O retrato do limite da estupidez humana

    Meu amigo Gilberto me mandou um link com a linha de tempo dos eventos que causaram o acidente de Chernobyl. Eu já tinha lido sobre o acidente - inclusive sobre a moça que percorreu a cidade fantasma - mas nunca uma descrição que mostrasse o grau de estupidez que foi necessária para que o acidente acontecesse.

    O acidente aconteceu durante um teste para ver se o gerador, em baixa potência, conseguia se retroalimentar o suficiente para se resfriar, no caso dos geradores de backup demorarem para entrar em funcionamento. Os operadores e engenheiros fizeram algo como "desliga o tratamento de erro, log e backup que nós vamos fazer um teste rápido em produção". Detalhe: era uma usina nuclear, e não um servidor de produção. O trecho que deixa mais evidente os procedimentos adotados é o seguinte:

    "O operador tentava, sem êxito, por meio de controles manuais, manter os parâmetros para o reator poder funcionar com segurança. A pressão de vapor e o nível da água caíram abaixo do permitido, fazendo soar os alarmes que exigiam o desligamento do reator. O operador desligou o próprio sistema de alarme."

    Eles fizeram o equivalente em C++ (que eu já vi MUITO por aí) a:

    int main
    {
      try
      {
        // um monte de código
      }
      catch(...)
      {
        // nada aqui
      }
      return 0;
    }
    

    Isso serve para que todos lembrem:

  • Sistemas de segurança devem estar ativados, sempre. Se seu sistema tem uma contingência, ela deve receber o mesmo - ou até mais - cuidado do que o sistema principal. Quando algo der errado e seu sistema de segurança ou contingência não funcionar, a probabilidade de algum desesperado fazer uma besteira é muito maior.
  • O backups devem estar sempre ativados e funcionando, pois você nunca sabe quando precisará deles. O mais importante: teste a recuperação do seu backup e se ele funciona em situações extremas. Em Chernobyl, quando eles ativaram o sistema de segurança para desabilitar tudo, descobriram que esse sistema não funcionava em situações extremas.
  • Nunca pense que o pior não vai acontecer. Todo elevador (e toda usina nuclear) tem um sistema de segurança simples e muito eficiente que evita que ele caia caso o cabo arrebente. Você só ouve estórias de elevadores que cairam porque algum técnico imbecil achou que não haveria problema em deixar o freio de segurança desligado por alguns dias. Não deixe o no-break do seu servidor sem bateria "por alguns dias", você pode se arrepender.
  • Ou, como o Gilberto disse, "foi genial, eles deveriam abrir uma empresa de software". Aí sim eles estariam no ramo certo...

    Em 04/10/2005 14:47 - Comentários (5)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: COM

    Outros posts dessa mesma série:   Win32     ATL     MFC

    COM - sigla para Component Object Model - é um padrão binário de componentes que foi criado para facilitar o uso de objetos entre as diversas linguagens de programação. Sendo uma das tecnologias mais importantes desenvolvidas pela Microsoft, ela é usada em praticamente todas as partes do Windows e em quase todos os produtos da Microsoft.

    Os pilares básicos desse tecnologia são o acesso aos objetos a partir de interfaces, uma interface padrão (IUnknown) para controle de referência e acesso às outras interfaces (da qual todas as interfaces criadas devem herdar), e o padrão binário da vtable (tabela com os ponteiros para as funções da interface) para que os objetos feitos em diversas linguagens possam se comunicar entre si. Para um objeto ser chamado de objeto COM, ele só precisa seguir essas premissas. Quando a implementação é em C++, isso que dizer que o objeto deve herdar de IUnknown e implementar seu três métodos:

    interface IUnknown
    {
        HRESULT QueryInterface(REFIID riid,[out]void **ppvObject);
        ULONG AddRef();
        ULONG Release();
    };
    

    No seu uso mais comum, além das premissas que eu já enumerei, é necessário registrar o objeto e o objeto que sabe criá-lo (objeto que implementa a interface IClassFactory). Entre as linguagem compatíveis com COM estão: C, C++, Delphi, VBScript, JScript, Visual Basic 4/5/6, todas as linguagens .NET (C#, VB, etc), PHP, Python, Perl e outras.

    O COM serve de base para diversas outras tecnologias criadas pela Microsoft, que veremos mais a frente. As tecnologias baseadas em COM são geralmente nada mais são do que uma lista de interfaces COM que determinam a comunicação entre os diversos componentes (de um modo geral, não necessariamente componentes COM). Para ser um COM, um objeto só precisa implementar a interface IUnknown. Para ser um controle ActiveX (ActiveX é uma das tecnologias baseadas em COM), um objeto precisa implementar IUnknown e mais as interfaces específicas da tecnologia ActiveX. Veja algumas tecnologias que usam COM como base:

    • ActiveX: Usada para criar controles (ActiveX control) que tem interação com o usuário. Seu uso é muito comum no Visual Basic 6 e anteriores.O próprio Internet Explorer nada mais é do que um programa que hospeda o controle ActiveX de renderização de HTML da Microsoft (mshtml.dll).
    • OLE: Object Linking and Embedding é a tecnologia de troca de dados entre aplicativos que sucedeu o DDE. Quando você copia um desenho no CorelDRAW! e cola no Microsoft Word, toda a comunicação entre eles para que isso seja possível é feita usado OLE. Nesse exemplo específico, o Microsoft Word chama um objeto COM/OLE do CorelDRAW! solicitando que ele desenhe o objeto para que o Word possa exibí-lo.
    • Structured Storage: Parte do OLE, permite que um arquivo possa guardar informações de vários programas. Voltando ao nosso exemplo anterior, quando o usuário salva um documento Microsoft Word que contém um gráfico do CorelDRAW!, o Word faz uma chamada para um objeto COM/OLE do CorelDRAW!, que salva o seu desenho dentro do arquivo DOC do Word. Quando o documento é carregado, ocorre o inverso.
    • MMC: O Microsoft Management Console é uma iniciativa da Microsoft de padronização da aparência e funcionamento das ferramentas de gerenciamento do Windows. O Snapins do MMC nada mais são do que objetos COM que implementam as interfaces específicas para se comunicar com o MMC. Para entender melhor, vá em "Start Menu" >> "Run" ("Menu Iniciar" >> "Executar" para alguns), digite "compmgmt.msc". Tudo que você vê aí é implementado em COM.
    • Windows Shell: A API para usar os recursos do Windows Shell (basicamente Windows Explorer) é exportada como objetos COM. Já mostrei antes um exemplo de como mudar a imagem de fundo do desktop usando o objeto COM do ActiveDesktop.
    • Shell Extensions: Quando você instala o WinZip ou WinRAR, eles adicionam ao menu de contexto do Windows Explorer opções para manipulação de arquivos ZIP ou RAR e para compactar arquivos comuns. Quem controla isso são objetos COM chamados pelo Explorer e registrados pelos respectivos aplicativos.
    • Internet Explorer: Todas as barras de ferramentas personalizadas do Internet Explorer (como as feitas pelo Yahoo! e Google) são objetos COM. Na realidade, todas as extensões feitas ao Internet Explorer são objetos COM, seja barra de ferramentas, um Pane (se você usa IE, use o CTRL+H para descobrir o que é um Pane), seja um Browser Helper Objects.
    • Automation: É a possibilidade de controlar um aplicativo usando objetos COM exportados por ele. Entre os aplicativos que suportam automation estão todos do pacote Office (Word, Excel, etc). As extensões para aplicativos feitas em VBA nada mais são do que programas VB(A) que usam esses objetos Automation para controlá-los. Hoje em dia é possível fazer em .NET o que se fazia com o VBA, mas os objetos .NET nada mais fazem do que repassar as chamadas para os objetos COM.
    • DirectX: O DirectX é 100% implementado como objetos COM.
    • Microsoft Office Addins, Extensions: Todos os Addins e extensões para o Office são feitos em COM, inclusive exemplos conhecidos como a intergração do Google Desktop Search e do MSN Desktop Search com o Outlook. Hoje é possível fazê-los em .NET, já que a Microsoft provê objetos .NET que encapsulam os objetos COM do Office (ou seja, no final das contas, é tudo COM).

    Além dessas tecnologias que eu detalhei, existem várias outras, como WMI, Scripting, ASP, Windows Media Player e outros.

    Eu tinha preparado uma lista de programas Microsoft ou módulos do Windows que usam COM (Windows Explorer, Internet Explorer, Office, Exchange, SQL Server, ISA, Visual Studio, etc, etc, etc). Depois cheguei a conclusão que é mais fácil listar os módulos que não usam COM: o kernel do Windows (que é feito em C, não em C++), o Bloco de Notas e a Calculadora. Alguém conhece mais algum que não use nada de COM?

    Em 16/10/2005 11:15 - Comentários (7)

    Meu apelo: tirem seus blogs do TheSpoke

    Faço aqui meu apelo para que os blogger brasileros tirem seus blogs do TheSpoke e mudem-se para um site melhor. O TheSpoke novo consegue ser pior do que a primeira versão (que já era bem ruim), e os responsáveis fizeram uma péssima migração . Aqui vão meus motivos:

    • É necessário efetuar login para fazer comentário. É necessário efetuar login para votar em uma pesquisa. É necessário efetuar login se você clica em "Community" >> "Members". Isso é um portal de blogs (e coisas inúteis como fotos) ou um clubinho fechado?
    • Eu não gosto da navegação do site, acho confusa demais. Você clica em "Community" >> "Blogs" e vai para uma lista de posts, não para uma lista de blogs. Estando em um post (como esse), onde eu clico para chegar na página principal do blog? Se eu clico no username do blogger ele me pede senha. Grrr...
    • Ele é muito mal feito. Eu estava tentando colocar um comentário e não lembrava se já criei um username. Tentei criar um usuário e inocentemente pensei que, se eu já tivesse criado um usuário rodrigostrauss, ele avisaria. A mensagem de erro foi "An error occured while attempting to create this user". Já vi muito estagiário fazer melhor.
    • A função de search é muito ruim. Procurando "Alfred Gary Myers", por exemplo, não retorna nenhum resultado. Eu ainda não achei uma forma de encontrar um blog lá sem ser pelo Google. E depois da migração, nem o Google está encontrando.
    • Já ví vários "Error - We are currently unable to serve your request". Só faltou complementar a mensagem com "Bad, bad server. No donut for you".
    • O RSS é resumido. Eu gosto de ler os posts no meu leitor de blogs, e não ter que clicar e ir até o site. Está faltando dinheiro para pagar bandwidth ou foi só uma péssima idéia mesmo?

    Já me falaram que é bom ter blog no TheSpoke por causa da visibilidade. Que visibilidade se é impossível encontrar alguma coisa lá e acho que o Google não se entendeu direito com ele? Eu acho que não vale a pena passar por tudo isso. É só escrever alguma coisa interessante (como código) que o próprio Google se encarrega de te dar visitantes, mesmo fora de um portal de blogs (como é o meu caso).

    Ok, o TheSpoke2 tem uma vantagem em relação ao primeiro. Não vi mais aquelas pesquisas do tipo "Qual o sentido da vida?" na primeira página.

    Em 19/10/2005 10:29 - Comentários (4)

    Tem gente que veste a camisa da empresa

    Lembra quando eu falei que eu não visto a camisa da empresa? Pois é, tem muita gente que veste...

    Em 20/10/2005 09:57 - Comentários (0)

    Explicando a sopa de letrinhas da programação C/C++ para Windows: ATL

    Outros posts dessa mesma série:   Win32     COM     MFC

    ATL - Active Template Library - é uma biblioteca de templates C++ criada pela Microsoft para simplificar a programação de objetos COM/OLE/ActiveX em C++. Ela foi criada inicialmente pela equipe do Visual Basic para simplificar o desenvolvimento interno, já que o VB até a versão 6 é todo baseado em COM. Hoje em dia ela é distribuída junto com todas as versões do Visual C++. É uma biblioteca pouco intrusiva, implementada em camadas e que tem um overhead muito pequeno, por ser baseada em templates. É usada pela Microsoft internamente em seus produtos, como o Windows Explorer, Windows Movie Maker, MMC e vários outros (ao contrário da MFC, que é pouco usada dentro da Microsoft).

    O desenvolvimento COM em C++, apesar de não ser muito complicado, é trabalhoso. Muitas interfaces que precisam ser implementadas contém muitas funções cujo código de implementação é o mesmo para todos os componentes, o que torna o trabalho chato e repetitivo. A implementação da interface IUnknown, por exemplo, é sempre a mesma: controle de referência e solicitação das interfaces suportadas.

    Além de suportar a implementação de objetos COM, a ATL é uma biblioteca com diversas classes e templates que facilitam muito a programação Windows, como classes para acesso ao registro, comunicação HTTP e SMTP, criptografia, BASE64, acesso à arquivos, ACLs, listas e hashmaps, etc.

    Como um trecho de código vale muito mais do que 186.112.794 palavras, veja como é implementado um objeto COM e seu Class Factory em C++ puro:

    //
    // interface do nosso objeto COM
    //
    __interface __declspec(uuid("977BF132-B6B6-4d70-88BD-C427A2724B48"))
    ITest : IUnknown
    {
       HRESULT WINAPI Method1(BSTR str, ULONG ul);
    };
    
    //
    // Objeto que implementa a class ITest
    //
    class CTest : public ITest
    {
       DWORD m_ref;
    public:
       CTest()
       {
          m_ref = 0;
       }
    
       //
       // implementação de IUnknown
       //
       STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
       {
          if(InlineIsEqualUnknown(riid))
          {
             AddRef();
             *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
          }
          else if(InlineIsEqualGUID(riid, __uuidof(ITest)))
          {
             AddRef();
             *ppvObject = static_cast<ITest*>(this);
             return S_OK;
          }
          
          return E_NOINTERFACE;
       }
    
       STDMETHOD_(ULONG,AddRef)()
       {
          return ++m_ref;
       }
    
       STDMETHOD_(ULONG,Release)()
       {
          DWORD ref = --m_ref;
    
          if(ref == 0)
             delete this;
    
          return ref;
       }
    
       //
       // implementação de ITest
       //
       STDMETHOD(Method1)(BSTR str, ULONG ul)
       {
          MessageBoxW(NULL, str, L"", MB_ICONEXCLAMATION);
          return S_OK;
       }
    };
    //
    // Class Factory para o nosso obejto
    //
    class CTestClassFactory : public IClassFactory
    {
       DWORD m_ref;
    public:
       CTestClassFactory()
       {
          m_ref = 0;
       }
    
       //
       // quando o objeto é registrado, a runtime do Microsoft COM 
       // chama a função GetClassObject exportada pela DLL do objeto.
       // como vamos fazer tudo na mão agora, vamos criar esse helper
       //
       static HRESULT CreateClassFactory(REFIID riid, void **ppv)
       {
          HRESULT hr;
          IUnknown* p;
    
          try
          {
             p = new CTestClassFactory();
          }
          catch(...)
          {
             return E_OUTOFMEMORY;
          }
    
          p->AddRef();
    
          hr = p->QueryInterface(riid, ppv);
    
          p->Release();
    
          return hr;
       }
    
       //
       // implementação do IClassFactory
       //
       STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
       {
          HRESULT hr;
          IUnknown* pUnk;
    
          if(pUnkOuter)
             return CLASS_E_NOAGGREGATION;
    
          try
          {
             //
             // pelo padrão C++, se o new falha uma exceção é disparada
             //
             pUnk = new CTest();
          }
          catch(...)
          {
             return E_OUTOFMEMORY;
          }
          
          pUnk->AddRef();
    
          hr = pUnk->QueryInterface(riid, ppvObject);
    
          
          pUnk->Release();
    
          return hr;
       }
       STDMETHOD(LockServer)(BOOL fLock)
       {
          return S_OK;
       }
    
       //
       // implementação de IUnknown
       //
       STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
       {
          if(InlineIsEqualUnknown(riid))
          {
             AddRef();
             *ppvObject = this;
             return S_OK;
          }
          else if(InlineIsEqualGUID(riid, IID_IClassFactory))
          {
             AddRef();
             *ppvObject = static_cast<IClassFactory*>(this);
             return S_OK;
          }
    
          return E_NOINTERFACE;
       }
    
       STDMETHOD_(ULONG,AddRef)()
       {
          return ++m_ref;
       }
    
       STDMETHOD_(ULONG,Release)()
       {
          DWORD ref = --m_ref;
    
          if(ref == 0)
             delete this;
    
          return ref;
       }
    };
    
    //
    // E no sétimo dia Deus disse: "int main"
    //
    int main()
    {
       HRESULT hr;
       BSTR bstr;
       IClassFactory* pClassFactory;
       ITest* pTest;
    
       //
       // criando o class factory
       //
       hr = CTestClassFactory::CreateClassFactory(IID_IClassFactory, (void**)&pClassFactory);
       if(FAILED(hr))
          return hr;
    
       //
       // solicitando gentilmente para que ele crie um objeto daquele tipo
       //
       hr = pClassFactory->CreateInstance(NULL, __uuidof(ITest), (void**)&pTest);
       if(FAILED(hr))
       {
          pClassFactory->Release();
          return hr;
       }
    
       //
       // usando o objeto
       //
       bstr = SysAllocString(L"Uma string bem legal");
       
       hr = pTest->Method1(bstr, 20);
       
       SysFreeString(bstr);
    
       if(FAILED(hr))
       {
          pClassFactory->Release();
          pTest->Release();
          return hr;
       }
    
       //
       // liberar as interfaces
       //
       pClassFactory->Release();
       pTest->Release();
    
    
       return S_OK;
    }

    Lembre-se, a única coisa realmente útil para nós nesse código é a implementação de ITest::Method1. O resto é tudo suporte ao contador de referências que todo objeto COM deve ter. Com ATL, nossa implementação seria assim:

    class ATL_NO_VTABLE CTest1 : 
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CTest1, &CLSID_Test1>,
      public ITest1
    {
    public:
    
       DECLARE_REGISTRY_RESOURCEID(IDR_TEST1)
    
       BEGIN_COM_MAP(CTest1)
           COM_INTERFACE_ENTRY(ITest1)
       END_COM_MAP()
    
    public:
    
       STDMETHOD(Method1)(BSTR p1, DWORD p2)
       {
          MessageBoxW(NULL, str, L"", MB_ICONEXCLAMATION);
          return S_OK;
       }
    };
    
    int main()
    {
       HRESULT hr;
       CComPtr<ITest1> pTest;
       
       hr = CTest1::CreateInstance(NULL, &pTest);
       if(FAILED(hr))
          return hr;
    
       hr = pTest->Method1(CComBSTR(L"Uma string muuuuito mais legal"), 150);
       if(FAILED(hr))
          return hr;
    
       //
       // "olhe mamãe, eu sei coletar meu próprio lixo"
       //
    
       return S_OK;
    }

    Explicação: STDMETHOD nada mais é do que uma macro que coloca o retorno da função com HRESULT (padrão de retorno de erros COM) e coloca o calling convention como __stdcall, o mesmo das APIs do Windows.

    Salvamos algumas dezenas de linhas de código usando ATL, já que ele tem uma implementação para IUnknown (single threaded e multi threaded, a nossa é só single) e um Class Factory, além de toda a implementação para que o nosso objeto seja registrado e usado por qualquer cliente COM, seja VB6, .NET, Delphi, etc. Se estivessemos fazendo um servidor OLE, a quantidade de código boilerplate que deixaríamos de escrever seria da ordem de centenas de linhas. E com a elegância e leveza que só o ATL tem :-)

    Em 24/10/2005 22:55 - Comentários (1)

    Por que sempre tentamos tudo do jeito mais difícil?

    Eu sou um leitor assíduo do Slashdot (para mais informações veja o artigo sobre o Slashdot na Wikipedia), aponto meu browser para lá algumas dezenas de vezes por dia. Uma das partes do Slashdot que eu mais gosto é a "Ask Slashdot", onde alguém com um problema de TI para resolver pergunta aos leitores e eles entopem os comentários de repostas (úteis e inúteis). Eu já aprendi várias coisas lendo o Ask Slashdot e passei a conhecer vários softwares e soluções que eu nem sabia que existiam. Alguns funcionários de empresas gigantescas, que já tiveram problemas gigantescos e souberam como resolver colocam suas experiências lá quase sempre, além de acadêmicos, pesquisadores e outros escovadores de bit de alto nível.

    Um dos "perguntadores" de hoje disse que precisa criar um sistema de storage (armazenamento) com até 1 Petabyte de capacidade (~1024 TB, ~1024*1024 GB) com hardware barato e comum, sem gastar muito dinheiro. Além disso, ele disse que no começo não precisava de total segurança dos dados (acho difícil alguém ter 1 PB de dados e não precisar de backup, mas tudo bem...).

    Estou rindo até agora com um comentário que resume bem o pedido dele e de dezenas de pessoas que recorrem diariamente ao Ask Slashdot:

    Dear Slashdot,
    I have been tasked with (insert very difficult, very important job). This is very important to my company. I have (insert number much lower than it should be) dollars to do this. I do not want to use (insert company name specializing in this exact thing) because management thinks they are too expensive. I think I can do this (insert better/faster/cheaper/...) than said company, even though they have vastly more experience and have invested much more time and research than I have. My continued and future employment probably rests on this project. Please advise.

    Por que sempre tentamos fazer as coisas do jeito mais difícil? Ou melhor, por que as pessoas que administram as empresas sempre pedem para que façamos as coisas do jeito mais difícil?

    Em 25/10/2005 20:09 - Comentários (3)

    Mais um blog brasileiro sobre C++

    Wanderley Caloni Jr - meu velho companheiro de escovação de bits e especialista em padrão C e C++ - resolveu criar vergonha na cara e compartilhar seus conhecimentos com a humanidade. Ele começou a escrever um blog, onde além de falar sobre C e C++, ele fala também sobre o Borland C++ Builder, o único e verdadeiro ambiente RAD para C++ na plataforma Windows. Eu venho acompanhando os post sobre Builder desde o começo, é muito bom respirar ares não-Microsoft de vez em quando (considerando que estou estudando Python, está virando "de vez em sempre"). Além disso o nome Borland me faz lembrar o "Borland Turbo C++ for DOS", nos meus felizes primeiros dias de estudo C++...

    O blog dele é uma ótima leitura, porque além de falar sobre C++, ele escreve código no blog - coisa bem escassa no Brasil....

    Em 27/10/2005 09:58 - Comentários (6)

    Visual Studio 2005 já disponível

    O Visual Studio 2005, versão final, já está disponível para download aos assinantes MSDN em http://msdn.microsoft.com/subscriptions/.

    Visual Studio 2005 significa:

    E a nova MSDN é compatível com o Firefox, o que é muito bom. Ah, e claro, tem também o .NET Framework 2.0 e o C# 2.0.

    Em 27/10/2005 15:54 - Comentários (4)

    Vista a camisa da empresa se puder

    Em um dos meus últimos posts eu coloquei um link para meu post "Eu não visto a camisa da empresa", e acabei por consequência revivendo-o. (Blogs têm esse problema de navegação, os post antigos ficam adormecidos até que alguém os ache pelo Google ou que você coloque um link para ele). Esse post, juntamente com o "Os contos que as empresas contam" gerou bastante discussão sobre o relacionamento entre funcionário/consultor e empresa e a postura de cada um, já que no post eu coloquei qual foi a postura que eu adotei depois das pouco mais de 10 empresas onde eu trabalhei com informática desde que eu tinha 15 anos, que foi quando eu comecei.

    Não sou muito de re-explicar meus posts, mas um comentário colocado no post "Eu não visto a camisa da empresa" me fez escrever uma resposta tão grande que eu resolvi transformar em post, por dois motivos. Um é para dar oportunidade de gerar mais discussão sobre o tema. O outro é que eu acho que o respeito com que o Daniel abordou o tema merece uma resposta e uma consideração. Da mesma forma que o mais comum é as empresas não respeitarem seus funcionários, o mais mais comum é não ter muito respeito nos comentários quando a opinião não é igual a minha. Recomendo que você leia o comentário do Daniel antes de ler o resto desse post.

    Segue aqui minha meio-resposta-meio-post-meia-calabresa:

    Muito do que eu escrevo aqui no site são coisas que eu gostaria de ter lido quando eu tinha menos experiência no mercado, mas ninguém escrevia. Exemplos claros são os FAQs sobre C++, Win32, COM, ATL, etc. Muitas pessoas não estudam coisas que gostariam porque não sabem por onde começar. Então eu tento dar um começo para as pessoas, como em "Como ser um programador".

    Sobre o posts "Eu não visto a camisa da empresa" e "Os contos que as empresas contam", eu quero passar para as pessoas as más experiências que eu passei, para que elas tentem perceber essas situações quando elas estiverem acontecendo. Eu já passei por várias situações (e empresas) como as que eu citei. Infelizemente o mundo não está cheio de pessoas honestas, e eu conheço muita gente que já passou por essas situações. Existem MUITAS empresas de software que não dão o menor valor para seus programadores, MUITAS mesmo. Como já disse o Joel, tratar cientistas de foguete como crianças é uma péssima idéia. Não se esqueça que essas "crianças" podem colocar o curriculum na APINFO e desaparecer em uma semana. Quem mais perde com isso são as empresas e seus super-administradores.

    Existem também pessoas que têm sorte e conseguem trabalhar em uma empresa que dá valor aos seus funcionários e onde o ambiente de trabalho é bom. Essas pessoas podem e devem vestir a camisa da empresa, afinal, a empresa veste a camisa dos funcionários. Foi exatamente isso que eu disse. Algumas partes do post podem ser "radicais", mas eu explico meu ponto de vista em várias pontos, como "não visto a camisa de empresa nenhuma pelo simples fato de que nenhuma empresa até hoje vestiu a minha camisa". O motivo é simples. Quando vestirem, eu visto também. Eu já cansei de me dedicar sem receber nada em troca. Eu, como qualquer programador normal, só peço que me deixem programar em paz.

    As pessoas com mais tempo de mercado e mais vivência não precisam muito das informações que eu tentei passar nesses posts. Mas quem está começando precisa. Eu precisava quando comecei e não tive. Eu e muita gente que eu conheço foi iludida. E isso não é muito difícil, porque eu gosto muito do que faço e uma promessa de um projeto interessante muitas vezes vale mais pra mim do que uma promessa de aumento de salário.

    O Daniel cita no comentário que "nenhum trabalhador (em qualquer area) que possa se dizer dedicado e concentrado, tem tempo pra ficar lendo noticias do Slashdot "dezenas de vezes por dia"". Eu discordo e vou explicar o motivo. Slashdot fala sobre tecnologia, sobre meu trabalho. Todo economista DEVE ler muito sobre economia. Se um programador passa o dia lendo sobre esportes (conheço alguns assim), aí sim é um problema. Muitas das coisas que eu sei hoje se devem ao Slashdot, CNET, OSNews, MSDN Magazine, The Register, ArsTechnica, aos blogs que eu leio (Raymond Chen, Joel Spolsky, Eric Lippert, Mark Russinovich, etc). Isso obviamente toma tempo (não muito quando você usa RSS), mas minha visão como arquiteto de sistemas é muito mais ampla do que a de muita gente pelo fato de eu conhecer diversas tecnologias e me manter MUITO bem informado. Por exemplo, eu sei o que é AJAX e ATLAS e o cara que trabalha com ASP/HTML/JavaScript aqui na empresa não sabe. É importante se manter bem informado, é importante ler sobre tecnologia. Eu respiro tecnologia 24 horas por dia, e isso me ajudou inúmeras vezes e vai continuar ajudando. Além disso, os melhores programadores e arquitetos que eu conheço têm a mesma atitude quanto à isso. Eu recomendo que quem quer ser um bom programador faça o mesmo, mantenha-se informado. Sem deixar de programar é claro. "Dezenas de vezes" ao dia é quase uma hipérbole, e para ser um bom programador é preciso programar muito dia e noite. (Se eu não passasse o dia (e a noite) programando eu não teria conhecimento técnico para escrever o que escrevo no site, e teria mais um desses "blogs de tecnologia" pt-br que só repete os sites de notícias e anuncia eventos)

    Meu ponto foi e continua sendo: se você acha que vale a pena vestir a camisa, vista-a. Note que o "vale a pena" é um conceito muito pessoal. Isso pode "valer a pena" para uma pessoa pelo simples fato de ela ter um emprego. Minha decepção vem justamente do fato de eu ter tentado "vestir a camisa da empresa" várias vezes e não ter visto retribuição alguma. Vista a camisa da empresa se puder.

    Em 08/11/2005 12:14 - Comentários (7)

    As implicações do HyperThreading

    Slava Oks, da equipe do SQL Server, escreveu em um post recente que os clientes têm reportado problemas de queda de performance em servidores com processadores HT (como o Pentium 4 HT). Nesse post ele explica o problema e prova que ele realmente acontece usando um programa de exemplo. Veja o post dele para mais detalhes. E eu vou aproveitar esse assunto para divagar...

    O que acontece é o seguinte: apesar de ter dois núcleos lógicos separados, em um processador HT eles compartilham os caches L1 e L2 entre si. Isso quer dizer que, se uma thread que acessa uma grande quantidade de memória de uma vez (como o lazy writer do SQL ou uma thread que lida com streaming de outra aplicação qualquer) estiver rodando no mesmo processador físico, ele vai acabar por sujar o cache toda vez. Esse efeito de cache trash também acontece com um processador single core, mas o que piora a situação nesse caso é que, além do cache miss, a worker thread do SQL vai ter que esperar a lazy writer thread liberar um spinlock. Em um processador single core, um spinlock não trava coisa nenhuma, já que não há possibilidade do scheduler do Windows colocar duas threads para rodar ao mesmo tempo. O máximo que acontece (em kernel mode) é subir a IRQL para que nenhuma INT interrompa a thread enquanto ela está sincronizada.

    O problema de cache trash não ocorre quando dois processadores físicos são usados porque, como os caches são separados, assim que o segundo processador liberar o spinlock as estruturas usadas pela worker thread estaram no cache (muito provavelmente). Assim, a thread voltará rodando em velocidade muito maior do que uma thread no segundo core de um processador HT, já que ela teria vários roundtrips até a RAM graças aos cache miss.

    Expandindo o problema além do SQL Server, já é sabido que aplicações multimídia são os maiores "destruidores de cache", já que elas lêem grande quantidade de dados na memória de forma sequencial, sem reaproveitar. Os aplicativos comuns (digamos, não-multimídia) costumam acessar quase sempre as mesmas posições na memória, o que faz com que os caches do processador aumentem bastante o desempenho. Os caches L1 e L2 estão "mais perto" do processador, e seu tempo de acesso é bem menor do que o da memória RAM. Como as aplicações multimídia lêem muitos dados e de forma sequencial, não há como fazer cache disso, e o processador quase sempre terá um "cache miss" tanto em L1 quanto em L2 na hora de fazer uma leitura na memória.

    O efeito de cache trash também acontece quando você usa uma aplicação multimidia no seu computador com um processador, já que todas as threads de todos os programas usam o mesmo L1 e L2. Sendo assim, ouvir mp3 ou rodar qualquer tipo de software que de streaming reduz o desempenho de computadores com o cache menor (como Celeron), já que o player vai sujar o cache durante o tempo todo e as outras thread vão sofrer com o cache miss toda hora. Por isso que os processadores para servidor (como o Xeon) têm um cache maior, porque a probabilidade de uma thread encontrar os dados nos cache é maior, o que faz com que a thread rode mais rapidamente. Só que a memória cache é cara, o que explica a maior disponibilidade em servidores...

    Para mais informações, leia:

    Em 14/11/2005 10:14 - Comentários (4)

    Não custa repetir: Dilbert não é ficção

    Em 14/11/2005 12:02 - Comentários (4)

    Mais problemas com o HyperThreading

    Pesquisando mais sobre HyperThreading e as implicações do cache compartilhado, cheguei à um artigo que já saiu a um bom tempo: Hyper-Threading Considered Harmful (pdf). Nesse artigo, o autor explica mais um problema sobre o cache compartilhado, só que dessa vez uma falha de segurança.

    Como eu já disse no post anterior, os caches L1 e L2 dos processadores HT são compartilhados entre os núcleos lógicos. Sendo assim, em um processador HT, duas threads rodam ao mesmo tempo e usando o mesmo cache (isso não acontece em dois processadores, porque apesar das threads rodarem ao mesmo tempo, cada processador tem seu cache). Isso posibilita que uma thread consiga pegar dados de outra thread quando eles são colocados no cache. Junte isso com a possibilidade da primeira thread estar lendo - e consequentemente colocando no cache - uma chave de criptografia, e pronto, está criada a falha de segurança.

    Apesar de não ser possível pegar todos os dados da outra thread, algumas partes de uma chave de criptografia é muito melhor do que nada, e isso muito mais perigoso quando uma chave é usada constantemente, dando à thread espiã a possibilidade de tentar pegar a chave por pedaços a cada vez que a chave de criptografia é usada.

    Em 15/11/2005 21:07 - Comentários (0)

    Programando direito com o Slashdot

    No Ask Slashdot de ontem, um slashdotter perguntou sobre como montar um padrão de desenvolvimento para um recém criado departamento de desenvolvimento de software. Como sempre, as respostas foram muito boas, recomendo a leitura. O que eu acho mais interessante é que ninguém recomenda RUP... :-)

    E isso me faz lembrar de como muitas das coisas que foram respondidas lá são óbivias para todo mundo mas muita gente não faz. E esse muita gente inclui "eu mesmo" em alguns pontos...

    Coisas obviamente necessárias para fazer um software que presta:

    • Especificação. Simples. Alguns DOCs, nada muito complicado, mas que diga o que deve ser feito, como deve ser feito e porque deve ser feito assim.
    • Comentário no código. Na minha opinião MAIS IMPORTANTE DO QUE A ESPECIFICAÇÃO, pelo fato de ser mais simples de escrever e estar mais ligado ao processo de desenvolvimento. Se você não comenta seu código, está errado, isso é essencial. Essencial. Essencial. for(;;) cout << "Essencial" << endl.
    • Software de controle de bugs. Essencial também. Pode ser uma planilha Excel ou algo como o Bugzilla (que usamos aqui onde eu trabalho).
    • Controle de versão. Eu espero mesmo que ninguém questione esse. O básico do básico. Pode ser um Source Safe (que funciona bem para projetos pequenos), CVS, SVN ou até um Preforce (dizem que é o melhor, US$ 800,00 por usuário).
    • Testes. Uma equipe de testes ou testes unitários. Eu tenho o segundo. Como faço só software server-side e nem tudo está 100% orientado a objetos do jeito que deveria ser, faço softwares que agem como clientes e testam todas as possibilidades e fazem testes de stress. Isso é importante e reduz muito o stress de ter um cliente dizendo que seu software é uma porcaria porque você não testou uma situação de uso que, para ele, é básica.
    • Build automático. É importante, mas muita gente não faz. Eu não fazia, até que compilar 10 dependências na mão começou a atrapalhar o meu trabalho. Alguns arquivos BAT resolvem o problema. Tenho feito meus últimos em Python. Se você usa .NET, estude o MSBuild.

    Acho que esses são os básicos do básico. Não são muito difíceis de implementar e trazem um imenso retorno em curto prazo.

    Em 17/11/2005 08:51 - Comentários (10)

    Google no Brasil: Agora é pra valer

    Acho que quase todo mundo já sabe que o Google veio para o Brasil. Eles compraram, esse ano, a empresa mineira Akwan e estão montando um equipe aqui no Brasil.

    Ontem foi um dia particularmente importante para essa entrada do Google no Brasil. Hoje no "Official Google Blog" eles anunciaram o lançamento do Google News em português, e ontem fizeram um anúncio de 2 páginas no "Estado de São Paulo" (alguém consegue um link para isso? Caderno A, página ~14) explicando sua entrada no Brasil, seu modelo de negócios e noticiando a abertura do escritório em São Paulo.

    E antes que alguém pergunte: sim, eles já estão contratando programadores no Brasil. Só programadores C/C++, é claro, é óbvio. :-)

    Em 18/11/2005 07:06 - Comentários (0)

    Fazendo o WinDbg entender o arquivo PDB compilado pelo Visual C++ em DEBUG

    Eu já apanhei bastante com erros do tipo "type information missing" no Windbg, tentando fazer debug de executáveis ou DLLs compilados em versão DEBUG com o VC6. O PDB gerado pelo VC6 - quando você compila em DEBUG - não é compatível com o WinDbg. Depois de várias tentativas e algumas perguntas no grupo de WinDbg gringo, consegui encontrar uma solução definitiva. Segue a receita de bolo:

    • Primeiro você precisa configurar o VC para gerar o PDB. Meu artigo sobre WinDbg explica como fazer isso;
    • Coloque a configuração "Debug Info" exatamente como manda o artigo. Ao invés de "Program Database for Edit and Continue", a configuração deve ser "Program Database". O Edit and Continue é um dos motivos do PDB não ser entendido pelo WinDbg;
    • Entre na aba "Link" do "Project Settings" e adicione "/pdbtype:con /EDITANDCONTINUE:no" na parte de "Project Options", logo depois de todos os outros flags que já estão configurados. Isso vai garantir que o "Edit And Continue" estará desabilitado e que todas as informações de debug estarão dentro do PDB gerado.

    Pronto. Agora é só dar um rebuild no projeto, o PDB gerado será compatível com o WinDbg.

    Em 18/11/2005 09:00 - Comentários (2)

    Bug escabroso no Visual C++ 6.0

    Como eu já disse antes, o Visual C++ 6.0 é muito velho e tem sérios problemas para compilar o Boost. E como ficar alocando e desalocando memória na mão é coisa de quem não sabe nada de C++ moderno, meu chefe/gerente criou uma classe de smart pointer com contador de referência para usarmos nos nossos projetos (sim, meu gerente programa e muito bem). Ele me passou a classe para colocar em alguns projetos, e depois de apagar todos os operadores de conversão automática (safety first!), eu encontrei um bug muito estranho no código que usava essa classe.

    O que eu consegui isolar é que o bug acontece quando existe um operador de atribuição que é um template. O VC6 se perde completamente, e não gera o código que chama o operador, fazendo com que a atribuição não seja feita e que ele compile um código que é inválido. Eu resumi o código ao menor caso de teste possível, e mudei o nome da classe para CStupidPtr. Bom, vamos ao código:

    //
    // Rode isso no Visual C++ 6.0 e veja o que acontece
    //
    
    template<typename T>
    class CStupidPtr
    {
      T* m_pT;
    public: 
    
      CStupidPtr()
      {
        m_pT = 0;
      }
      
      template< typename TSrc >
      CStupidPtr<T>& operator=(const CStupidPtr<TSrc>& p ) throw()
      {
        //
        // vamos colocar um break point forçado aqui para ver
        // se ele passa
        //
        __asm int 3;
    
        m_pT = p.m_pT;
        return( *this );
      }
    
    public:
    
      T* operator->() const throw()
      {
        return m_pT;
      }
    };
    
    struct TEST
    {
      int d;
    };
    
    int main()
    {
      CStupidPtr<TEST> p;
        
      //
      // essa linha NÃO É GERADA PELO VC6, 
      // e não compila em uma versão mais nova do VC, pois é inválida
      // rode esse programa no debugger do VC6, passo a passo, e você verá
      // que essa linha é pulada pelo debugger.
      //
      p = new TEST();
      
    
      //
      // tã!
      //
      p->d = 10;
    
      return 0;
    }

    Esse código funcionaria se houvesse uma conversão possível de T* para CStupidPtr, o que parace acontecer quando esse código é compilado. Mas note que não existe nenhum operador ou construtor que faça a conversão. O que acontece é que não há conversão, não acontece nenhum erro durante a compilação (mesmo o código sendo inválido) e o VC6 não gera código para a linha de atribuição. E no final ganhamos um 0xC00000005 (access violation) de presente, já que o operador -> retorna NULL.

    Olha que o acontece quando eu compilo esse código no Visual C++ 8:

    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    vc6_bug.cpp
    vc6_bug.cpp(49) : error C2679: binary '=' : no operator found which takes a 
                      right-hand operand of type 'TEST *' (or there is no acceptable conversion)
            vc6_bug.cpp(35): could be 'CStupidPtr &CStupidPtr::operator =(const CStupidPtr &)'
            with
            [
                T=TEST
            ]
            while trying to match the argument list '(CStupidPtr, TEST *)'
            with
            [
                T=TEST
            ]
    
    

    Pois é, o VC6 já está bem velhinho...

    Em 24/11/2005 21:42 - Comentários (2)

    Onde está o STL.NET?

    Uma das maiores promessas do Visual C++ 2005 era a STL.NET, uma adapatação da STL para ser usada com tipos managed. Isso faria com que nós, programadores C++, continuassemos usando a STL mesmo programando em .NET - mesmo porque, comparado com a STL, os containers e algoritmos do .NET Framework parecem brincadeira de criança. O próprio Stan Lippman escreveu um artigo em 2004 falando da STL.NET. Ela possibilitaria que você escrevesse - em .NET, 100% managed e safe - um código assim:

    ref class ProgrammingLanguage
    {
      bool _hasMultipleInheritance;
      bool _isStandard;
      bool _hasBoost; //apelei... :-)
      String^ _name;
    public:
      ProgrammingLanguage(String^ name, bool hasMultipleInheritance, bool isStandard, bool hasBoost):
        _name(name),
        _hasMultipleInheritance(hasMultipleInheritance),
        _isStandard(isStandard),
        _hasBoost(hasBoost)
        {}
    
        property String^ Name
        {
          String^ get()
          { return _name; } 
        }
    
        ref class IsBestLanguage
        {
        public:
          bool operator()(const ProgrammingLanguage^ language)
          {
            return language->_hasMultipleInheritance &&
                 language->_isStandard &&
               language->_hasBoost;
          }
      };
    };
    
    int main(array<System::String ^> ^args)
    {
      vector<ProgrammingLanguage^>^ Languages = gcnew vector<ProgrammingLanguage^>();
      vector<ProgrammingLanguage^>::iterator iBestLanguage;
    
      ProgrammingLanguage ^CSharp, ^CPlusPlus, ^VB, ^Java;
    
      CSharp = gcnew ProgrammingLanguage("C#", false, true, false);
      CPlusPlus = gcnew ProgrammingLanguage("C++", true, true, true);
      VB = gcnew ProgrammingLanguage("Visual Basic / Visual Fred", false, false, false);
      Java = gcnew ProgrammingLanguage("Java", false, false, false);
    
      Languages.push_back(CSharp);
      Languages.push_back(CPlusPlus);
      Languages.push_back(VB);
      Languages.push_back(Java);
    
      iBestLanguage = find(Languages->begin(), 
                           Languages->end(), 
                           gcnew ProgrammingLanguage::IsBestLanguage());
    
      if(iBestLanguage != Languages->end())
        Console::WriteLine("A melhor linguagem de programação é: " + *iBestLanguage);
    
       return 0;
    }
    

    Pois é, mas por algum problema a STL.NET não saiu junto com o Visual Studio 2005. Eu procurei em todos os headers do VC8 e não achei nada. Quando eu cansei de fuçar, resolvi perguntar no fórum de Visual C++ da Microsoft gringa. E a resposa não foi bem o que eu esperava... A STL.NET vai ser disponibilizada como download, futuramente. Só espero que eles tenham atrasado a STL.NET por causa das várias novidades que eles lançaram na parte unmanaged - que é o que interessa do final das contas.

    (Quanto à brincadeira sobre a melhor linguagem de programação, não fique bravo, eu não sou o único que fala besteira)

    Em 27/11/2005 18:57 - Comentários (6)

    Sobre comentários no código

    Esse é mais um post que começou como resposta para um comentário, mas cresceu demais e aprendeu a falar :-). Eu já estava a muito tempo ensaiando para escrever algo sobre a importância de comentar muito bem o código, e agora vou aproveitar o gancho para passar minhas opniões e gerar mais discussão sobre o assunto. Esse post começou como uma resposta para os comentários em "Programando direito com Slashdot".

    Minha teoria é simples:

    Você consegue fazer um script com 10 linhas de código que remove todos os comentários dos arquivos, se você não gosta de ler comentários. Agora eu quero ver você fazer um script que coloque os comentários de acordo com o que você estava pensando...

    Comente tudo que puder, o mais explicado possível, se você pecar pelo excesso é só apagar os comentários depois. Os comentários são essenciais para saber qual idéia você teve na hora de desenvolver uma rotina ou algoritmo. Eu sou um programador C++, algumas vezes virtuoso, leio assembly com relativa facilidade, e não acho que código, em qualquer linguagem, seja claro. Colocar um comentário em uma expressão "variavel +=2;" não é exagero. Qualquer um percebe que você está somando 2 ao valor da variável, o que importa é dizer porque você está somando 2 e como isso causa impacto no restante do seu programa.

    Comentar o código não gasta tempo a mais, faz parte do processo de desenvolvimento. Se você não coloca comentários no seu código, está pulando um parte importante desse processo, está fazendo pela metade. Pode parecer radical, mas eu levei alguns anos para perceber a importância disso, porque já dei manutenção em código bem comentado e código sem comentários. A diferença do tempo que você leva para entender o código e fazer as mudanças necessárias é absurda. Comentar código economiza tempo e dinheiro. Quem nunca se perguntou "o que esse cara queria fazer aqui"?

    Se alguém aqui já teve oportunidade de ver o fontes do kernel do Windo^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H os samples do DDK do Windows, sabe a diferença que faz um código bem comentado e bem organizado. Eu sinto muita diferença no meu trabalho quando vou dar manutenção em um código da equipe antiga (sem comentários) e da equipe atual (bem comentado). Posso garantir que, muitas vezes, o tempo para correção de um bug em um código bem comentado é menos da metade do tempo que se leva para corrigir um bug em um código sem comentários. Escrever código mais claro ajuda muito, mas não resolve o problema. O ideal é código claro e comentado. Se as empresas soubessem o quanto isso abaixa o custo de desenvolvimento, iam parar de pensar só em metodologia e pensariam um pouco mais em melhorar o código e a capacidade técnica de quem o escreve...

    Existe sim o risco dos comentários ficarem errados com o tempo, alguém pode modificar o funcionamento de alguma coisa e deixar o comentário antigo. Sim, da mesma forma que seu software pode ter um bug. Esse é um risco pequeno perto dos grandes ganhos que um código bem comentado gera. Sem comentários, se você ficar 6 meses sem mexer no projeto e esquecer como ele funciona, vai ter que entender o código todo novamente (ou ler a documentação, que também pode estar desatualizada). Se você tiver bons comentários, toda vez que você for mudar alguma coisa, por mais que o intervalo seja longo, você terá um ponto de início. Sem comentários você tem um monte de código. E como eu disse - e torno a dizer - nenhum código, em nenhuma linguagem, é claro. Qualquer sistema que seja mais complexo do que um Hello World não é claro. Você pode achar claro enquanto faz, mas quem vem depois de você não achará, seja por falta de conhecimento da regra de negócio, ou mesmo falta de conhecimento na linguagem ou ambiente de programação.

    Comentar o código é de suma importância, e isso deve ser pensado e conversado antes de tentar implementar qualquer metodologia. Comentar o código é um processo rápido, não traumático e resolve muitos problemas. Isso traz grandes vantagens a curto prazo e é muito mais barato e fácil do que implementar uma metodologia. É uma coisa que você pode começar hoje, agora, não depende de aprovação de gerente nenhum, não depende da má vontade do setor administrativo ou financeiro. Além de fazer um bem para empresa comentando bem o seu código, você fará um grande bem para você, pois terá bem menos dor de cabeça quando precisar fazer uma alteração em um sistema 4 anos depois. E a gente sabe que isso acontece.

    Em 29/11/2005 12:00 - Comentários (10)