# De volta à tona

2008-09-07 ^

Depois dessas duas semanas de férias forçadas volto a escrever-vos neste humilde blogue. Houve um pequeno problema com o script que insere um arquivo de código-fonte dentro da página e ele comeu todos os meus bits disponíveis por mês na hospedagem do saite.

Entre os próximos artigos estão um bug na função PathIsDirectory, algumas traduções não-ortodoxas para handle, thread e dead lock, a estrutura dos códigos de erro do Windows e algumas ruminações sobre como fazer um script de build.

Até lá.


# Retorno do PathIsDirectory

2008-09-10 ^

Estava eu outro dia programando aquele código esperto "para ontem" quando me deparei com uma situação no mínimo inusitada. Ao testar se um caminho recebido era de fato um diretório me foi retornado pela API um valor diferente de TRUE. E diferente de FALSE!

De acordo com a documentação, o retorno deveria ser TRUE caso o caminho enviado à função fosse de fato um diretório. Caso contrário, o retorno deveria ser FALSE.

Note que existem apenas dois valores possíveis para essa função. Porém, o valor retornado não é 1, o equivalente ao define TRUE, mas sim 0x10 (16 em hexadecimal). O simples exemplo abaixo deve conseguir reproduzir a situação (Windows XP Service Pack 3):

   Setting environment for using Microsoft Visual Studio 2008 x86 tools.
   
   C:\Tests>copy con IsPathDir.cpp
   #include 
   #include 
   #include 
   
   #pragma comment(lib, "shlwapi.lib")
   
   int main()
   {
           BOOL isDir = PathIsDirectory("C:\\Tests"); // obs.: diretorio TEM que existir
           printf("Resultado: %d.\n", isDir);
   }^Z
           1 arquivo(s) copiado(s).
   
   C:\Tests>cl IsPathDir.cpp
   Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
   Copyright (C) Microsoft Corporation.  All rights reserved.
   
   IsPathDir.cpp
   Microsoft (R) Incremental Linker Version 9.00.21022.08
   Copyright (C) Microsoft Corporation.  All rights reserved.
   
   /out:IsPathDir.exe
   IsPathDir.obj
   
   C:\Tests>IsPathDir.exe
   Resultado: 16.

Isso quer dizer apenas que o código abaixo vai funcionar,

   if( PathIsDirectory(path) ) // legal: qualquer coisa diferente de zero

o código abaixo vai funcionar

   if( ! PathIsDirectory(path) ) // legal: se der zero (FALSE), OK

e o código abaixo não vai funcionar:

   if( PathIsDirectory(path) == TRUE ) // vixi: TRUE nem sempre é o resultado

E, pior, o código abaixo também não vai funcionar!

   if( PathIsDirectory(path) != TRUE ) // aff... é bom rever os seus conceitos

Pesquisando um pouco descobri uma boa discussão sobre o tema, e inclusive que outras pessoas descobriram o interessante detalhe que para pastas normais o retorno é 0x10, mas para compartilhamentos o retorno é 0x1.

O bug atrás dos documentos

