|
My Training Period: cc hours. Before you begin, read some instruction here.
Programming Abilities:
Windows Volume Management Functions
|
|||||||||||||||||||
The following program example uses GetDriveType() to test all the drive type of the machine. The list of the drives is supplied in the program.
#include <windows.h>
#include <stdio.h>
// using two dimensional arrays to store the drive strings
char drive2[13][5] = {"A:\\", "B:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\","I:\\", "J:\\", "K:\\", "L:\\"};
int main()
{
for(int i=0; i<12; i++)
{
UINT test = GetDriveType(drive2[i]);
switch(test)
{
case 0: printf("Drive %s is type %d - Cannot be determined.\n", &drive2[i], test);
break;
case 1: printf("Drive %s is type %d - Invalid root path/Not available.\n", &drive2[i], test);
break;
case 2: printf("Drive %s is type %d - Removable.\n", &drive2[i], test);
break;
case 3: printf("Drive %s is type %d - Fixed.\n", &drive2[i], test);
break;
case 4: printf("Drive %s is type %d - Network.\n", &drive2[i], test);
break;
case 5: printf("Drive %s is type %d - CD-ROM.\n", &drive2[i], test);
break;
case 6: printf("Drive %s is type %d - RAMDISK.\n", &drive2[i], test);
break;
default : "Unknown value!\n";
}
}
return 0;
}
The output:
Drive A:\ is type 2 - Removable.
Drive B:\ is type 1 - Invalid root path/Not available.
Drive C:\ is type 3 - Fixed.
Drive D:\ is type 3 - Fixed.
Drive E:\ is type 3 - Fixed.
Drive F:\ is type 3 - Fixed.
Drive G:\ is type 3 - Fixed.
Drive H:\ is type 3 - Fixed.
Drive I:\ is type 3 - Fixed.
Drive J:\ is type 5 - CD-ROM.
Drive K:\ is type 5 - CD-ROM.
Drive L:\ is type 2 - Removable.
Press any key to continue
The following is an output sample run using VC++ EE 2005 on another machine.

The following Table lists the needed information in order to use the GetLogicalDrives() function.
The return value is a bitmask representing the currently available disk drives. Bit position 0 (the least-significant bit) is drive A, bit position 1 is drive B, bit position 2 is drive C, and so on.
|
Information |
Description |
|
The function |
GetLogicalDrives(). |
|
The use |
Retrieves a bitmask representing the currently available disk drives. |
|
The prototype |
DWORD GetLogicalDrives(void); |
|
Example |
TCHAR szDrive[ ] = _T(" A:");
DWORD uDriveMask = GetLogicalDrives(); printf("This machine has the following logical drives:\n");
while(uDriveMask) { if(uDriveMask & 1) printf(szDrive); // binary 1 shows that the drive is available and 0 otherwise ++szDrive[1]; // shift right uDriveMask >>= 1; } |
|
The parameters |
This function has no parameters. |
|
The return value |
If the function succeeds, the return value is a bitmask representing the currently available disk drives. Bit position 0 (the least-significant bit) is drive A, bit position 1 is drive B, bit position 2 is drive C, and so on. |
|
The header file |
<windows.h> |
|
Table 2: GetLogicalDrives() information. |
|
The following program example uses GetLogicalDrives() to retrieve the bitmask, then using a while loop, bitwise AND and binary shift right operator to list all the available logical drives.
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <tchar.h>
// initial value
TCHAR szDrive[ ] = _T(" A:");
int main()
{
DWORD uDriveMask = GetLogicalDrives();
printf("The bitmask of the logical drives in hex: %0X\n", uDriveMask);
printf("The bitmask of the logical drives in decimal: %d\n", uDriveMask);
if(uDriveMask == 0)
printf("GetLogicalDrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while(uDriveMask)
{// use the bitwise AND, 1–available, 0-not available
if(uDriveMask & 1)
printf(szDrive);
// increment...
++szDrive[1];
// shift the bitmask binary right
uDriveMask >>= 1;
}
printf("\n ");
}
return 0;
}
The output:
The bitmask of the logical drives in hex: FFD
The bitmask of the logical drives in decimal: 4093
This machine has the following logical drives:
A: C: D: E: F: G: H: I: J: K: L:
Press any key to continue
The following is output when running using VC++ EE 2005 on another machine.

Notice that, in binary the FFD (hex) is equal to 111111111101 that is A: until L: drive with no B: drive.
The following Table lists the needed information in order to use the GetLogicalDriveStrings() function.
|
Information |
Description |
|
The function |
GetLogicalDriveStrings(). |
|
The use |
Fills a buffer with strings that specify valid drives in the system. |
|
The prototype |
DWORD GetLogicalDriveStrings(DWORD nBufferLength, LPTSTR lpBuffer); |
|
Example |
DWORD mydrives = 100; char lpBuffer[100];
GetLogicalDriveStrings(mydrives, lpBuffer); |
|
The parameters |
nBufferLength - [in] Maximum size of the buffer pointed to by lpBuffer, in TCHARs. This size does not include the terminating null character. If this parameter is zero, lpBuffer is not used. lpBuffer - [out] Pointer to a buffer that receives a series of null-terminated strings, one for each valid drive in the system, plus with an additional null character. Each string is a device name. |
|
The return value |
If the function succeeds, the return value is the length, in characters, of the strings copied to the buffer, not including the terminating null character. Note that an ANSI-ASCII null character uses one byte, but a Unicode null character uses two bytes. If the buffer is not large enough, the return value is greater than nBufferLength. It is the size of the buffer required to hold the drive strings. If the function fails, the return value is zero. To get extended error information, use the GetLastError() function. |
|
The header file |
<windows.h> |
|
Table 3: GetLogicalDriveStrings() information. |
|
The following program example uses GetLogicalDriveStrings() to list all the available logical drives.
#include <windows.h>
#include <stdio.h>
// buffer length
DWORD mydrives = 100;
// buffer for drive string storage
char lpBuffer[100];
int main()
{
DWORD test = GetLogicalDriveStrings( mydrives, lpBuffer);
printf("GetLogicalDriveStrings() return value: %d\nError: %d \n", test, GetLastError());
printf("The logical drives of this machine are:\n");
for(int i = 0; i<100; i++)
printf("%c", lpBuffer[i]);
printf("\n");
return 0;
}
The output:
GetLogicalDriveStrings() return value: 44
Error: 0
The logical drives of this machine are:
A:\ C:\ D:\ E:\ F:\ G:\ H:\ I:\ J:\ K:\ L:\
Press any key to continue
VC++ EE 2005 sample output.

The following Table lists the needed information in order to use the QueryDosDevice() function and then followed by a program example.
|
Information |
Description |
|
The function |
QueryDosDevice(). |
|
The use |
Retrieves information about MS-DOS device names. The function can obtain the current mapping for a particular MS-DOS device name. The function can also obtain a list of all existing MS-DOS device names. |
|
The prototype |
DWORD QueryDosDevice( LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax ); |
|
Example |
char lpDeviceName[11][3] = {"A:", "C:", "D:", "E:", "F:", "G:", "H:","I:", "J:", "K:", "L:"}; // the buffer for storage char lpTargetPath[1000];
QueryDosDevice(lpDeviceName[i], lpTargetPath, 1000); |
|
The parameters |
lpDeviceName - [in] Pointer to an MS-DOS device name string specifying the target of the query. The device name cannot have a trailing backslash. This parameter can be NULL. In that case, the QueryDosDevice() function will store a list of all existing MS-DOS device names into the buffer pointed to by lpTargetPath. lpTargetPath - [out] Pointer to a buffer that will receive the result of the query. The function fills this buffer with one or more null-terminated strings. The final null-terminated string is followed by an additional NULL. If lpDeviceName is non-NULL, the function retrieves information about the particular MS-DOS device specified by lpDeviceName. The first null-terminated string stored into the buffer is the current mapping for the device. The other null-terminated strings represent undeleted prior mappings for the device. If lpDeviceName is NULL, the function retrieves a list of all existing MS-DOS device names. Each null-terminated string stored into the buffer is the name of an existing MS-DOS device. ucchMax - [in] Maximum number of TCHARs that can be stored into the buffer pointed to by lpTargetPath. |
|
The return value |
If the function succeeds, the return value is the number of TCHARs stored into the buffer pointed to by lpTargetPath. If the function fails, the return value is zero. To get extended error information, call GetLastError(). If the buffer is too small, the function fails and the last error code is ERROR_INSUFFICIENT_BUFFER. |
|
The header file |
<windows.h> |
|
Table 4: QueryDosDevice() information. |
|
The QueryDosDevice() function retrieves information about MS-DOS device names. The function can obtain the current mapping for a particular MS-DOS device name. The function can also obtain a list of all existing MS-DOS device names.
MS-DOS device names are stored as junctions in the object name space. The code that converts an MS-DOS path into a corresponding path uses these junctions to map MS-DOS devices and drive letters.
The QueryDosDevice() function enables an application to query the names of the junctions used to implement the MS-DOS device namespace as well as the value of each specific junction.
When all existing MS-DOS device names are queried, the list of device names that are returned is dependent on whether it is running in the LocalSystem context.
If so, only the device names included in the Global MS-DOS Device namespace will be returned.
If not, a concatenation of the device names in the Global and Local MS-DOS Device namespaces will be returned. If a device name exists in both namespaces, QueryDosDevice() will return the entry in the Local MS-DOS Device namespace.
Program example using QueryDosDevice().
#include <windows.h>
#include <stdio.h>
// query these on my machine. The L: is thumb drive, J: and K: are CD-ROMs
char lpDeviceName[11][3] = {"A:", "C:", "D:", "E:", "F:", "G:", "H:","I:", "J:", "K:", "L:"};
// the buffer for storage
char lpTargetPath[1000];
int main()
{
for(int i=0; i<13; i++)
{
// using NULL for the parameter 1 is not working lol...
DWORD test = QueryDosDevice(lpDeviceName[i], lpTargetPath, 1000);
// test the return value and error if any
printf("\nQueryDosDevice() return value: %d, Error: %d\n", test, GetLastError());
printf("The DOS device for %s is:\n", lpDeviceName[i]);
// display the result
for(int i = 0; i<35; i++)
printf("%c", lpTargetPath[i]);
}
printf("\n");
return 0;
}
The output:
QueryDosDevice() return value: 17, Error: 0
The DOS device for A: is:
\Device\Floppy0
QueryDosDevice() return value: 25, Error: 0
The DOS device for C: is:
\Device\HarddiskVolume1
QueryDosDevice() return value: 25, Error: 0
The DOS device for D: is:
\Device\HarddiskVolume5
QueryDosDevice() return value: 25, Error: 0
The DOS device for E: is:
\Device\HarddiskVolume2
QueryDosDevice() return value: 25, Error: 0
The DOS device for F: is:
\Device\HarddiskVolume3
QueryDosDevice() return value: 25, Error: 0
The DOS device for G: is:
\Device\HarddiskVolume4
QueryDosDevice() return value: 25, Error: 0
The DOS device for H: is:
\Device\HarddiskVolume6
QueryDosDevice() return value: 25, Error: 0
The DOS device for I: is:
\Device\HarddiskVolume7
QueryDosDevice() return value: 16, Error: 0
The DOS device for J: is:
\Device\CdRom0 olume7
QueryDosDevice() return value: 16, Error: 0
The DOS device for K: is:
\Device\CdRom1 olume7
QueryDosDevice() return value: 30, Error: 0
The DOS device for L: is:
\Device\Harddisk2\DP(1)0-0+a
QueryDosDevice() return value: 0, Error: 6
The DOS device for is:
\Device\Harddisk2\DP(1)0-0+a
QueryDosDevice() return value: 0, Error: 6
The DOS device for is:
\Device\Harddisk2\DP(1)0-0+a
Press any key to continue
A VC++ EE 2005 sample output.
![]() |
The following Table lists the needed information in order to use the GetDiskFreeSpaceEx() function.
|
Information |
Description |
|
The function |
GetDiskFreeSpaceEx(). |
|
The use |
Retrieves information about the amount of space available on a disk volume: the total amount of space, the total amount of free space, and the total amount of free space available to the user associated with the calling thread. |
|
The prototype |
BOOL GetDiskFreeSpaceEx( LPCTSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailable,
PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes); |
|
Example |
char pszDrive[10] = "C:\\"; DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
GetDiskFreeSpace(pszDrive, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); |
|
The parameters |
lpDirectoryName - [in] Pointer to a null-terminated string that specifies a directory on the disk of interest. If this parameter is NULL, the function uses the root of the current disk. If this parameter is a UNC name, it must include a trailing backslash (for example, \\MyServer\MyShare\). Note that this parameter does not have to specify the root directory on a disk. The function accepts any directory on the disk. lpFreeBytesAvailable - [out] Pointer to a variable that receives the total number of free bytes on the disk that are available to the user associated with the calling thread. This parameter can be NULL. If per-user quotas are in use, this value may be less than the total number of free bytes on the disk. lpTotalNumberOfBytes - [out] Pointer to a variable that receives the total number of bytes on the disk that are available to the user associated with the calling thread. This parameter can be NULL. If per-user quotas are in use, this value may be less than the total number of bytes on the disk. To determine the total number of bytes on a disk or volume, use IOCTL_DISK_GET_LENGTH_INFO. lpTotalNumberOfFreeBytes - [out] Pointer to a variable that receives the total number of free bytes on the disk. This parameter can be NULL. |
|
The return value |
If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError(). |
|
The header file |
<windows.h> |
|
Table 5: GetDiskFreeSpaceEx() information. |
|
The GetDiskFreeSpace() function retrieves information about the specified disk, including the amount of free space on the disk.
The GetDiskFreeSpace() function cannot report volume sizes that are greater than 2 gigabytes (GB) but when tested it just OK for volume size greater than 2GB!
For large capacity hard drives, use the GetDiskFreeSpaceEx() function.
Program example using GetDiskFreeSpaceEx() and GetDiskFreeSpace() functions showing the free space of dive C.
#include <windows.h>
#include <stdio.h>
int main()
{
char pszDrive[10] = "C:\\";
// 64 bits integer
__int64 lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes;
DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
BOOL test = GetDiskFreeSpaceEx(
pszDrive,
(PULARGE_INTEGER)&lpFreeBytesAvailable,
(PULARGE_INTEGER)&lpTotalNumberOfBytes,
(PULARGE_INTEGER)&lpTotalNumberOfFreeBytes
);
printf("Drive to be checked: %s\n", pszDrive);
printf("\nUsing GetDiskFreeSpaceEx()...\n");
// check the return value
printf("The return value: %d, error code: %d\n", test, GetLastError());
printf("Total number of free bytes available for user-caller: %ul\n", lpFreeBytesAvailable);
printf("Total number of bytes available for user: %ul\n", lpTotalNumberOfBytes);
// just straight to the free bytes result
printf("Total number of free bytes on disk: %ul\n", lpTotalNumberOfFreeBytes);
BOOL fResult = GetDiskFreeSpace(pszDrive,
&dwSectPerClust,
&dwBytesPerSect,
&dwFreeClusters,
&dwTotalClusters);
printf("\nUsing GetDiskFreeSpace()...\n");
printf("The return value: %d, error code: %d\n", fResult, GetLastError());
printf("Sector per cluster = %ul\n", dwSectPerClust);
printf("Bytes per sector = %ul\n", dwBytesPerSect);
printf("Free cluster = %ul\n", dwFreeClusters);
printf("Total cluster = %ul\n", dwTotalClusters);
// using GetDiskFreeSpace() need some calculation for the free bytes on disk
printf("Total free bytes = %ul\n", (dwFreeClusters*dwSectPerClust*dwBytesPerSect));
return 0;
}
The output:
Drive to be checked: C:\
Using GetDiskFreeSpaceEx()...
The return value: 1, error code: 0
Total number of free bytes available for user-caller: 263147520l
Total number of bytes available for user: 1897263104l
Total number of free bytes on disk: 263147520l
Using GetDiskFreeSpace()...
The return value: 1, error code: 0
Sector per cluster = 8l
Bytes per sector = 512l
Free cluster = 64245l
Total cluster = 2560351l
Total free bytes = 263147520l
Press any key to continue
VC++ EE 2005 sample output.

Two factors can make it hard to reliably mount a specific volume at a specified volume mount point across operating system restarts.
Two different volumes can have the same label, which makes them indistinguishable except by drive letter.
Drive letters do not necessarily remain the same. If a computer's administrator does not use the Disk Administrator to enforce drive letters, then drive letters can change as drives are removed from or added to the system.
To solve this problem, the system refers to volumes to be mounted with unique volume names. These are strings of this form:
"\\?\Volume{GUID}\"
Where GUID is a globally unique identifier (GUID) that identifies the volume. For example:
\\?\Volume{a3446ed5-ebd9-11d8-a25c-806d6172696f}\
The \\?\ prefix disables path parsing and is not parsed along with the path. For example, "\\?\C:\myworld\private" is parsed as "C:\myworld\private".
By pre-pending paths with \\?\, you can specify paths that are 32,767 Unicode characters long. However, each component in the path cannot be more than a file-system-specific value returned by the function GetVolumeInformation().
You must specify full paths when using specifying unique volume names with \\?\. This prefix can also be used with paths constructed according to the universal naming convention (UNC), such as "\\OtherComputer\Directory\Filename.ext".
All volume mount point functions that take a unique volume name as a parameter require the trailing backslash; and all volume mount point functions that return a unique volume name provide the trailing backslash.
However, this is not the case with CreateFile(). You can open a volume by calling CreateFile() and omit the trailing backslash from the volume name you specify.
CreateFile() processes a unique volume name with an appended backslash as the root directory of the volume.
The operating system assigns a unique volume name to a volume when the computer first encounters it such as during formatting or installation.
Path lengths may be a concern when a volume with a deep directory tree is mounted to another volume. This is because path of the mounted volume is concatenated to the path of the mount point.
The globally defined constant MAX_PATH defines the maximum number of characters a path can have. You can avoid this constraint by doing the following:
Refer to volumes by their unique volume names.
Use the Unicode (W) versions of file functions, which support the \\?\ prefix.
The following Table lists the needed information in order to use the GetVolumeInformation() function.
|
Information |
Description |
|
The function |
GetVolumeInformation(). |
|
The use |
Retrieves information about a file system and volume whose root directory is specified. |
|
The prototype |
BOOL GetVolumeInformation( LPCTSTR lpRootPathName, LPTSTR lpVolumeNameBuffer, DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize); |
|
Example |
#define BUFSIZE MAX_PATH #define FILESYSNAMEBUFSIZE MAX_PATH
// buffer for unique volume identifiers char buf[BUFSIZE]; DWORD lpMaximumComponentLength; // flags that describe the file system DWORD dwSysFlags; char FileSysNameBuf[FILESYSNAMEBUFSIZE];
GetVolumeInformation( buf, NULL, BUFSIZE, NULL, &lpMaximumComponentLength, &dwSysFlags, FileSysNameBuf, FILESYSNAMEBUFSIZE); |
|
The parameters |
lpRootPathName - [in] Pointer to a string that contains the root directory of the volume to be described. If this parameter is NULL, the root of the current directory is used. A trailing backslash is required. For example, you would specify \\MyServer\MyShare as \\MyServer\MyShare\, or the C drive as "C:\". lpVolumeNameBuffer - [out] Pointer to a buffer that receives the name of the specified volume. nVolumeNameSize - [in] Length of the volume name buffer, in TCHARs. This parameter is ignored if the volume name buffer is not supplied. lpVolumeSerialNumber - [out] Pointer to a variable that receives the volume serial number. This parameter can be NULL if the serial number is not required. lpMaximumComponentLength - [out] Pointer to a variable that receives the maximum length, in TCHARs, of a file name component supported by the specified file system. A file name component is that portion of a file name between backslashes. The value stored in variable pointed to by *lpMaximumComponentLength is used to indicate that long names are supported by the specified file system. For example, for a FAT file system supporting long names, the function stores the value 255, rather than the previous 8.3 indicator. Long names can also be supported on systems that use the NTFS file system. lpFileSystemFlags - [out] Pointer to a variable that receives flags associated with the specified file system. This parameter can be one or more of the following flags; however, FS_FILE_COMPRESSION and FS_VOL_IS_COMPRESSED are mutually exclusive.
|