Bug no retorno do PathIsDirectory
Caloni, 2008-09-10 computer blogEstava 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 <shlwapi.h>
#include <windows.h>
#include <stdio.h>
#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 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 ... ntdll!DbgBreakPoint: 7c901230 cc int 3 0:000> g shlwapi!PathIsDirectoryA 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 push ebp mov ebp,esp sub esp,20Ch mov eax,dword ptr [SHLWAPI!__security_cookie push esi mov esi,dword ptr [ebp+8] ss:0023:0012ff70=0041dc5c test esi,esi mov dword ptr [ebp-4],eax ss:0023:0012ff64=fffffffe je SHLWAPI!PathIsDirectoryA+0xb2 (77ee75ea) [br=0] push esi call SHLWAPI!PathIsUNCServerA (77ec35b9) test eax,eax jne SHLWAPI!PathIsDirectoryA+0xb2 (77ee75ea) [br=0] push esi call SHLWAPI!PathIsUNCServerShareA (77ee737d) test eax,eax je SHLWAPI!PathIsDirectoryA+0xc1 (77ee75f9) [br=1] push esi call dword ptr [SHLWAPI!_imp__GetFileAttributesA (77ea11d4)] cmp eax,0FFFFFFFFh ...
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:"
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.