O problema ocorre por causa da maneira que a função determina se o caminho é um diretório ou não. Uma simples vistoria sobre a função nos revela o detalhe crucial:

   C:\Tests>cl /Zi IsPathDir.cpp
   Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
   Copyright (C) Microsoft Corporation.  All rights reserved.
   
   IsPathDir.cpp
   Microsoft (R) Incremental Linker Version 9.00.21022.08
   Copyright (C) Microsoft Corporation.  All rights reserved.
   
   /out:IsPathDir.exe
   /debug
   IsPathDir.obj
   
   C:\Tests>cdb IsPathDir.exe
   
   Microsoft (R) Windows Debugger Version 6.8.0004.0 X86
   Copyright (c) Microsoft Corporation. All rights reserved.
   
   CommandLine: IsPathDir.exe
   Symbol search path is: SRV*c:\symbols*http://msdl.microsoft.com/download/symbols
   Executable search path is:
   ModLoad: 00400000 00426000   IsPathDir.exe
   ModLoad: 7c900000 7c9b4000   ntdll.dll
   ModLoad: 7c800000 7c8ff000   C:\WINDOWS\system32\kernel32.dll
   ModLoad: 77ea0000 77f16000   C:\WINDOWS\system32\SHLWAPI.dll
   ModLoad: 77f50000 77ffb000   C:\WINDOWS\system32\ADVAPI32.dll
   ModLoad: 77db0000 77e41000   C:\WINDOWS\system32\RPCRT4.dll
   ModLoad: 77e50000 77e97000   C:\WINDOWS\system32\GDI32.dll
   ModLoad: 7e360000 7e3f0000   C:\WINDOWS\system32\USER32.dll
   ModLoad: 77bf0000 77c48000   C:\WINDOWS\system32\msvcrt.dll
   (ea0.de0): Break instruction exception - code 80000003 (first chance)
   eax=00241eb4 ebx=7ffde000 ecx=00000004 edx=00000010 esi=00241f48 edi=00241eb4
   eip=7c901230 esp=0012fb20 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
   ntdll!DbgBreakPoint:
   7c901230 cc              int     3
   0:000> g shlwapi!PathIsDirectoryA
   ModLoad: 76360000 7637d000   C:\WINDOWS\system32\IMM32.DLL
   ModLoad: 62e80000 62e89000   C:\WINDOWS\system32\LPK.DLL
   ModLoad: 74d50000 74dbb000   C:\WINDOWS\system32\USP10.dll
   eax=009836e0 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee7538 esp=0012ff6c ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA:
   77ee7538 8bff            mov     edi,edi
   0:000> p
   eax=009836e0 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee753a esp=0012ff6c ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x2:
   77ee753a 55              push    ebp
   0:000>
   eax=009836e0 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee753b esp=0012ff68 ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x3:
   77ee753b 8bec            mov     ebp,esp
   0:000>
   eax=009836e0 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee753d esp=0012ff68 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x5:
   77ee753d 81ec0c020000    sub     esp,20Ch
   0:000>
   eax=009836e0 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee7543 esp=0012fd5c ebp=0012ff68 iopl=0         nv up ei pl nz ac pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
   SHLWAPI!PathIsDirectoryA+0xb:
   77ee7543 a180d2f077      mov     eax,dword ptr [SHLWAPI!__security_cookie
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee7548 esp=0012fd5c ebp=0012ff68 iopl=0         nv up ei pl nz ac pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
   SHLWAPI!PathIsDirectoryA+0x10:
   77ee7548 56              push    esi
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0006f4cc edi=7c911970
   eip=77ee7549 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz ac pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
   SHLWAPI!PathIsDirectoryA+0x11:
   *** WARNING: Unable to verify checksum for IsPathDir.exe
   77ee7549 8b7508          mov     esi,dword ptr [ebp+8] ss:0023:0012ff70=0041dc5c
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee754c esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz ac pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
   SHLWAPI!PathIsDirectoryA+0x14:
   77ee754c 85f6            test    esi,esi
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee754e esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
   SHLWAPI!PathIsDirectoryA+0x16:
   77ee754e 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0012ff64=fffffffe
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee7551 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
   SHLWAPI!PathIsDirectoryA+0x19:
   77ee7551 0f8493000000    je      SHLWAPI!PathIsDirectoryA+0xb2 (77ee75ea) [br=0]
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee7557 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
   SHLWAPI!PathIsDirectoryA+0x1f:
   77ee7557 56              push    esi
   0:000>
   eax=00007a43 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee7558 esp=0012fd54 ebp=0012ff68 iopl=0         nv up ei pl nz na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
   SHLWAPI!PathIsDirectoryA+0x20:
   77ee7558 e85cc0fdff      call    SHLWAPI!PathIsUNCServerA (77ec35b9)
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee755d esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x25:
   77ee755d 85c0            test    eax,eax
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee755f esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x27:
   77ee755f 0f8585000000    jne     SHLWAPI!PathIsDirectoryA+0xb2 (77ee75ea) [br=0]
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee7565 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x2d:
   77ee7565 56              push    esi
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee7566 esp=0012fd54 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x2e:
   77ee7566 e812feffff      call    SHLWAPI!PathIsUNCServerShareA (77ee737d)
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee756b esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x33:
   77ee756b 85c0            test    eax,eax
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee756d esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0x35:
   77ee756d 0f8486000000    je      SHLWAPI!PathIsDirectoryA+0xc1 (77ee75f9) [br=1]
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee75f9 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xc1:
   77ee75f9 56              push    esi
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee75f9 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xc1:
   77ee75f9 56              push    esi
   0:000>
   eax=00000000 ebx=7ffde000 ecx=00000001 edx=00422828 esi=0041dc5c edi=7c911970
   eip=77ee75fa esp=0012fd54 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xc2:
   77ee75fa ff15d411ea77    call    dword ptr [SHLWAPI!_imp__GetFileAttributesA (77ea11d4)]
   0:000>
   eax=00000011 ebx=7ffde000 ecx=7c91056d edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee7600 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xc8:
   77ee7600 83f8ff          cmp     eax,0FFFFFFFFh
   0:000>
   eax=00000011 ebx=7ffde000 ecx=7c91056d edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee7603 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz ac pe cy
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
   SHLWAPI!PathIsDirectoryA+0xcb:
   77ee7603 74e5            je      SHLWAPI!PathIsDirectoryA+0xb2 (77ee75ea) [br=0]
   0:000>
   eax=00000011 ebx=7ffde000 ecx=7c91056d edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee7605 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz ac pe cy
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
   SHLWAPI!PathIsDirectoryA+0xcd:
   77ee7605 83e010          and     eax,10h
   0:000>
   eax=00000010 ebx=7ffde000 ecx=7c91056d edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee7608 esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
   SHLWAPI!PathIsDirectoryA+0xd0:
   77ee7608 ebe2            jmp     SHLWAPI!PathIsDirectoryA+0xb4 (77ee75ec)
   0:000>
   eax=00000010 ebx=7ffde000 ecx=7c91056d edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee75ec esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
   SHLWAPI!PathIsDirectoryA+0xb4:
   77ee75ec 8b4dfc          mov     ecx,dword ptr [ebp-4] ss:0023:0012ff64=00007a43
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0041dc5c edi=7c911970
   eip=77ee75ef esp=0012fd58 ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
   SHLWAPI!PathIsDirectoryA+0xb7:
   77ee75ef 5e              pop     esi
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=77ee75f0 esp=0012fd5c ebp=0012ff68 iopl=0         nv up ei pl nz na po nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
   SHLWAPI!PathIsDirectoryA+0xb8:
   77ee75f0 e82bcafbff      call    SHLWAPI!__security_check_cookie (77ea4020)
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=77ee75f5 esp=0012fd5c ebp=0012ff68 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xbd:
   77ee75f5 c9              leave
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=77ee75f6 esp=0012ff6c ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   SHLWAPI!PathIsDirectoryA+0xbe:
   77ee75f6 c20400          ret     4
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=0040101f esp=0012ff74 ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   IsPathDir!main+0xf:
   0040101f 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0012ff74=00000001
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=00401022 esp=0012ff74 ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   IsPathDir!main+0x12:
   00401022 8b45fc          mov     eax,dword ptr [ebp-4] ss:0023:0012ff74=00000010
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=00401025 esp=0012ff74 ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   IsPathDir!main+0x15:
   00401025 50              push    eax
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=00401026 esp=0012ff70 ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   IsPathDir!main+0x16:
   00401026 6868dc4100      push    offset IsPathDir!__xt_z+0x12c (0041dc68)
   0:000>
   eax=00000010 ebx=7ffde000 ecx=00007a43 edx=00140608 esi=0006f4cc edi=7c911970
   eip=0040102b esp=0012ff6c ebp=0012ff78 iopl=0         nv up ei pl zr na pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
   IsPathDir!main+0x1b:
   0040102b e81a000000      call    IsPathDir!printf (0040104a)
   0:000>
   Resultado: 16.
   eax=0000000f ebx=7ffde000 ecx=004010e5 edx=004228b8 esi=0006f4cc edi=7c911970
   eip=00401030 esp=0012ff6c ebp=0012ff78 iopl=0         nv up ei ng nz ac pe nc
   cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000296
   IsPathDir!main+0x20:
   00401030 83c408          add     esp,8
   0:000>

Ou seja, para pastas locais a função simplesmente usa a conhecidíssima GetFileAttributes, que retorna o flag 0x10 setado caso se trate de uma pasta, de acordo com a documentação: "The attributes can be one or more of the following values:"

   Return code/value              Description
   FILE_ATTRIBUTE_ARCHIVE         A file or directory that is an archive file or directory.
   32
   0x20
   FILE_ATTRIBUTE_COMPRESSED      A file or directory that is compressed.
   2048
   0x800
   ...
   
   FILE_ATTRIBUTE_DIRECTORY       The handle that identifies a directory.
   16
   0x10

Aqui termina nossa dúvida sobre o pequenino bug na documentação. E isso nos lembra também que é sempre bom comparar as coisas da melhor maneira possível. E essa melhor maneira em se tratando de ifs é supor apenas dois valores binário: ou é zero ou é não-zero.


# Todo programador é um filósofo em potencial

2008-09-12 ^

Tivemos uma conversa muito frutífera hoje durante o almoço ao conhecer uma professora que sentava ao nosso lado, exímia conhecedora da mente humana e amante das artes nobres como a filosofia e a lógica. O importante dessa colóquio foi ter encontrado um motivo muito mais forte para gostar de programação do que qualquer outro que já me surgira na cabeça desde que mexo com essas coisas: O computador não deve dar ordens ao homem e este repeti-las como uma máquina. O homem, como ser pensante, deve dizer ao computador o que fazer, e este responder-lhe diligentemente.

Para resumir desde o começo essa conclusão, um exemplo paupável: quando fazemos um curso de treinamento, uma especialização, uma faculdadeZINHA ou qualquer outro evento de "aprendizado" que nos propõe a digitar comandos sem fim, repetidamente para um computador, para no final ganharmos um comprovante de que sabemos digitar aqueles comandos decorados e repetitivos como ninguém, isso quer dizer que as coisas vão mal, se e quando tudo se resume a isso.

Por outro lado, ao aprendermos, por nós mesmos ou pelos outros, a usar o computador como a ferramenta que vai fazer de nossas idéias realidade, e nossas criações se materializam de uma forma inimitável, então, somos criadores e comandantes da máquina, e não uma cópia de uma máquina que repete comandos. Então, nesse caso, temos um motivo para viver: criar sempre coisas novas e interessantes que surgem em nossas cabeças.

E ser feliz é isso: achar significado para o que fazemos. É criar. Pura e simplesmente. Quem está sempre criando está sempre satisfeito com sua vida. Pois dá sentido a ela todo santo dia. Isso vale para qualquer profissão interessante o suficiente. Não precisa fazer código. Pode construir móveis, ensinar pessoas ou desenhar uma nova peça de roupa.

Criar é pensar. Programadores pensam em coisas novas todos os dias e as executam. Quando encontram algo repetitivo, organizam o código para não terem que repetir mais a mesma baboseira e voltam a fazer coisas interessantes e originais. Se existe um processo enfadonho e chato, o programador inventa um jeito para o computador fazê-lo, e não ele. E a vida do programador sempre gira em torno desse ciclo: dispensa as coisas chatas mandando o computador fazer e se dedica a fazer coisas novas.

A discussão não parou por aí, pois me levou a entender o vazio que eu sinto ao estudar coisas que não uso nunca. Porque aprender por aprender não vai me levar a lugar algum. Pode até ser perigoso ler coisas que não servem para nada. Me faz parecer inútil.

E não livros longos, ricos em detalhes e que fazem perder o fio da meada ao terminá-lo.

Também me mostra que mais vale a pena aprender a pensar, ou pensar melhor, do que aprender uma nova tecnologia em um livro recém-lançado de 1500 páginas. Quando aprendemos a pensar resolvemos os problemas por nós mesmos, e não por uma formulazinha mágica tirada do saite favorito de bricabraques.

Ser programador é criar. Criar é bom. Criar nos faz felizes.

E é por isso, realmente, que amo o que eu faço.


# Reúna seus comandos mais usados no WinDbg com .cmdtree

2008-09-19 tag_coding ^

Tudo começou com o artigo de Roberto Farah sobre o comando "escondido" do WinDbg .cmdtree. Logo depois meus outros colegas do fã-clube do WinDbg Volker von Einem e Dmitry Vostokov comentaram sobre a imensa utilidade desse comando. E não é pra menos. É de longe o melhor comando não-documentado do ano. Tão bom que sou obrigado a comentar em português sobre ele, apesar dos três artigos já citados.

Comandos repetitivos

E eu estava justamente falando sobre essa mania dos programadores sempre acharem soluções para tarefas repetitivas e monótonas que o computador possa fazer sozinho.O comando .cmdtree é uma dessas soluções, pois possibilita ao depurador profissional juntar em uma só guia o conjunto de comandos mais usados por ele no dia-a-dia, por mais bizarros e com mais parâmetros que eles sejam, já que é possível representá-los por um alias (apelido):

   windbg ANSI Command Tree 1.0
   title {"Meus Comandos Comuns"}
   body
   {"Comandos Comuns"}
    {"Subsecao"}
     {"Breakpoint no inicio do programa"} {"bp @$exentry"}
     {"GetLastError"} {"!gle"}

O resultado:

E podemos usar essa janela no nosso WinDbg, cada vez mais bonitinho e cada vez mais WYSIWYG:

Realmente não há segredos em seu uso. Esse artigo foi apenas um patrocínio do clube do WinDbg.


# V

2008-09-23 tag_ccppbr ^

Parabéns a todos que participaram e ajudaram para que todos nós chegássemos ao quinto encontro de programadores/aficionados C/C++. Parece mentira, mas hoje temos capacidade para lotar um auditório razoável, e temos a ousadia de sempre poder contar com uma grade de palestras pra lá de avançadas. Vejamos o que foi visto até hoje nesses últimos três encontros (III, IV e o seminário):

 * C++ com WxWidgets
 * O novo padrão C++0x
 * Threads no C++ ISO
 * C e microcontroladores
 * Drivers para Windows
 * TCP/IP via Boost.Asio
 * C++ com Qt
 * Dicas de portabilidade
 * Programação concorrente
 * C++ com STL/Boost
 * Otimização de código

E esse é só o começo.

04 de outubro de 2008, São Paulo, Brasil

 * Ferramentas para programação C++ para Windows por Rodrigo Strauss
 * Programando com Conceitos no novo C++ por Leandro Melo
 * Arquivos de memória mapeada no Windows com C++ por Basílio Miranda
 * Explorando o Windows (Vista & Server 2008) com C++ por Fábio Galuppo
 * Criando Linguagens Embutidas para Otimização por Felipe Almeida

08 de novembro de 2008, São Paulo, Brasil

 * Técnicas de Programação em C para Sistemas Embarcados por Daniel Quadros
 * Utilização de C++ em Microcontroladores por Luiz Barros
 * Explorando os 16 bits da Microchip e as ferramentas de trabalho por Daniel Rodrigues
 * Otimização de código C para sistemas embarcados por Fábio Pereira
 * Desenvolvimento Embedded no Mundo da eLua por Dado Sutter

Se repararam, o número de palestras foi acrescido de um (palestras++) e o tempo para cada uma delas foi ligeiramente encolhido. Espero que esse não seja um empecilho para o desenvolver dos assuntos, pois existem alguns bem delicados acima (como a linguagem embutida e memória mapeada) para serem explicados em cerca de uma hora.

É isso aí. Vida longa ao C++! (e ao C! e ao COBOL! e ao FORTRAN!)


# Windows Jobs com Completion Port

2008-09-23 tag_coding ^

Ou "Como esperar o término de todos os processos-filho criados a partir de um conjunto de processos".

Dessa vez confesso que esperava um pouco mais de documentação do MSDN, ou pelo menos um sistema de referências cruzadas eficiente. Outro dia demorei cerca de duas horas para conseguir criar um _**job**_, anexar o processo desejado e, a pior parte, esperar que todos os processos (o principal e seus filhos e netos) terminassem.

Além da pouca documentação, parece que não são muitas as pessoas que fazem isso e publicam na web, ou eu não sei procurar direito.

Mas, pra início de conversa, o que é um job mesmo?

Leve introdução sobre o conceito de jobs

Um job é um objeto "novo" no kernel do Windows 2000 em diante, e se prontifica a suprir a carência que havia anteriormente de **controle sobre o que os processos podem fazer e por quanto tempo**.

A abstração mais coerente que eu consigo tirar de um job é como **um trabalho a ser executada por um ou mais processos**. O objeto job controla a criação, o término e as exceções que ocorrem dentro dele mesmo.

Entre as funções mais úteis de um job estão limitar o tempo de execução do conjunto de processos, o número de handles/arquivos/outros objetos abertos, limite de memória RAM ocupada e a possibilidade de terminar todos os processos de uma só vez.

Para informações básicas de como criar um job e anexar processos recomendo o ótimo artigo de Jeffrey Richter (se você conseguir encontrar, meu último link está quebrado).

No final desse artigo ele chega a citar o controle mais refinado dos processos através de uma **completion port**, que permitirá receber eventos que ocorrem dentro de um job durante sua vida útil. Apesar de citar, não há código de exemplo que faça isso.

Bom, agora há:

#define _WIN32_WINNT 0x0500 // Jobs só existem do 2000 em diante
#include 
/** @brief Função que cria um processo a partir de cmdLine
 * e coloca-o dentro de um job. A função aguarda o término
 * do processo e de qualquer subprocesso criado por este.
 */
DWORD CreateJobAndWait(LPSTR cmdLine)
{
   // primeiro, criamos um job sem nome
   HANDLE job = CreateJobObject(NULL, NULL);
   if( job )
   {
      STARTUPINFO si = { sizeof(si) };
      PROCESS_INFORMATION pi;
      // depois, criamos um processo suspenso (travado)
      if( CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 
         CREATE_SUSPENDED | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) )
      {
         // atribuímos esse processo ao nosso jobo
         AssignProcessToJobObject(job, pi.hProcess);
         // rodamos o processo
         ResumeThread(pi.hThread);
         // essa é uma completion i/o port genérica
         // (ou seja, não relacionada com nenhum arquivo
         // ou outra completion port)
         HANDLE port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 
                 NULL, 0, 0);
         if( port )
         {
            JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobPort;
            jobPort.CompletionKey = 0; // ver variável key abaixo
            jobPort.CompletionPort = port; // nossa completion port vai aqui!
            // definimos a c.p. em nosso job
            if( SetInformationJobObject(job, 
                        JobObjectAssociateCompletionPortInformation, 
                        &jobPort, sizeof(jobPort)) )
            {
               ULONG_PTR key = 0; // ver membro CompletionKey acima
               LPOVERLAPPED overlap = 0;
               DWORD tranferred = 0;
               // nosso loop de mensagens com completion port
               while( GetQueuedCompletionStatus(port, &tranferred, 
                  &key, &overlap, INFINITE) )
               {
                  // transferred especifica a mensagem
                  DWORD msg = *(LPDWORD) &tranferred;
                  // significa que não existem mais processos rodando
                  if( msg == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO )
                     break; // saímos fora
               }
            }
            CloseHandle(port); // fecha tudo
         }
         CloseHandle(pi.hThread); // fecha tudo
         CloseHandle(pi.hProcess); // fecha tudo
      }
      CloseHandle(job); // fecha tudo
   }
   return 0;
}
int main(int argc, char* argv[])
{
   if( argc == 2 )
      CreateJobAndWait(argv1);
}

