Detecting drive ready

I want to be able to detect if a removable media drive (except for CDROM) is ready (ie media is in the drive) before I try to read to or write from it.  The method needs to work on Windows 95 (original edition) and later as well as NT 4.0 and later.  Besides being able to check floppies and other removable media (e.g Zip drives) on the local machine, it should also be able to check such drives accessed over a network with UNC names.

All the methods I have tried work in some situations and not others.  E.g. GetDiskFreeSpace works on local drives but when used on network (UNC) paths returns a "network request is not supported" error.  I can't get CreateFile to work on Win 9x.  DeviceIoControl with vwin32 of course only works on Win 9x and only for floppies.  I have searched for "drive ready" in MSDN and found nothing useful.  Also I can find no info in a similar search on this site and some others.  I can't be the first person to ever have this problem.
The need to check for drive ready is such a common need.  Most Windows programs seem to be able to trap this conditon.  Am I making this more complicated that I need?  It seems there ought to be a simple way to do this.  Any suggestions would be appreciated.  An example in C++ Win API would be most helpful.
NormB062799Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

nietodCommented:
A common way (but I've never tried it) is to use GetFileAttributes() on the root directory.  if GetFileAttributes fails, you can assume that the media is not present, or look at GetLastError() to be sure (I'm not sure what error code it will return for that case, though.)
0
NormB062799Author Commented:
This solution works some time and not others, just like all the others I've tried.

On win98, it seems to work inconsistently.  With local drives of the form A:\ it is fine.  If a UNC path (eg. \\MyMachine\A\) refers to a local drive it works fine.  If the UNC path refers to another machine, it returns that the drive is ready even though it is not.

On Win NT 4.0 (SR1) it doesn't seem to recognize the UNC paths \\SomeComputer\SomeDrive\ at all.  It consistently comes back with ERROR_PATH_NOT_FOUND.  On local drives, at least for floppies, NT itself comes up with a dialog complaining there is no diskette in the drive.  It would be great if I could get Win 9x to do the same thing.

The MSDN documentation for GetFileAttributes says that it does not support \\?\ as part of a path.  I'm not sure if that means that UNC names are not supported under Win 95 or something else.

By the way, I note that the Windows (98) file explorer correctly reports if a remote network drive is ready.  This tells me there must be a way to do this.

So I'll have to wait for other proposed solutions.
0
nietodCommented:
>> it returns that the drive is ready even though it is not
How is that possible?  what attributes does it return?

>> On Win NT 4.0 (SR1) it doesn't seem to recognize
>> the UNC paths
In what way?  I use UNC paths on NT 4 with no problem, also other version of NT.

>> On local drives, at least for floppies, NT itself comes
>> up with a dialog complaining there is no diskette in
>> the drive.  It would be great if I could get Win 9x to
>> do the same thing.
That can be turned on or off by your program on a program-by-program basis.  It should default to being on for all prorams, you usually have to turn it off.

>> does not support \\?\
That is needed for when the path is longer than MAX_PATH characters.  That is not really part of the path, that just indicates that the path must be parsed differently.
0
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

MadshiCommented:
Here comes my Delphi function, that works perfectly in all situations under all OSs:

function PathExists(path: string) : boolean;
var c1  : cardinal;
    wfd : TWin32FindData;
