什么是“卷”?
卷,又稱為“邏輯驅動器”,是 NTFS, FAT32 等文件系統組織結構的最高層。卷是存儲設備(如硬盤)上由文件系統管理的一塊區域,是在邏輯上相互隔離的存儲單元。一個磁盤分區至少包含一個卷,當然卷也可以存在於多個磁盤分區上,僅存在於一個分區上的卷稱為“簡單卷“,存在於多個磁盤分區上的卷稱為”多分區卷“或”跨區卷“。最為常見的情況是一個分區只包含 一個卷,一個卷只存在於一個分區上,所以往往會造成的混淆是卷等於分區。卷存在卷標,程序可以通過卷標訪問卷。
卷掛接在 Unix/Linux/Netware 系統上應用比較常見,Windows 可能由於多用於個人平台,所以知道卷掛接技術的人就比例上來說少了很多。你可以將它理解為:把一個物理設備掛接到一個邏輯盤符或文件夾中來使用,例如:如果需要擴展 C 盤的容量,而 C 盤所在的分區/磁盤已沒有空閑空間來擴展它時,可以加裝一個 200GB 的新硬盤,然后在 C: 中新增一個目錄 ExtDisk,並將這個新磁盤掛接到這個目錄中即可,現在 C: 盤的容量就增加了 200GB。有一點要注意,就是卷掛接只能用於 NTFS 系統。
4.2.1 遍歷卷並獲取屬性
獲取一個主機上的所有驅動器列表有兩種方法,一種是使用 GetLogicalDrives 或
GetLogicalDriveStrings,另一種是使用 FindFirstVolume 和 FindNextVolume 組合。
第一種方法獲取主機上的邏輯驅動器,也就是所有分配的卷標的驅動器,返回的結果是
驅動器的根路徑。第二種方法返回的是“\\?\volume{GUID}”形式的驅動器設備名。
使用 GetDriveType API 可以獲取驅動器類型,使用 GetVolumeInformation 可以獲取驅
動器屬性。
1.關鍵 API
(1) GetLogicalDrives。
獲取主機中所有的邏輯驅動器,以 BitMap 的形式返回,其函數原型如下:
◇返回值
GetLogicalDrive 函數返回一個 DWORD 類型的值,第一位表示所對應的驅動器是否存在。
一般情況下 DWORD 的數據長度是 32 位,在這個 DWORD 中,每一位對應了一個邏輯驅動器是
否存在。第二位如果是“1”則表示驅動器“B:”存在,第 4 位如果是“1”則表示驅動器
“D:”是存在的,以此類推。
(2) GetLogicalDriverStrings。
獲取主機中所有驅動器,以驅動器根路徑字符串返回,其函數原型如下:
◇參數
nBufferLength:參數 lpBuffer 所指向的內存空間的大小,以字節為單位。
lpBuffer:指向存儲返回結果字符串的內存空間。
◇返回值
函數的返回值指明了函數調用是否成功,如果成功則返回緩沖區中返回結果的總長度。
如果返回值大於 nBufferLength,說明給定的緩沖區大小不夠,返回值是實際需要的大小。
如果返回 0,則說明函數運行出錯。
◇使用說明
這個 API 實現了與 GetLogicalDrives 同樣的功能,卻以一種更直觀的方式返回執行結
果。函數執行結果放在 lpBuffer 所指向的內存區域中,此內存區域大小由 nBufferLength
參數指定,使函數返回結果不至於溢出。在調用此函數前需保證內存分配。
函數調用成功后,將在緩沖區中依次填入本機所具有的驅動器根路徑字符串,如在筆者
系統中有 5 個邏輯驅動器“C:\”、“D:\”“E:\”、“F:\”、“I:\”。執行后在緩沖
區中的結果如下:
也就是連續放置了“C:\”、“D:\”“E:\”、“F:\”、“I:\”這 5 個字符串(會
在每個字符串后加一個‘\o’結束符,在所有卷標字符串的最后再加一個結束符)。
(3)FindFirstVolume。
查找主機中的第一個驅動器,返回驅動器設備名,其函數原型如下:
◇參數
lpszVolumeName:指向驅動器名的內存緩沖區。
cchBufferLength:參數 lpszVolumeName 所指向的緩沖區大小,以字節為單位。
◇返回值
驅動器查找句柄, F 貓 NextVolume 和 FindVolumeColse 的參數, 如果執行失敗, 返回 NULL。
(4)FindNextVolume
查找主機中后繼的邏輯驅動器,其函數原型如下:
◇參數
hFindVolume: FindFirstVolume 所返回的驅動器查找句柄。
lpszVolumeName:指向保存驅動器名的內存緩沖區。
cchBufferLength:參數 lpszVolumeName 所指向的緩沖區大小,以字節為單位。
◇返回值
返回 BOOL 表示是否成功,如果失敗說明已經查找完成所有邏輯驅動器。
(5)FindVo1umeClose。
\關閉 FindFirstVolume 打開的卷遍歷句柄,其函數原型如下:
◇參數
hFindVolume:要關閉的驅動器查找句柄。
◇返回值
返回 BOOL 值表示是否成功關閉句柄。
(6) GetDriveType。
獲取驅動器類型,其函數原型如下:
◇參數
lpRootPathIName:驅動器根路徑,如“C:\”。
◇返回值 /
驅動器的類型,如 DRIVE' FIXED 表示硬盤,DRIVE_CDROM 表示光盤等。詳見實例 4-2
的 GetDirverInfo 函數。
(7) GetVolumeInformation。
獲取邏輯驅動器信息,其函數原型如下:
◇參數
lpRootPathName:輸入參數,指向所要獲取屬性的驅動器的根路徑字符串。
lpVolumeNameBuffer:輸出參數,返回驅動器名。
GetLogicalDriveStrings枚舉磁盤,然后可以調用GetDiskFreeSpaceEx獲取大小信息。
GetLogicalDrives
DWORD WINAPI GetLogicalDrives(void);
該函數沒有參數,返回值為DWORD,是一個位掩碼代表當前的磁盤驅動器。第0位表示驅動器A,第二位表示驅動器B。以此類推,某一位為1表示存在驅動器,為0表示不存在。
void GetDisksInformation()
{
printf("Begin Call GetDisksInformation()\n");
DWORD dwDisk = GetLogicalDrives();
int dwMask = 1;
int step = 1;
dwMask<<1;
while (step < 32)
{
++step;
switch (dwMask&dwDisk)
{
case 1:
printf("volume A\n");
break;
case 2:
printf("volume B\n");
break;
case 4:
printf("volume C\n");
break;
case 8:
printf("volume D\n");
break;
case 32:
printf("volume E\n");
break;
case 64:
printf("volume F\n");
break;
case 128:
printf("volume G\n");
break;
case 256:
printf("volume H\n");
break;
default:
break;
}
dwMask = dwMask<<1;
}
printf("end Call GetDisksInformation()\n");
}

