在DOS 7.x中获取大型驱动器结构信息

6
我写了一个目录信息实用程序,并且(因为我和我为之编写的人收集和使用老式硬件),它兼容DOS和Windows 9x以及Windows XP/Vista/7/8 64位(因为我们也使用这些操作系统)。我遇到的问题是在Windows 9x和FAT32驱动器上。只要Windows 9x实际上已经加载,我就设法让它工作,但是如果我仅在命令提示符下启动或重新启动到MS-DOS模式,我就无法访问允许我获取大型驱动器数据的Windows API,并且它会默认回到我拥有的DOS例程。这些例程受限于2GB限制。检查DOS 7.x程序(主要是chkdsk)如何处理此问题(因为他们没有报告正确的驱动器大小问题),似乎他们使用DOS中断(主要是INT 21h)来解决此问题。想着没问题,我将进行快速版本检查,如果是DOS 7或更高版本,我将运行一个快速的汇编路由来获取驱动器结构并计算总空间和可用空间。只是,这个例程(虽然没有返回错误),却没有用任何东西填充我的缓冲区。
以下是代码:
#include <stdio.h>
#include <dos.h>

void main(void) {
    unsigned short hes,hdi,sectors,bytes;
    unsigned long tclusters,fclusters;
    unsigned char far *drivedata;
    char test = '\0';
    char display[17] = "0123456789ABCDEF";
    int count;

    drivedata = new unsigned char [63];

    for (count = 0; count < 63; count++) drivedata[count] = '\0';

    drivedata[0] = '\x3d';
    drivedata[1] = '\x00';

    hes = FP_SEG(drivedata);
    hdi = FP_OFF(drivedata);

asm {
        push ax
        push es
        push di
        push ds
        push dx
        push cx
        mov ax,0x440d
        mov bx,0x0003
        mov cx,0x484a
        int 21h
        jnc _GOOD
        mov ax,0x7302
        mov es,[hes]
        mov di,[hdi]
        mov dx,0x0003
        mov cx,0x003f
        int 21h
        jnc _GOOD
    }
    test = '\1';
_GOOD:
    asm {
        mov ax,0x440d
        mov bl,0x03
        mov cx,0x486a
        int 21h
        pop cx
        pop dx
        pop ds
        pop di
        pop es
        pop ax
    }

    if (test == '\1') {
        printf("There was an error.\r\n");
        return;
    }



    tclusters = (unsigned long) drivedata[48];
    tclusters = (tclusters * 256) + (unsigned long)drivedata[47];
    tclusters = (tclusters * 256) + (unsigned long)drivedata[46];
    tclusters = (tclusters * 256) + (unsigned long)drivedata[45];
    ++tclusters;

    fclusters = (unsigned long)drivedata[36];
    fclusters = (fclusters * 256) + (unsigned long)drivedata[35];
    fclusters = (fclusters * 256) + (unsigned long)drivedata[34];
    fclusters = (fclusters * 257) + (unsigned long)drivedata[33];

    bytes = (unsigned int)drivedata[5];
    bytes = (bytes * 256) + (unsigned int)drivedata[4];

    sectors = (unsigned long)drivedata[6];
    ++sectors;

    printf("Drive C has:\r\n");
    printf("   Total Clusters: %u\r\n",tclusters);
    printf("    Free Clusters: %u\r\n",fclusters);
    printf("          Sectors: %u\r\n",sectors);
    printf("            Bytes: %u\r\n",bytes);

    printf("\r\n");
    printf("   |  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r\n");
    printf("---------------------------------------------------------------------");
    for (count = 0; count < 63; count++) {
        if ((count % 16) == 0) printf("\r\n %c | ",display[(count / 16)]);
        printf("%03u ",drivedata[count]);
    }
    printf("\r\n");

    return;
}

最后一个步骤是我试图找出问题所在。我得到了奇怪的结果,无法找出规律。最初,我不担心清除缓冲区,因为INT调用应该用它自己的值填充它(除了前两个字节,应该用EDB数据缓冲区大小填充)。在得到如此多的随机结果之后,我在开头添加了一个循环来用零填充缓冲区,然后加入缓冲区大小。结果从那时起停止了随机变化,它们始终都是零,这意味着INT调用未填充缓冲区。通过各种测试,我已经确认hes和hdi正确地被赋予了缓冲区地址的段和偏移量。我还尝试将es和di设置为指针地址而不是缓冲区地址。我认为这不会起作用,因为我读到的所有内容都说要将其设置为地址而不是指针,但我尝试了我能想到的一切。在所有情况下,缓冲区都没有被填充。