begin
  DelBackslash(path);
  result := fio_DirExists(path) or fio_DirExists(path);
  if result then begin
    if IsTextEqual(ExtractFileDrive(path), path + '\') then begin
      c1 := FindFirstFile(pchar(path + '\*.*'), wfd);
      result := (c1 <> INVALID_HANDLE_VALUE) or (GetLastError in [2, 18]);
      // 2 = file not found / 18 = no more files
      if c1 <> INVALID_HANDLE_VALUE then
        windows.FindClose(c1);
    end else begin
      result := false;
      c1 := FindFirstFile(pchar(path), wfd);
      if c1 <> INVALID_HANDLE_VALUE then
        try
          repeat
            if (wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) and
               IsTextEqual(wfd.cFileName, ExtractFileName(path)) then begin
              result := true;
              exit;
            end;
          until not FindNextFile(c1, wfd);
        finally windows.FindClose(c1) end;
    end;
  end;
end;

If you have problems with converting it to C++, feel free to ask me.

Regards, Madshi.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
MadshiCommented:
P.S: You can give in e.g.

c:\
c:\dir\subDir
\\server\share\
\\server\share\dir\subDir
0
RadlerCommented:
Maybe a SetErrorMode( SEM_FAILCRITICALERRORS ) to Win16 call is necessary ?

T++, Radler.
0
nietodCommented:
Actually, it sound like he wants the error dialogs to appear--I don't know why.  he said

>> NT itself comes up with a dialog complaining there
>> is no diskette in the drive.  It would be great if
>> I could get Win 9x to do the same thing.

If so, he should set the SEM_NOOPENFILEERRORBOX flag.
0
NormB062799Author Commented:
I've been trying to evaluate Madshi's proposed solution.  I think I follow the logic with the additional help of the explanation found under FindFirstFile.  I don't understand the business about fio_DirExists.  It doesn't seem to be a standard function and there is no explanation about what it's suppose to do.  Could you explain what it does?  

I think the proposed solution has some promise.  Hopefully tomorrow I can rewrite in C++,  evaluate it fully and get be able to accept or reject the proposal.  There have been too many other times however that I thought I had something that works that ended up not working.

In response to Nietod, here's what I was using:

\\Remove trailing \ if it exists
      int lastCharPos = strlen(drPath) - 1;
      if (drPath[lastCharPos] == '\\') drPath[lastCharPos] = 0;

      char* temppath = new char[lastCharPos+3];
      strcpy(temppath,drPath);
      if (GetFileAttributes(strcat(temppath,"\\")) && FILE_ATTRIBUTE_DIRECTORY)
            return 1;
      else
            return 0;
      delete temppath;


I've also noted the other comments, particularly about using SEM_NOOPENFILEERRORBOX.  That may be an adequate solution to my rather simple problem.  I'll check that out tomorrow and get back to you all.

Thanks for the help so far.
0
MadshiCommented:
Oops, I'm sorry, fio_DirExists is my own function. It works like this:

function fio_DirExists(dir: string);
var i1 : integer;
begin
  DelBackslash(dir);
  i1 := GetFileAttributes(pchar(dir));
  result := (i1 <> -1) and (FILE_ATTRIBUTE_DIRECTORY and i1 <> 0);
end;

The reason why I called it twice in the PathExists function is, that sometimes when you create a new share on NT, the first time you access the UNC path to this new share, GetFileAttributes fails, but the 2nd time works. Strange thing...

If you've additional questions about my sources, please ask me...
0
nietodCommented:
if (GetFileAttributes(strcat(temppath,"\\")) && FILE_ATTRIBUTE_DIRECTORY)

will not work!!!

When the file is not available--the condiftion you are trying to test for (like when the media is not present) the procedure will return 0xFFFFFFFF  That is what you should test for.  You test to see if the FILE_ATTRIBUTE_DIRECTORY bit is set.  Well it is set if 0xFFFFFFFF si returned to, but that doesn't mean a directory was found.

I think you will agree that GetFileAttributes is a lot simpler and more direct than madshi's solution--not offence madshi.
0
MadshiCommented:
Yes, nietod, I'm using GetFileAttributes, too. Look at my sources. The beginning of my function looks something like this:

  result := (GetFileAttribtes okay) or (GetFileAttributes okay);

(twice because of the already mentioned problem with newly created NT shares)

But that is not enough!!!! Sometimes this returns true, although it should not. Thus the rest of my code...
0
MadshiCommented:
P.S: One example:

If called with an (existing) UNC path GetFileAttributes ALWAYS returns that this path would be accessible, regardless whether you have access to that path or not.
0
nietodCommented:
Why does it return true?  I've never seen that happen.  Are you sure the code is correctly detecting the error condition?
0
nietodCommented:
My "why does it return truue" refers to the the GetfileAttributes() code, not the access issue.
0
MadshiCommented:
GetFileAttributes('\\server\share') -> 0x10

The UNC path is shared, but I've no access to it (e.g. when double clicking on this path in the network neighborhood, the explorer say: No access).

My system: win98. Server: NT4SP5.

Is that what you wanted to know?
0
nietodCommented:
I don't understand.   I have never seen GetFileAttributes return incorrect information.  Are you saying it does?
0
MadshiCommented:
Yes, I do...  :-)

Well, it DOES return incorrect information on my win98 PC. How about a check on your PC? Perhaps you never had a situation where you tested an UNC path that is existing, but not accessible?

I've tested another thing. I've connected a local drive to that UNC path, that is shared, but not accessible. GetFileAttributes returns 0x10 for that drive, too - while the Explorer refuses to show the content.
0
nietodCommented:
Yes it does return the wrong vlue when a UNC drive is not available.  It may be buffering the information.
0
MadshiCommented:
It can't be buffering. I never had access to that UNC path...   :-)
Well, it's not *absolutely* wrong, the path *IS* there, I just can't access it. Perhaps Microsoft has a different understanding of what GetFileAttribute should return than we have...
0
nietodCommented:
We're talking about a different type of access.  The question concerns removable media being available.  
0
NormB062799Author Commented:
Madshi gets the prize! His method seems to work with everything I've tried so far.  It consistently returns whether or not the path is accessible at the moment.  What I like about it is the fact that it tests whether or not the folder is accessible, not just the drive.

I never bothered pursuing the SEM_NOOPENFILEERRORBOX route since it's probably better to keep all this under program control.

For the next poor soul with this problem, here's the C++ code I ended up with.  It's essentially Madshi's algorithm.


int getRootPath(char* InPath,char* OutPath);
int dirExists(char* path);
int path_ready(char* drPath);


