首页 > 解决方案 > 如何通过c语言获取内部SATA智能状态

问题描述

我正在尝试在 Windows 上获取内部 sata 驱动器的温度,到目前为止,我已成功获取驱动器 ID,它的所有信息,如存储容量、序列号。,型号等,但无法获得其温度详细信息,尽管我的 sata 驱动器通过工具(水晶信息)提供温度。我也通过文件来解决这个问题,但没有得到太多。

这是将命令发送到磁盘的示例代码

typedef struct _SCSI_PASS_THROUGH {
    USHORT Length;
    UCHAR ScsiStatus;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR CdbLength;
    UCHAR SenseInfoLength;
    UCHAR DataIn;
    ULONG DataTransferLength;
    ULONG TimeOutValue;
    ULONG_PTR DataBufferOffset;
    ULONG SenseInfoOffset;
    UCHAR Cdb[16];
}SCSI_PASS_THROUGH;

#define SCSI_IOCTL_DATA_IN           1
#define READ_ATTRIBUTE_BUFFER_SIZE  512
#define READ_ATTRIBUTES         0xD0
void smartStatus()
{
    BOOL    bRet;
    DWORD   dwReturned;
    DWORD length;

    typedef struct _SMART_ATTRIBUTE
    {
        BYTE    Id;
        WORD    StatusFlags;
        BYTE    CurrentValue;
        BYTE    WorstValue;
        BYTE    RawValue[6];
        BYTE    Reserved;
    } SMART_ATTRIBUTE;
    typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS {
        SCSI_PASS_THROUGH Spt;
        ULONG             Filler;      // realign buffers to double word boundary
        UCHAR             SenseBuf[32];
        UCHAR             DataBuf[4096];
    } SCSI_PASS_THROUGH_WITH_BUFFERS;
    
    SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;

    ZeroMemory(&sptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));

    sptwb.Spt.Length = sizeof(SCSI_PASS_THROUGH);
    sptwb.Spt.PathId = 0;
    sptwb.Spt.TargetId = 0;
    sptwb.Spt.Lun = 0;
    sptwb.Spt.SenseInfoLength = 24;
    sptwb.Spt.DataIn = SCSI_IOCTL_DATA_IN;
    sptwb.Spt.DataTransferLength = READ_ATTRIBUTE_BUFFER_SIZE;
    sptwb.Spt.TimeOutValue = 2;
    sptwb.Spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf);
    sptwb.Spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseBuf);
    sptwb.Spt.CdbLength = 12;
    sptwb.Spt.Cdb[0] = 0xA1;//ATA PASS THROUGH(12) OPERATION CODE(A1h)
    sptwb.Spt.Cdb[1] = (4 << 1) | 0; //MULTIPLE_COUNT=0,PROTOCOL=4(PIO Data-In),Reserved
    sptwb.Spt.Cdb[2] = (1 << 3) | (1 << 2) | 2;//OFF_LINE=0,CK_COND=0,Reserved=0,T_DIR=1(ToDevice),BYTE_BLOCK=1,T_LENGTH=2
    sptwb.Spt.Cdb[3] = READ_ATTRIBUTES;//FEATURES (7:0)
    sptwb.Spt.Cdb[4] = 1;//SECTOR_COUNT (7:0)
    sptwb.Spt.Cdb[5] = 1;//LBA_LOW (7:0)
    sptwb.Spt.Cdb[6] = 0x4F;//LBA_MID (7:0)
    sptwb.Spt.Cdb[7] = 0xc2;//LBA_HIGH (7:0)
    sptwb.Spt.Cdb[8] = 0xA0;
    sptwb.Spt.Cdb[9] = 0xb0;//COMMAND

    length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf) + sptwb.Spt.DataTransferLength;
    bRet = ::DeviceIoControl(hDev, IOCTL_SCSI_PASS_THROUGH,
        &sptwb, sizeof(SCSI_PASS_THROUGH),
        &sptwb, length, &dwReturned, NULL);
    DWORD err = GetLastError();
    log(logDEBUG1) << "Error smartStatus" << " Error " << err;

    DWORD count = 0;
    for (int i = 0; i < 512; i++)
    {
        count += sptwb.DataBuf[i];
    }
    if (count == 0)
    {
        return;
    }

    int life = -1;
    BYTE SmartReadData[512] = {0};
    SMART_ATTRIBUTE Attribute;

    memcpy(SmartReadData,sptwb.DataBuf, 512);
    //HexDump(SmartReadData, 512);

    int j = 0;
    for (int i = 0; i < 30; i++)
    {
        //DWORD rawValue = 0;
        memcpy(&(Attribute),
            &SmartReadData[i * sizeof(SMART_ATTRIBUTE) + 2], sizeof(SMART_ATTRIBUTE));
        if (i == 0)
        {
            if (Attribute.CurrentValue <= 100)
            {
                //log(logINFO) << "before case ";
                life = Attribute.CurrentValue;
                //log(logINFO) << "life " << life;

            }
        }
        if (Attribute.Id != 0)
        {
            switch (Attribute.Id)
            {
            case 0xC2: // Temperature
                if ((Attribute.RawValue[1] != 0 || Attribute.RawValue[0] > 70))
                {
                    disk_info.temperature = ((Attribute.RawValue[1] & 0xff) | ((Attribute.RawValue[0] & 0xff)<<8));
                }
                else if (Attribute.RawValue[0] > 0)
                {
                    disk_info.temperature = (Attribute.RawValue[0] * 1);
                }
                if (disk_info.temperature >= 100)
                {
                    disk_info.temperature = 0;
                }
                break;
            default:
                break;
            }
            j++;
        }
    }

    //disk_info.temperature = SmartReadData[0x2] * 256 + SmartReadData[0x1] - 273;
    //if (disk_info.temperature == -273)
    //{
    //  disk_info.temperature = -1000;
    //}

    //life = 100 - SmartReadData[0x05];
    if (life < 0)
    {
        life = 0;
    }


    if (life > 10)
    {
        disk_info.health = DISK_STATUS_GOOD;
    }
    else if (life == 10)
    {
        disk_info.health = DISK_STATUS_CAUTION;
    }
    else if (life < 10)
    {
        disk_info.health = DISK_STATUS_BAD;
    }
    else
    {
        disk_info.health = DISK_STATUS_UNKNOWN;
    }
    //_aligned_free(identifyResp);

    return;

在我的情况下,“DeviceIoControl”函数之后的计数始终为零,这意味着缓冲区未填充。disk_info 只是收集与驱动器相关的所有详细信息的结构。看到日志后,我发现“DeviceIoControl”返回错误代码 1117。请帮我解决这个问题

标签: cwindowsinternalstemperaturesata

解决方案


推荐阅读