正如你可能已经注意到的,这只是我编写的一个测试程序,以便在将其添加到我的主要程序中之前找出确切的过程(我的主要程序工作得非常好,除了这个问题)。FP_行只是可以表示为(unsigned long)(x&0xffff0000)> 16的段和(unsigned long)(x&0x0000ffff)的宏,用于偏移量。通常,您会传递指针(&drivedata),但drivedata已经是指针。

实际输出:

Drive C has:
   Total Clusters: 1
    Free Clusters: 0
          Sectors: 1
            Bytes: 0

   |  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
---------------------------------------------------------------------
 0 | 061 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
 1 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
 2 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 
 3 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 

那么,我错过了什么吗?就像chkdsk一样,在调用之前我会锁定驱动器,在调用之后解锁它(尽管我不确定是否有必要)。我该如何使它正常工作?或者,有没有比使用INT 21h获取驱动器结构(簇,每簇扇区数,每扇区字节数)更好的方法?在搜索中找到的所有内容都只指向Windows API函数,而如果用户进行引导到命令提示符等操作,则无法访问这些函数...


1
我是否误读了你的代码?看起来如果你成功锁定驱动器,你会跳过读取数据的代码。如果失败,也许可以尝试以下方法:1)将SI设置为0xF1A6。2)将dl设置为0x80。3)使用更大的缓冲区(256?)。 - David Wohlferd
1
实际上你不是:/ 我错过了那个....正在重新编译...马上回来。 - user3399848
1
叹气 是的,我在成功时跳过了我的代码,而不是在失败时跳过。将jnc操作码更改为jc,并添加一个新标签(以适当地更改好/坏标志)。我的错。现在我正在获取数据,尽管数字似乎有些不对。现在要进行更多测试 :) 谢谢 - user3399848
1
好的,又是我犯的一个愚蠢错误。在printf命令中使用了%u指定符,但没有添加'l'表示长整型:( 啊...问题解决了,对此给您带来的麻烦感到抱歉。我宁愿不使用7303例程,因为据报道它与CD-ROM存在问题。我得去研究一下这个问题。真不想最终不得不再写一个例程:( - user3399848
2个回答

1

哇,使用DOS系统,那可真是老派!虽然没有使用打孔卡那么古老,但也相当古老...

显然,FreeDOS 支持 FAT 32 文件系统。你可以尝试在那些甚至没有安装 Windows 95 的机器上安装它。


3
制作老式系统的人通常会坚持使用当时使用的操作系统。我的小程序可以在DOS 3+上运行(实际上我只安装过和测试到这个版本,但可能向前兼容更早的版本)。它也可以在现代系统上运行(例如我的Windows 7 64位)。唯一的问题是DOS 7(Windows 9x引导到命令提示符时)。现在我正在努力让它在DOS 7上运行。顺便说一句,当他们发明键盘时,我真的很高兴,打孔卡片真的很麻烦。 - user3399848
哇,你绝对应该在 CP/M 上检查这个! - zmbq

1

对于您的复古爱好,您应该配备LBAFAT32规范,Wikipedia: File Allocation Table似乎有很好的链接。

您可能会发现的一件事是,那些传统系统(以及为它们编写的软件)无法优雅地处理大型磁盘(磁盘大小> 2 ^ (32-1))。

我认为其他材料也非常重要:

在所有情况下应该都可以使用BIOS调用来查找基本信息,然后在自己的代码中复制计算大小等算法。在旧的DOS时代,非Microsoft程序没有易于重用的API可用。需要执行高级操作的程序必须自己知道如何进行裸机编程。


1
我现在正在使用BIOS调用,这就是我遇到问题的地方:( 我已经阅读了关于LBA和FAT32的相关内容。当我第一次编写这个小工具时,我已经做了所有这些。正如你在上面看到的,结果证明我的问题都是我自己造成的。很抱歉,我不是专业程序员,只是一个业余爱好者。现在我已经基本解决了问题。还有一个小错误要追踪(在实际的实用程序代码中),以及一个针对CD-ROM的解决方法,我就可以完成了。 - user3399848

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接