GetLogicalDriveStrings
DWORD WINAPI GetLogicalDriveStrings(
_In_ DWORD nBufferLength,
_Out_ LPTSTR lpBuffer
);
lpBuffer ,存儲驅動器根路徑字符串的緩沖區內存
nBufferLength 緩沖區 的大小
void GetDisksInformationEx()
{
printf("Begin Call GetDisksInformationEx()\n");
CHAR szLogicalDriveString[BUFFERSIZE];
PCHAR szDrive;
ZeroMemory(szLogicalDriveString,BUFFERSIZE);
GetLogicalDriveStrings(BUFFERSIZE - 1, szLogicalDriveString);
szDrive = szLogicalDriveString;
while (*szDrive)
{
printf("volume %s\n",szDrive);
szDrive += (lstrlen(szDrive) + 1);
}
printf("end Call GetDisksInformationEx()\n");
}

FindFirstVolume
HANDLE WINAPI FindFirstVolume(
_Out_ LPTSTR lpszVolumeName, //驅動器名的緩沖區
_In_ DWORD cchBufferLength //緩沖區的大小
);
返回值為驅動器的句柄
BOOL WINAPI FindNextVolume(
_In_ HANDLE hFindVolume, //調用FindFirstVolume返回的驅動器句柄
_Out_ LPTSTR lpszVolumeName, //驅動器名的緩沖區
_In_ DWORD cchBufferLength //緩沖區的大小
);
查找下一個驅動器
BOOL WINAPI FindVolumeClose(
_In_ HANDLE hFindVolume //調用FindFirstVolume返回的驅動器句柄
);
該函數用來關閉一個驅動器的句柄。被關閉的句柄就不能再在FindNextVolume或者FindVolumeClose中使用了。
void FindVolume()
{
printf("Begin Call FindVolume()\n");
CHAR szVolume[MAX_PATH];
HANDLE hVolume;
ZeroMemory(szVolume,MAX_PATH);
hVolume = FindFirstVolume(szVolume,MAX_PATH);
if (hVolume == INVALID_HANDLE_VALUE)
{
printf("Not found the first volume\n");
}
printf("volume %s\n",szVolume);
while (FindNextVolume(hVolume,szVolume,MAX_PATH))
{
printf("volume %s\n",szVolume);
}
FindVolumeClose(hVolume);
printf("end Call FindVolume()\n");
}