O exemplo acima cria um processo baseado em uma linha de comando e espera pelo término do processo criado e de todos os subprocessos criados a partir do primeiro processo. Note que mesmo que o primeiro processo termine, a Completion Port só receberá o evento que todos os processos acabaram depois que o último subprocesso terminar.

Dessa forma, ao compilarmos o código e rodarmos mais um prompt de comando através de nosso programa ele fica travado mesmo ao fecharmos o prompt criado. O programa só será finalizado ao fecharmos o Bloco de Notas iniciado pelo segundo prompt.

Além desse evento, que era o que eu estava procurando, esse método permite obter outros eventos bem interessantes:

 * JOB_OBJECT_MSG_NEW_PROCESS. Um novo processo foi criado dentro do job.
 * JOB_OBJECT_MSG_EXIT_PROCESS. Um processo existente dentro do job foi terminado.
 * JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT. O limite de memória de um processo já foi alcançado.
 * JOB_OBJECT_MSG_END_OF_PROCESS_TIME. O limite de tempo de processamento de um processo já foi alcançado.

Enfim, jobs não terminam por aí. Dê mais uma olhada no MSDN e veja se encontra mais alguma utilidade interessante para o nosso amigo job. Eu encontrei e fiquei feliz.


# Cnasi, geração Y e seus gastos em TI

