Find a free Drive Letter

  • Thread starter Thread starter Gavaskar
  • Start date Start date
G

Gavaskar

What is te best way to find a free or unused drive letter?

I'm thinking of doing DriveInfo[] allDrives = DriveInfo.GetDriver()

then cycle in a for loop for 1 through 26
if the name property is "null" I know that drive letter is unused so assign
it to a string cariable, and break out of the loop
 
Gavaskar said:
What is te best way to find a free or unused drive letter?

I'm thinking of doing DriveInfo[] allDrives = DriveInfo.GetDriver()

then cycle in a for loop for 1 through 26
if the name property is "null" I know that drive letter is unused so assign
it to a string cariable, and break out of the loop
DriveInfo.GetDrives() will only return assigned drives, so there will be no
DriveInfo with a null name property.

Got LINQ?

var unusedDrives = "CDEFGHIJKLMNOPQRSTUVWXYZ".Except(from d in
DriveInfo.GetDrives() select d.Name.First());
var firstUnusedDrive = unusedDrives.First();

Note that "A" and "B" are reserved for floppy drives and shouldn't be used
for other purposes even when available. Also, I don't know what DriveInfo
returns for network drives (connected and disconnected).
 
What is te best way to find a free or unused drive letter?
I'm thinking of doing DriveInfo[] allDrives = DriveInfo.GetDriver()

then cycle in a for loop for 1 through 26
if the name property is "null" I know that drive letter is unused so
assign it to a string cariable, and break out of the loop

Unless I could find an analogue to "GetLogicalDrives()" in the WinAPI I'd
probably just P/Invoke to it. At the top of my head (this is untested) it
would look something like this in C++ (note that you should check
"GetLogicalDrives()" for failure however):

TCHAR GetNextFreeDriveAfterC()
{
DWORD freeDrives = ::GetLogicalDrives() ^ 0x3FFFFFF;
TCHAR nextFreeDriveAfterC = '\0';
for (int i = 3; i < 26; ++i)
{
if (freeDrives & (1 << i))
{
nextFreeDriveAfterC = 'A' + i;
break;
}
}

return nextFreeDriveAfterC ;
}

Perhaps there's even a faster way of finding the first 1 bit rather than
looping but I didn't give it much thought.
 
Larry said:
What is te best way to find a free or unused drive letter?

I'm thinking of doing DriveInfo[] allDrives = DriveInfo.GetDriver()

then cycle in a for loop for 1 through 26
if the name property is "null" I know that drive letter is unused so
assign it to a string cariable, and break out of the loop

Unless I could find an analogue to "GetLogicalDrives()" in the WinAPI

If by "analogue" you mean "function that returns a bitmask", there is none.
Would you really expect the framework to expose an API that requires bit
twiddling? That probably violates half a dozen design guidelines. :-)
DriveInfo.GetDrives() and Environment.GetLogicalDrives() both use
::GetLogicalDrives() under the cover.
Perhaps there's even a faster way of finding the first 1 bit rather than
looping but I didn't give it much thought.
Here's my handy URL for all your bit twiddling needs:
http://www-graphics.stanford.edu/~seander/bithacks.html

Yes, there are faster ways, but none I'd bother with in general. In
addition, optimizing this is a rather pointless exercise, since there's
hardly a scenario where searching for the first available drive letter is a
hotspot. And if there *were*, the overhead of P/Invoking would probably be
more significant than any cleverness you'd come up with inside the function
anyway.

Here's another managed version for good measure, this one not using LINQ.

string[] drives = Environment.GetLogicalDrives();
int k = 'C'; // Skip A: and B: as they're reserved for floppy drives
int m = 0;
while (k <= 'Z' && m != drives.Length) {
if (drives[m][0] < k) {
++m;
} else if (drives[m][0] == k) {
++k;
} else {
break;
}
}
char? firstFreeDriveLetter = k <= 'Z' ? (char?) k : null;

I'd probably still prefer this to calling an unmanaged function, even if
it's just a P/Invoke to ::GetLogicalDrives(), because such things require
full trust and there's already managed functionality for it that requires
less permission (even if it's not as convenient as it could be in this case).
 
I just used the following code If Directory exists C:\ is enough to
indicate to me what I'm loking for.

for (int x = 90; x >= 65; x-- )

{

//char drvltr = (char)x;

if (!Directory.Exists((char) x+ ":\\"))

{

string drvLtr = (char)x + ":\\";

break;

}

}

Jeroen Mostert said:
Larry said:
What is te best way to find a free or unused drive letter?

I'm thinking of doing DriveInfo[] allDrives = DriveInfo.GetDriver()

then cycle in a for loop for 1 through 26
if the name property is "null" I know that drive letter is unused so
assign it to a string cariable, and break out of the loop

Unless I could find an analogue to "GetLogicalDrives()" in the WinAPI

If by "analogue" you mean "function that returns a bitmask", there is
none. Would you really expect the framework to expose an API that requires
bit twiddling? That probably violates half a dozen design guidelines. :-)
DriveInfo.GetDrives() and Environment.GetLogicalDrives() both use
::GetLogicalDrives() under the cover.
Perhaps there's even a faster way of finding the first 1 bit rather than
looping but I didn't give it much thought.
Here's my handy URL for all your bit twiddling needs:
http://www-graphics.stanford.edu/~seander/bithacks.html

