在避免范围检查错误方面,David的答案是正确的。但如果你不想这样做,仍然可以手动开启/关闭{$RANGECHECKS}
,只需使用{$IFOPT}
以条件方式执行,以便周围的代码不受影响,例如:
function DirExists(Name: string): Boolean;
var
Code: Integer;
begin
{$IFOPT R+}
{$DEFINE _RPlusWasEnabled}
{$R-}
{$ENDIF}
Code := GetFileAttributesW(PChar(Name));
Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
{$IFDEF _RPlusWasEnabled}
{$UNDEF _RPlusWasEnabled}
{$R+}
{$ENDIF}
end;
说了这么多,仅仅检查
GetFileAttributes()
的结果是否为
INVALID_FILE_ATTRIBUTES
是不够的。一个目录可能存在但无法访问。因此RTL的
DirectoryExists()
函数会检查
GetLastError()
多个错误代码(例如
ERROR_PATH_NOT_FOUND
、
ERROR_BAD_NETPATH
、
ERROR_NOT_READY
等),以寻找这种可能的情况。另外,
DirectoryExists()
还可以选择性地检查指定的路径是否实际上是目录的快捷方式,并在这种情况下检查目标目录是否存在。
更新:以下是XE3中SysUtils.DirectoryExists()
的实现:
function DirectoryExists(const Directory: string; FollowLink: Boolean = True): Boolean;
{$IFDEF MSWINDOWS}
var
Code: Cardinal;
Handle: THandle;
LastError: Cardinal;
begin
Result := False;
Code := GetFileAttributes(PChar(Directory));
if Code <> INVALID_FILE_ATTRIBUTES then
begin
if faSymLink and Code = 0 then
Result := faDirectory and Code <> 0
else
begin
if FollowLink then
begin
Handle := CreateFile(PChar(Directory), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if Handle <> INVALID_HANDLE_VALUE then
begin
CloseHandle(Handle);
Result := faDirectory and Code <> 0;
end;
end
else if faDirectory and Code <> 0 then
Result := True
else
begin
Handle := CreateFile(PChar(Directory), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if Handle <> INVALID_HANDLE_VALUE then
begin
CloseHandle(Handle);
Result := False;
end
else
Result := True;
end;
end;
end
else
begin
LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
(LastError <> ERROR_PATH_NOT_FOUND) and
(LastError <> ERROR_INVALID_NAME) and
(LastError <> ERROR_BAD_NETPATH) and
(LastError <> ERROR_NOT_READY);
end;
end;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
var
StatBuf, LStatBuf: _stat;
Success: Boolean;
M: TMarshaller;
begin
Success := stat(M.AsAnsi(Directory, CP_UTF8).ToPointer, StatBuf) = 0;
Result := Success and S_ISDIR(StatBuf.st_mode);
if not Result and (lstat(M.AsAnsi(Directory, CP_UTF8).ToPointer, LStatBuf) = 0) and
S_ISLNK(LStatBuf.st_mode) then
begin
if Success then
Result := S_ISDIR(StatBuf.st_mode)
else if not FollowLink then
Result := True;
end;
end;
{$ENDIF POSIX}
XE4中的实现与之前的版本相同,唯一的区别是windows版本在调用
GetLastError()
时还包括一个检查
LastError <> ERROR_BAD_NET_NAME
。
DirectoryExists()
确实在我的电脑上返回空的 DVD 驱动器的 true。使用 Delphi XE 和 Windows 8。 - Leonardo HerreraERROR_NOT_READY
的功能来“修复”这个问题。对我来说,这一切似乎非常虚假。 - David Heffernan