int getRootPath(char* InPath,char* OutPath)
{
      int result = 0;
      if (isalpha(InPath[0]) && (InPath[1] == ':'))
      {
            // Paths in the form A;
            strncpy(OutPath,InPath,2);
            result = 1;
      }
      else
      {
            // search UNC path
            int len = strlen(InPath);
            // if it walks like a duck
            if (((InPath[0] == '\\') && (InPath[1] == '\\')) && (len > 4))
            {
                  int i = 2;
                  int slashfound = 0;
                  // and talks like a duck
                  while ((i < len) && !slashfound)
                        if (InPath[i++] == '\\') slashfound = 1;
                  // It must be a duck.  We have the start of a UNC name \\Server\........
                  if (slashfound && i<len)
                  {
                        slashfound = 0;
                        while ((i < len) && !slashfound)
                              if (InPath[i++] == '\\') slashfound = 1;
                        if (slashfound)
                              strncpy(OutPath,InPath,i-1);      //This is the part that is the root directory
                        else
                              strcpy(OutPath,InPath);                  // InPath is itself a root
                        result = 1;
                  }
            }
      }
      return result;
}


int dirExists(char* path)
{
      DWORD result = GetFileAttributes(path);
      if ((result != -1) && (FILE_ATTRIBUTE_DIRECTORY && (result != 0)))
            return 1;
      else
            return 0;
}

int path_ready(char* drPath)
{
      int result = 0;

      if (strlen(drPath) <= MAX_PATH)
      {
            char* tempPath = new char[MAX_PATH];
            strcpy(tempPath,drPath);

            //Remove trailing \ if it exists
            int lastCharPos = strlen(tempPath) - 1;
            if (tempPath[lastCharPos] == '\\') tempPath[lastCharPos] = 0;
      
            SetErrorMode(SEM_FAILCRITICALERRORS);
            result = (dirExists(tempPath) || dirExists(tempPath));
            if (result)
            {
                  char* rootPath = new char[MAX_PATH];
                  if (getRootPath(tempPath,rootPath))
                  {
                        WIN32_FIND_DATA wfd;
                        HANDLE hFind;
                        if(strcmp(tempPath,rootPath) == 0)
                        {
                              // drPath is a root folder
                              // Must call FindFirstFile with \* appended for root folders
                              hFind = FindFirstFile(strcat(tempPath,"\\*"),&wfd);
                              result = ((hFind != INVALID_HANDLE_VALUE) || (GetLastError() == ERROR_FILE_NOT_FOUND) ||(GetLastError() == ERROR_NO_MORE_FILES));
                              if (hFind != INVALID_HANDLE_VALUE)
                                    FindClose(hFind);
                        }
                        else
                        {
                              // If it's a regular non root folder, must call FindFirstFile without the trailing backslash
                              hFind = FindFirstFile(tempPath,&wfd);
                              if (hFind != INVALID_HANDLE_VALUE)
                              {
                                    try
                                    {
            do
                                                if (wfd.dwFileAttributes && FILE_ATTRIBUTE_DIRECTORY)
                                                      return 1;
                                          while (FindNextFile(hFind,&wfd));
                                    }
                                    catch(...)
                                    {
                                    }
                                    FindClose(hFind);
                              }
                        }
                  }
                  delete rootPath;
            }      
            delete tempPath;
      }
      return result;


If anybody sees any flaws in it, I'd appreciate the feedback.

I'd like to thank everyone for their help and comments.
0
MadshiCommented:
nietod, that's right, but look at this extract from the original question:

"it should also be able to check such drives accessed over a network with UNC names."

So we're talking about UNC stuff, too.

NormB,

if ((result != -1) && (FILE_ATTRIBUTE_DIRECTORY && (result != 0)))

This line looks a bit strange to me. I'm no C++ guru, but should it not be like this?

if ((result != -1) && ((FILE_ATTRIBUTE_DIRECTORY && result) != 0))

?

Regards, Madshi.
0
nietodCommented:
Madshi, I missed that

good find for not knowing C, but wrong fix.  Use 1 & for bit operations.  It should be

if ((result != -1) && ((FILE_ATTRIBUTE_DIRECTORY & result) != 0))

or better yet, since 0 is false and non-zero is true.

if ((result != -1) && (FILE_ATTRIBUTE_DIRECTORY & result))
0
NormB062799Author Commented:
I guess I need to review the rules for operator precedence.  Thanks for the feedback.
0
NormB062799Author Commented:
By the way, does this work under Win 95 original edition?  Some of the material I've been reading suggests that UNC's are not supported very well (or at all) there.  Have you ever tried it on that platform?
0
MadshiCommented:
It works under all systems, including Win95 original edition...
0
nietodCommented:
>> rules for operator precedence
More importantly is which operator to use.  Note the use of "&" for the bit operation, not "&&" which is for boolean logic.  They will generate different results in this case.
0
NormB062799Author Commented:
Nietod: I was referring to Madshi's original code that I translated wrong because I didn't pay attention to operator precedence rules.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.