Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find out if a directory exists on Delphi XE2 *correctly*?

I simply need to check if a directory exists! But if the directory is "E:\Test" where E: is CD/DVD drive and there is no disk inserted on it, I see the following Delphi and Windows issues.

The first method:

function DirExists(Name: string): Boolean;
var
  Code: Integer;
begin
  Code := GetFileAttributesW(PChar(Name));
  Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
end;

It gives Range Check Error. I can't use {$RANGECHECKS OFF}, {$RANGECHECKS ON} blocks because:

  1. It breaks the current state of $RANGECHECKS option.
  2. We will see another system error Drive is not ready instead Range Check Error. But I simply need to check if directory exists without any error dialogs for user.

The second method:

if DirectoryExists(Name, True) then ...

This function returns True for non-existing E:\Test directory on empty CD/DVD drive. So can't use it because it works incorrectly.

But then, how to find out if a directory exists?

P.S. I think the error exists with any CD/DVD drive. But I am using Windows 7 x64 on VMWare Fusion 5 under Mac OS X 10.8.4 with external CD/DVD drive.

like image 715
Dmitry Avatar asked Feb 15 '23 15:02

Dmitry


1 Answers

You can just fix your function so that it doesn't cause range check errors:

function DirExists(Name: string): Boolean;
var
  Code: DWORD;
begin
  Code := GetFileAttributes(PChar(Name));
  Result := (Code <> INVALID_FILE_ATTRIBUTES) 
    and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
end;

The range check errors are due to you mixing signed and unsigned types. Remy also points out the very useful trick to set compiler options and then restore to the prevailing state. That's a good trick to learn, but you don't need it here.

The XE3 implementation of DirectoryExists is modified to address the problem that you have encountered. So, if using XE3+ was an option, you should take it.


To suppress the system error dialogs call this at process startup:

procedure SetProcessErrorMode;
var
  Mode: DWORD;
begin
  Mode := SetErrorMode(SEM_FAILCRITICALERRORS);
  SetErrorMode(Mode or SEM_FAILCRITICALERRORS);
end;

Doing this is best practise as described on MSDN:

Best practice is that all applications call the process- wide SetErrorMode function with a parameter of SEM_ FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application.

like image 142
David Heffernan Avatar answered May 03 '23 20:05

David Heffernan