#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE MAX_PATH
BOOL GetDirverInfo(LPSTR szDrive);
int main(void)
{
TCHAR buf[BUFSIZE];
HANDLE hVol;
BOOL bFlag;
/**********************************
* FindFirstVolume(buf,BUFSIZE)
* 功能:查找主機中的第一個驅動器
* 參數:buf,BUFSIZE
* buf 指向驅動器名的內存緩存區
* BUFSIZE 參數buf所指向的緩存區大小
* 返回值 HANDLE :驅動器設備名
**********************************/
hVol = FindFirstVolume(buf,BUFSIZE);
if (hVol == INVALID_HANDLE_VALUE)
{
printf(TEXT("No volmes found!\n"));
return (-1);
}
GetDirverInfo(buf);
while(FindNextVolume(hVol,buf,BUFSIZE))
{
GetDirverInfo(buf);
}
bFlag = FindVolumeClose(hVol);
char ch;
ch = getchar();
return (bFlag);
}
//GetDirverInfo函數
BOOL GetDirverInfo(LPSTR szDrive)
{
UINT uDiveType;
DWORD dwVolumeSerialNumber;
DWORD dwMaximumComponentLength;
DWORD dwFileSystemFlags;
CHAR szFileSystemNameBuffer[BUFSIZE];
CHAR szDirverName[MAX_PATH];
printf("\n%s\n",szDrive);
/*********************
* UINT WINAPI GetDriveType(LPSTR szDrive);
* 功能:判斷一個磁盤驅動器的類型
* 參數:LPSTR szDrive
* 指明要獲取屬性的驅動器的根目錄,如C:\
* 返回值 UINT:表示磁盤類型
**********************/
uDiveType = GetDriveType(szDrive);
switch(uDiveType)
{
case DRIVE_UNKNOWN://未知磁盤類型
printf("The drive type cannot be determined.");
break;
case DRIVE_NO_ROOT_DIR://說明參數是無效
printf("The root path is invalid,for example,no volume is mounted at the path.");
break;
case DRIVE_REMOVABLE://可移動磁盤
printf("The drive is a type that has removable media,for example,\
a floppy drive or removable hard disk.");
break;
case DRIVE_FIXED://固定磁盤
printf("The drive is a type that cannot be removed,for example,a fixed\
hard drive.");
break;
case DRIVE_REMOTE://網絡磁盤
printf("The drive is a remote (network) drive");
break;
case DRIVE_CDROM://光驅
printf("The drive is a CD-ROM drive.");
break;
case DRIVE_RAMDISK://RAM
printf("The drive is a RAM disk.");
break;
default:
break;
}
/***********************************
*GetVolumeInformation(szDrive,szDirverName,MAX_PATH,
&dwVolumeSerialNumber,&dwMaximumComponentLength,
&dwFileSystemFlags,szFileSystemNameBuffer,BUFSIZE)
* 功能:獲取磁盤驅動器的信息
* 參數:如下
* 返回值:非零即成功
***********************************/
if (!GetVolumeInformation(szDrive,
szDirverName,//卷標(字符串)
MAX_PATH,//szDirveName的長度
&dwVolumeSerialNumber,//用於裝載磁盤卷序列號的變量
&dwMaximumComponentLength,//用於裝載文件名每一部分的長度
&dwFileSystemFlags,//用於裝載一個或多個二進制位標志的變量
szFileSystemNameBuffer,//系統類型
BUFSIZE))
{
return FALSE;
}
if (0!=lstrlen(szDirverName))
{
printf("\nDrive Name is %s\n",szDirverName);
}
printf("\nVolume Serial Number is %u",dwVolumeSerialNumber);
printf("\nMaximun Component Length is %u",dwMaximumComponentLength);
printf("\nSystem Type is %s\n",szFileSystemNameBuffer);
if (dwFileSystemFlags & FILE_SUPPORTS_REPARSE_POINTS)
{
printf("The file system does not support volume mount points.\n");
}
if (dwFileSystemFlags & FILE_VOLUME_QUOTAS)
{
printf("The file system supports disk quotas.\n");
}
if (dwFileSystemFlags & FILE_CASE_SENSITIVE_SEARCH)
{
printf("The file system supports case-sensitive file names.\n");
}
printf("...\n");
return TRUE;
}