如何使用 DeviceIOControl() 从 CD 中获取“曲目信息”?

Posted

技术标签:

【中文标题】如何使用 DeviceIOControl() 从 CD 中获取“曲目信息”?【英文标题】:How to fetch "Track Information" from a CD with DeviceIOControl()? 【发布时间】:2014-02-23 01:15:34 【问题描述】:

我正在尝试将 DeviceIoControl() 与 SCSI_PASS_THROUGH_DIRECT 一起使用,以获取 TRACK_INFO。

此代码“有效”,但它通过将 ScsiStatus 设置为 2 来返回。 关于“跟踪”数据字段的大小似乎存在相互矛盾的文档。 mmc 文档说它有 1 位长,但是,在网上我发现它是 2 位长的参考资料。

然而,不管怎样,它都失败了。

我整天绞尽脑汁,一无所获。有人明白吗?

#define kSCSICmd_READ_TRACK_INFORMATION 0x52

// Read Track Information Format
struct CDTrackInfo

    UInt16 dataLength;
    UInt8  trackNumberLSB;
    UInt8  sessionNumberLSB;
    UInt8  reserved;
#ifdef __LITTLE_ENDIAN__
    UInt8  trackMode:4;
    UInt8  copy:1;
    UInt8  damage:1;
    UInt8  reserved3:2;

    UInt8  dataMode:4;
    UInt8  fixedPacket:1;
    UInt8  packet:1;
    UInt8  blank:1;
    UInt8  reservedTrack:1;

    UInt8  nextWritableAddressValid:1;
    UInt8  lastRecordedAddressValid:1;
    UInt8  reserved5:6;
#else /* !__LITTLE_ENDIAN__ */
    UInt8  reserved3:2;
    UInt8  damage:1;
    UInt8  copy:1;
    UInt8  trackMode:4;

    UInt8  reservedTrack:1;
    UInt8  blank:1;
    UInt8  packet:1;
    UInt8  fixedPacket:1;
    UInt8  dataMode:4;

    UInt8  reserved5:6;
    UInt8  lastRecordedAddressValid:1;
    UInt8  nextWritableAddressValid:1;
#endif /* !__LITTLE_ENDIAN__ */
    UInt32 trackStartAddress;
    UInt32 nextWritableAddress;
    UInt32 freeBlocks;
    UInt32 fixedPacketSize;
    UInt32 trackSize;
    UInt32 lastRecordedAddress;
    UInt8  trackNumberMSB;
    UInt8  sessionNumberMSB;
    UInt8  reserved6;
    UInt8  reserved7;
;
typedef struct CDTrackInfo CDTrackInfo;

/*
 from mmc r10a
 6.2.8 Read CD 
 table 140
 page 110
 */
typedef struct 
    UInt8       op_code;

    #if TARGET_RT_BIG_ENDIAN
        UInt8       reserved0       : 7;    //  <-- possibly 6?
        UInt8       track           : 1;    //  <-- possibly 2?
    #else
        UInt8       track           : 1;
        UInt8       reserved0       : 7;
    #endif

    UInt32      lba_or_track_number;
    UInt8       reserved1;
    UInt16      alloc_len;
    UInt8       control;
 SCSICommandDescriptorBlock_ReadTrackInfo;

OSStatus    CRawDiscReader::GetTrackInfo(int trackI, CDTrackInfo *trackInfoP)
   
    OSStatus            err = noErr;

    if (i_deviceH == NULL) 
        ERR(Open());
    

    if (!err) 
        SCSI_PASS_THROUGH_DIRECT        pass_thru =  0 ;
        SCSICommandDescriptorBlock_ReadTrackInfo&       cdb(*(SCSICommandDescriptorBlock_ReadTrackInfo *)&pass_thru.Cdb[0]);

        CF_ASSERT(sizeof(cdb) == kSCSICDBSize_10Byte);
        structclr(*trackInfoP);

        cdb.op_code                 = kSCSICmd_READ_TRACK_INFORMATION;
        cdb.track                   = true;
        cdb.lba_or_track_number     = trackI;
        cdb.alloc_len               = sizeof(CDTrackInfo);

        DWORD           numRead = 0;

        pass_thru.Length                    = sizeof(pass_thru);
        pass_thru.CdbLength                 = sizeof(cdb);
        pass_thru.DataIn                    = SCSI_IOCTL_DATA_IN;
        pass_thru.TimeOutValue              = kTimeOutSeconds;
        pass_thru.DataBuffer                = (PVOID)trackInfoP;
        pass_thru.DataTransferLength        = cdb.alloc_len;

        if (!DeviceIoControl(
            i_deviceH, 
            IOCTL_SCSI_PASS_THROUGH_DIRECT,
            (PVOID)&pass_thru,
            (DWORD)sizeof(pass_thru),
            (PVOID)&pass_thru,
            (DWORD)sizeof(pass_thru),
            &numRead, NULL)
        ) 
            err = GetLastError();
            LogErr("reading CD:", err);

         else if (numRead != pass_thru.Length) 
            err = GetLastError();
            LogErr("reading CD:", err);
            err = noErr;
        
    

    if (err && err != ERR_Already_Reported) 
        #if OPT_WINOS
            if (err == ERROR_INVALID_PARAMETER) 
                err = paramErr;
             else 
        #endif
        
            err = EIO;
        
    

    return err;

【问题讨论】:

也许我正在“艰难地”做这件事。我看到 msdev 网站上有一个“TRACK_INFORMATION2”结构,这是我想要的,但我找不到有关如何获取它的信息。 我意识到 lba_or_track_number 和 alloc_len 是大端的。我已经在我的代码中修复了这个问题,但无济于事:仍然无法正常工作 【参考方案1】:

答案似乎是“不要使用 IOCTL_SCSI_PASS_THROUGH_DIRECT”。

OSStatus    CRawDiscReader::GetTrackInfo(IDiscRecorder2Ex *discP, int trackI, CDTrackInfo *trackInfoP)

    OSStatus                            err = noErr;
    HRESULT                             resultL = 0;
    BYTE                                *dataP = NULL;
    ULONG_IMAPI2_TRACK_INFORMATION      outSize = 0;

    structclr(*trackInfoP);

    resultL = discP->GetTrackInformation(
        trackI, 
        IMAPI_READ_TRACK_ADDRESS_TYPE_TRACK, 
        &dataP, &outSize);

    XTE(TrapError("GetTrackInformation", resultL));

    if (!err) 
        CDTrackInfo     *returnInfoP = (CDTrackInfo *)dataP;

        *trackInfoP = *returnInfoP;
        CoTaskMemFree(dataP);
        dataP = NULL;

        SwapTrackInfo(trackInfoP);
    

    return err;

【讨论】:

以上是关于如何使用 DeviceIOControl() 从 CD 中获取“曲目信息”?的主要内容,如果未能解决你的问题,请参考以下文章

如何异步调用 DeviceIOControl 代码?

如何通过 C#.net 中的 DeviceIoControl 使用 IOCTL_SCSI_MINIPORT?

如何在 C# 中使用 DeviceIoControl 和 FSCTL_MAKE_MEDIA_COMPATIBLE 完成 Live File System (LiveUDF) 光盘?

DeviceIoControl 返回 ERROR_ACCESS_DENIED

使用 DeviceIoControl 都有哪些好的策略?

用于 DeviceIOControl 的 SetWindowsHookEx,要使用啥 hookid?