2008-09-25 ^

Nosso crachá de visitantes dava direito a uma palestra. E haviam muitas. Porém, logo após a hora do almoço, das disponíveis uma era particularmente interessante, pois citava uma expressão que eu e minha colega nunca havíamos escutado: um senhor iria nos falar sobre como lidar com essas novas pessoas que estão cada vez mais invadindo nossas casas e nossos escritórios, pertencentes a esse grupinho, a tão famosa chamada geração Y.

Agora não sei se a culpa foi dessa tal geração Y, mas o fato é que o folhetim estava errado e caímos em uma outra palestra, essa falando sobre um tema que aí sim nós nunca tínhamos ouvido falar: como gerenciar as finanças na parte de TI da empresa.

O homem discursou por uma hora falando de como todos os principais frameworks de gerenciamento de custos, projetos e todas aquelas coisas, poderiam ser integrados para facilitar a vida do gestor de projetos que tem como tarefa saber o que irá continuar fazendo e o que irá cortar devido à lucratividade/risco perigosos.

A grande questão dessa discussão, pelo que eu pude entender, foi que os custos de um projeto e manutenção são difíceis de mensurar se não existir alguém no meio daquele bando de nerds que consiga dizer quais são os recursos usados (quem), para que (tarefa) e por quê (vantagem). Sem contar que é necessário transformar isso em dado contábil. Além de que, como bem disse nosso palestrante, a maioria das empresas ainda considera o departamento de nerds uma despesa sem vantagens. E uma despesa bem cara, se estivermos falando dos salários atualmente pagos para esse pessoal.

