1. 程式人生 > >windows的磁碟操作之七——獲取當前所有的物理磁碟號

windows的磁碟操作之七——獲取當前所有的物理磁碟號

有了前幾節的基礎後,本節給出一個更復雜但卻非常實用的例子。

很多情況下,我們想知道當前系統下安裝了多少塊磁碟,他們的物理驅動器號都是多少,每一塊磁碟上有多少個分割槽,分割槽號怎麼分佈,每個分割槽大小是多少。這就類似於我們開啟windows的磁碟管理看到的那種非常清晰的列表。對於後幾個問題,我們根據物理驅動器號呼叫第五節http://cutebunny.blog.51cto.com/301216/624567中的GetPartitionLetterFromPhysicalDrive函式,以及第三節http://cutebunny.blog.51cto.com/301216/624079中的GetDiskDriveLayout函式即可搞定。那麼我們這一節的重點放在如何獲得當前所有物理驅動器號上。

先引入一個新的概念,裝置GUID,它是同類裝置統一併且唯一的標識碼。對於磁碟,GUID為GUID_DEVINTERFACE_DISK,具體值為{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}。windows提供一組API,可以通過GUID枚舉出所有該型別的裝置。先給出幾個相關API的簡要介紹

HDEVINFO

SetupDiGetClassDevs(

IN LPGUID  ClassGuid,  OPTIONAL

IN PCTSTR  Enumerator,  OPTIONAL

IN HWND  hwndParent,  OPTIONAL

IN DWORD  Flags

);

其中,ClassGuid填入我們感興趣的裝置GUID,該函式返回滿足查詢條件的一組裝置的資訊集合的控制代碼,該控制代碼就是獲取裝置資訊的關鍵鑰匙。

WINSETUPAPI BOOL WINAPI

SetupDiEnumDeviceInterfaces(

IN HDEVINFO  DeviceInfoSet,

IN PSP_DEVINFO_DATA  DeviceInfoData,  OPTIONAL

IN LPGUID  InterfaceClassGuid,

IN DWORD  MemberIndex,

OUT PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData

);

該函式列舉SetupDiGetClassDevs獲得的控制代碼中包含的所有裝置。引數DeviceInfoSet填入我們上一步中獲得的控制代碼,InterfaceClassGuid仍舊是我們感興趣的GUID,MemberIndex為裝置在集合中的索引,從0開始計數,最後DeviceInterfaceData是輸出引數,儲存枚舉出的裝置介面,後續可通過此介面獲得詳細的裝置資訊。

注意,引數DeviceInterfaceData.cbSize在呼叫前必須初始化為sizeof(SP_DEVICE_INTERFACE_DATA),這是函式的強制要求。

WINSETUPAPI BOOL WINAPI

SetupDiGetDeviceInterfaceDetail(

IN HDEVINFO  DeviceInfoSet,

IN PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData,

OUT PSP_DEVICE_INTERFACE_DETAIL_DATA  DeviceInterfaceDetailData,  OPTIONAL

IN DWORD  DeviceInterfaceDetailDataSize,

OUT PDWORD  RequiredSize,  OPTIONAL

OUT PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL

);

該函式根據上兩步中的控制代碼和介面獲取裝置的詳細資訊資料。引數DeviceInfoSet和DeviceInterfaceData在上兩步中獲得。輸出引數DeviceInterfaceDetailData儲存著裝置資訊資料,這個結構體中的成員DevicePath就是我們辛辛苦苦找尋的東西了。用它可以作為裝置名呼叫CreateFile函式開啟裝置,之後的操作,嘿嘿,你懂的…

下面是具體程式碼

/******************************************************************************

* Function: get device path from GUID

* input: lpGuid, GUID pointer

* output: pszDevicePath, device paths

* return: Succeed, the amount of found device paths

*         Fail, -1

******************************************************************************/

DWORD GetDevicePath(LPGUID lpGuid, CHAR **pszDevicePath)

{

    HDEVINFO hDevInfoSet;

    SP_DEVICE_INTERFACE_DATA ifdata;

    PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;

    DWORD nCount;

    BOOL result;

    //get a handle to a device information set

    hDevInfoSet = SetupDiGetClassDevs(

                    lpGuid,      // class GUID

                    NULL,        // Enumerator

                    NULL,        // hwndParent

                    DIGCF_PRESENT | DIGCF_DEVICEINTERFACE    // present devices

                    );

    //fail...

    if (hDevInfoSet == INVALID_HANDLE_VALUE)

    {

        fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());

        return (DWORD)-1;

    }

    pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(INTERFACE_DETAIL_SIZE);

    if (pDetail == NULL)

    {

        return (DWORD)-1;

    }

    pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

    nCount = 0;

    result = TRUE;

    // device index = 0, 1, 2... test the device interface one by one

    while (result)

    {

        ifdata.cbSize = sizeof(ifdata);

        //enumerates the device interfaces that are contained in a device information set

        result = SetupDiEnumDeviceInterfaces(

                    hDevInfoSet,     // DeviceInfoSet

                    NULL,            // DeviceInfoData

                    lpGuid,          // GUID

                    nCount,   // MemberIndex

                    &ifdata        // DeviceInterfaceData

                    );

        if (result)

        {

            // get details about a device interface

            result = SetupDiGetDeviceInterfaceDetail(

                        hDevInfoSet,    // DeviceInfoSet

                        &ifdata,        // DeviceInterfaceData

                        pDetail,        // DeviceInterfaceDetailData

                        INTERFACE_DETAIL_SIZE,    // DeviceInterfaceDetailDataSize

                        NULL,           // RequiredSize

                        NULL          // DeviceInfoData

                        );

            if (result)

            {

                // copy the path to output buffer

                strcpy(pszDevicePath[nCount], pDetail->DevicePath);

                //printf("%s\n", pDetail->DevicePath);

                nCount++;

            }

        }

    }

    free(pDetail);

    (void)SetupDiDestroyDeviceInfoList(hDevInfoSet);

    return nCount;

}