Yes, there are faster ways, but none I'd bother with in general. In
addition, optimizing this is a rather pointless exercise, since there's
hardly a scenario where searching for the first available drive letter is
a hotspot. And if there *were*, the overhead of P/Invoking would probably
be more significant than any cleverness you'd come up with inside the
function anyway.

Here's another managed version for good measure, this one not using LINQ.

string[] drives = Environment.GetLogicalDrives();
int k = 'C'; // Skip A: and B: as they're reserved for floppy drives
int m = 0;
while (k <= 'Z' && m != drives.Length) {
if (drives[m][0] < k) {
++m;
} else if (drives[m][0] == k) {
++k;
} else {
break;
}
}
char? firstFreeDriveLetter = k <= 'Z' ? (char?) k : null;

I'd probably still prefer this to calling an unmanaged function, even if
it's just a P/Invoke to ::GetLogicalDrives(), because such things require
full trust and there's already managed functionality for it that requires
less permission (even if it's not as convenient as it could be in this
case).
 
If by "analogue" you mean "function that returns a bitmask", there is
none. Would you really expect the framework to expose an API that requires
bit twiddling? That probably violates half a dozen design guidelines

I wasn't suggesting they literally expose it that way (forcing you to use
bit operators). A high-level equivalent is clearly preferable and that's
what I would expect (using one of the availabe bit classes or whatever).
DriveInfo.GetDrives() and Environment.GetLogicalDrives() both use
::GetLogicalDrives() under the cover.

Almost certainly but you can't assume it regardless of what you might see in
MSFT's (current) implementation. It's immaterial in any case. Simply
returning a 26 bit array or some other specific collection would have made
more (fundamental) sense IMO.
And if there *were*, the overhead of P/Invoking would probably
be more significant than any cleverness you'd come up with inside the
function anyway.

It's not a clever alternative I'm looking for since that should normally be
avoided (if it complicates things). In any case, "GetLogicalDrives()" is in
"kernel32.dll" which is all but guaranteed to be loaded at the time. Since
it's also (probably) driving the .NET equivalents as you noted, those
functions will also have the same overhead.
I'd probably still prefer this to calling an unmanaged function, even if
it's just a P/Invoke to ::GetLogicalDrives(), because such things require
full trust and there's already managed functionality for it that requires
less permission (even if it's not as convenient as it could be in this
case).

The security issue is unconvincing. For most it will never realistically
fail and relying on unmanaged code when you don't absolutely have to is an
imperfect situation though not a serious flaw. Ultimately it's a religious
issue but in this case doing it the .NET way seems more like a cult to me
(even if
it is preaching to the mainstream masses).
 
Larry said:
I wasn't suggesting they literally expose it that way (forcing you to use
bit operators). A high-level equivalent is clearly preferable and that's
what I would expect (using one of the availabe bit classes or whatever).
DriveLetterCollection, something like that? Probably not worth it. But with
your bit twiddling acumen, you could write it for the rest. :-)
Almost certainly but you can't assume it regardless of what you might see in
MSFT's (current) implementation.

I'm sorry, I was actually specifically talking about the current
implementation, not trying to make a sweeping statement. Implementations can
change, the Mono guys want to play too, etc, that's not the point. I wasn't
arguing this was some sort of hard guarantee, I was just pointing out that
the managed functions get as close as is reasonable (that is, this is what
the framework guys thought served us best, given the existence of
::GetLogicalDrives()).
It's immaterial in any case. Simply returning a 26 bit array or some
other specific collection would have made more (fundamental) sense IMO.
Eh, lots of room for arguing. A 26-bit collection is still too complicated
for most programmers and most scenarios, and a DriveLetterCollection that
could have answered such neat questions as "what are the unassigned drive
letters, if any" would probably be too much overhead to implement for too
little gain.

Drive letters are so 1960s anyway. :-)
The security issue is unconvincing. For most it will never realistically
fail

Agreed, however, almost all .NET security issues can be marginalized this
way because running with full trust probably is probably a 99% scenario.
and relying on unmanaged code when you don't absolutely have to is an
imperfect situation though not a serious flaw.

Also agreed. I would like to point out, though, that P/Invoking to an
existing function is still a lot less involved than writing your own
unmanaged function and P/Invoking to that, if only because you have to
integrate it in the build and deployment process -- there's some maintenance
overhead.
Ultimately it's a religious issue but in this case doing it the .NET way
seems more like a cult to me (even if it is preaching to the mainstream
masses).
Well if you leave out the "cult" bit I agree it's a religious issue (in the
technical sense). In the framework I trust!

Thankfully this is such a minor issue, technically speaking, that it matters
little how you solve it. For myself, I would only break out the C compiler
if it either couldn't be done in .NET at all, or (increasingly less common)
it couldn't be done fast enough.
 
Gavaskar said:
I just used the following code If Directory exists C:\ is enough to
indicate to me what I'm loking for.

for (int x = 90; x >= 65; x-- )

{

//char drvltr = (char)x;

if (!Directory.Exists((char) x+ ":\\"))

{

string drvLtr = (char)x + ":\\";

break;

}
Well I dislike this intensely because it's simpler than my solution and I
can't immediately think of circumstances in which it fails. So fie on you!
 
Back
Top