Mas vou parar por aqui antes que alguém levante a bandeira e queira discursar a respeito da justeza com que são pagas nossas mentes, um assunto muito precoce nos dias atuais. (Bom, talvez seja precoce para sempre, do jeito que a economia é ciência aberta.)

O fato é que acabamos subindo a rua novamente sem saber que tal de geração Y é essa que nos fez entrar na palestra errada. Como sempre, nada que uma boa "googada" não resolva.

Parênteses. Descobri recentemente que os seguidores do outro lado agora inventaram um novo "termo": Windows-Live-procurada. Um de seus representantes, anteriormente um homem de respeito e opiniões fortes e imparciais, acredita este ser um termo de uso corrente no dia-a-dia.

Pelo que pudemos encontrar, a tal da geração Y somos nós mesmo. Que espanto! E eu pensei que estávamos falando de uma raça alienígena ou algo assim. Talvez até seja, mas temos todos os genes da espécie humana.

Aparentemente existem estudos "sérios" nos EUA que dividem as gerações usando letras e encontrando as principais diferenças entre nós mesmos e nossos pais e avós. Como essa última geração recebeu elogios de diversas partes do estudo, a nominação de geração Y foi se estendendo ao resto do mundo, principalmente nos novos mercados de tecnologia.

A conclusão a que chego é que existem rótulos demais para as pessoas hoje em dia. As pessoas não podem mais ser simplesmente pessoas. Precisam ser nerds, geeks, ráquers ou geração Y. Se você não é, está de fora. Se está dentro, é antenado. Que é outro rótulo.

