大卫在避免范围检查错误方面有正确的答案。但是如果你不想这样做,你仍然可以{$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()
for的结果INVALID_FILE_ATTRIBUTES
是不够的。目录可能存在但无法访问。这就是为什么 RTL 的DirectoryExists()
函数会检查GetLastError()
多个错误代码(ERROR_PATH_NOT_FOUND
、ERROR_BAD_NETPATH
、ERROR_NOT_READY
等)以寻找可能的情况。可以做的另一件事DirectoryExists()
是可选地检查指定路径是否实际上是目录的快捷方式,如果是,则检查目标目录是否存在。
更新:这是SysUtils.DirectoryExists()
XE3 中的实现:
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 版本还包括LastError <> ERROR_BAD_NET_NAME
调用时的检查GetLastError()
。