執行完畢後,所有滿足條件的磁碟裝置名稱都儲存在字串陣列pszDevicePath中。有了這個關鍵的陣列,後面就可以為所欲為了。

以下是獲得所有物理磁碟號的完整程式碼

/******************************************************************************

* Function: get all present disks' physical number

* input: N/A

* output: ppDisks, array of disks' physical number

* return: Succeed, the amount of present disks

*         Fail, -1

******************************************************************************/

DWORD GetAllPresentDisks(DWORD **ppDisks)

{

    CHAR *szDevicePath[MAX_DEVICE];        // device path

    DWORD nDevice;

    HANDLE hDevice;

    STORAGE_DEVICE_NUMBER number;

    BOOL result;

    DWORD readed;

    WORD i, j;

    for (i = 0; i < MAX_DEVICE; i++)

    {

        szDevicePath[i] = (CHAR *)malloc(INTERFACE_DETAIL_SIZE);

        if (NULL == szDevicePath[i])

        {

            for (j = 0; j < i; j++)

            {

                free(szDevicePath[i]);

            }

            return (DWORD)-1;

        }

    }

    // get the device paths

    nDevice = GetDevicePath(const_cast<LPGUID>(&GUID_DEVINTERFACE_DISK), szDevicePath);

    if ((DWORD)-1 == nDevice)

    {

        for (i = 0; i < MAX_DEVICE; i++)

        {

            free(szDevicePath[i]);

        }

        return (DWORD)-1;

    }

    *ppDisks = (DWORD *)malloc(sizeof(DWORD) * nDevice);

    // get the disk's physical number one by one

    for (i = 0; i < nDevice; i++)

    {

        hDevice = CreateFile(

                    szDevicePath[i], // drive to open

                    GENERIC_READ | GENERIC_WRITE,     // access to the drive

                    FILE_SHARE_READ | FILE_SHARE_WRITE, //share mode

                    NULL,             // default security attributes

                    OPEN_EXISTING,    // disposition

                    0,                // file attributes

                    NULL            // do not copy file attribute

                    );

        if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive

        {

            for (j = 0; j < MAX_DEVICE; j++)

            {

                free(szDevicePath[j]);

            }

            free(*ppDisks);

            fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());

            return DWORD(-1);

        }

        result = DeviceIoControl(

                    hDevice,                // handle to device

                    IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode

                    NULL,                            // lpInBuffer

                    0,                               // nInBufferSize

                    &number,           // output buffer

                    sizeof(number),         // size of output buffer

                    &readed,       // number of bytes returned

                    NULL      // OVERLAPPED structure

                    );

        if (!result) // fail

        {

            fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());

            for (j = 0; j < MAX_DEVICE; j++)

            {

                free(szDevicePath[j]);

            }

            free(*ppDisks);

            (void)CloseHandle(hDevice);

            return (DWORD)-1;

        }

        *(*ppDisks + i) = number.DeviceNumber;

        (void)CloseHandle(hDevice);

    }

    for (i = 0; i < MAX_DEVICE; i++)

    {

        free(szDevicePath[i]);

    }

    return nDevice;

}

程式碼說明:

1. 呼叫函式GetDevicePath獲得前面所說的磁碟裝置名稱陣列。

2. 對每一個磁碟裝置,呼叫CreateFile開啟並獲得裝置控制代碼。

3. 呼叫操作碼為IOCTL_STORAGE_GET_DEVICE_NUMBER的DeviceIoControl函式獲得磁碟物理驅動器號。

4. 將所有物理磁碟號存入陣列返回。

大功告成了。可能有朋友會問,GetDevicePath不是已經獲得了磁碟路徑麼,你前面說過,這個路徑不是\\.\PhysicalDriveX就是\\.\X: ,那我們解析一下這個字串不就可以獲得磁碟號或者碟符了麼。很可惜,這裡的磁碟路徑出現了第三種形式,而且是毫無章法的形式。開啟函式GetDevicePath中的註釋行//printf("%s\n", pDetail->DevicePath);將這種形式的路徑打印出來,可以看到類似為

\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&245a6b6d&0&0.0.0#

{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&37141c12&0&0.1.0#

{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

所以,沒辦法,我們還是得用DeviceIoControl找出磁碟號。