Bogdaniv
Bogdaniv

Reputation: 23

Read HDD SMART attributes using DeviceIoControl

I run my program that gets HDD SMART attributes as admin, but get this error: Failed to get S.M.A.R.T. data. Error code: 5 Error message: Access is denied.

Why access is denied?How to fix it?Program seems to be correct,if not,please help to correct it.

void GetSMART()
{
    // Открытие физического диска
    int driveNumber = 0; // № диска
    wstring drivePath = L"\\\\.\\PhysicalDrive" + std::to_wstring(driveNumber);
    HANDLE hDrive = CreateFile(drivePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
    
    if (hDrive == INVALID_HANDLE_VALUE) {
        return;
    }
    // Получение S.M.A.R.T.
    bool isGetData;
    DWORD bytesReturned;
 
    const int inParamsSize = sizeof(SENDCMDINPARAMS) - 1;
    SENDCMDINPARAMS inParams = { 0 };
    inParams.cBufferSize = READ_ATTRIBUTE_BUFFER_SIZE;                                    
    inParams.irDriveRegs.bFeaturesReg = READ_ATTRIBUTES;                                  
    inParams.irDriveRegs.bSectorCountReg = 1;                                             
    inParams.irDriveRegs.bSectorNumberReg = 1;                                            
    inParams.irDriveRegs.bCylLowReg = SMART_CYL_LOW;                                       
    inParams.irDriveRegs.bCylHighReg = SMART_CYL_HI;                                    
    inParams.irDriveRegs.bDriveHeadReg = 0xA0 | ((static_cast<BYTE>(driveNumber) & 1) << 4);     
    inParams.irDriveRegs.bCommandReg = SMART_CMD;                                        
    const int outParamsSize = sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE;
    SENDCMDOUTPARAMS outParamsAttributes[outParamsSize] = { 0 };
    isGetData = DeviceIoControl(hDrive, SMART_RCV_DRIVE_DATA,
        &inParams, inParamsSize, &outParamsAttributes, outParamsSize, &bytesReturned, NULL);
    if (!isGetData) {
        DWORD errorCode = GetLastError();
        LPSTR errorMessage = nullptr;
        DWORD result = FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            errorCode,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            reinterpret_cast<LPSTR>(&errorMessage),
            0,
            nullptr
        );
        if (result != 0) {
            std::cout << "Failed to get S.M.A.R.T. data. Error code: " << errorCode << std::endl;
            std::cout << "Error message: " << errorMessage << std::endl;
        }
        else {
            std::cout << "Failed to retrieve error message." << std::endl;
        }
        LocalFree(errorMessage);
        CloseHandle(hDrive);
        return;
    }
}

Upvotes: 1

Views: 425

Answers (1)

ForumsReg
ForumsReg

Reputation: 51

Why access is denied?

Because the I/O control code SMART_RCV_DRIVE_DATA is defined in the winioctl.h header file as

#define SMART_RCV_DRIVE_DATA            CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

Note that RequiredAccess parameter in the CTL_CODE macro is FILE_READ_ACCESS | FILE_WRITE_ACCESS.

How to fix it?

In order to fix the problem, you should call CreateFile() function specifying dwDesiredAccess as FILE_READ_DATA | FILE_WRITE_DATA, not just GENERIC_READ. Like that:

HANDLE hDrive = CreateFile(drivePath.c_str(), FILE_READ_DATA | FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);

GENERIC_READ | GENERIC_WRITE also will work:

HANDLE hDrive = CreateFile(drivePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);

Running it as Administrator is unnecessary.

Upvotes: 0

Related Questions