Fim de passeio. Voltamos à empresa e falamos com um outro colega sobre o que havíamos aprendido sobre o uso dos recursos no gerenciamento dos projetos no departamento de TI de uma empresa. Ele disse: "TI pra quê? Só traz despesa esse negócio."

E viva a geração Y!

Adendum. A palestra que participamos foi a "Por que implementar a gestão financeira de TI? Uma abordagem baseada no ITIL, COBIT/VAL IT, e PMBOK", e queríamos ter visto a palestra "Desafio: Como Gerenciar a Geração Y", que como pudemos ver não é tarefa fácil, já que essa geração não consegue nem entrar na palestra certa. Veja a grade do evento para mais informações.

Ah, sim, meu crachá de gerente. Pois então, achei superinteressante o fato de eu ter entrado em um evento de segurança portando um crachá que diz "gerente" no nome quando na verdade eu continuo sendo de fato um programador. Fui eu que disse que era gerente na entrada da feira, como um pequeno teste de acessibilidade.

Não que isso seja uma falha de segurança muito grave, mas imagine uma empresa suficientemente grande com pessoas mal intencionadas que foram convidadas a visitar a feira e escolheram ser gerentes ou diretores na hora de escolher o cargo. O que poderiam ter feito, em nome da empresa, dentro dessa feira? É algo a se pensar...


<< >>