[C#

在.Net1.1上并没有DriveInfo这个类存在,此类是从Net2.0开始出现使用的,所以在.Net1.1上要得知目前电脑的硬盘有多少磁盘机代号,各个磁盘机的类型与是否有格式化过,这些状况都无法方便的处理…

那么到底我们要如何是好呢?


前言


在.Net1.1上并没有DriveInfo这个类存在,此类是从Net2.0开始出现使用的,所以在.Net1.1上要得知目前电脑的硬盘有多少磁盘机代号,各个磁盘机的类型与是否有格式化过,这些状况都无法方便的处理…

那么到底我们要如何是好呢?

这也是此篇的记录与用意,如果有朋友正好在使用.Net1.1也遇到此问题时,希望这篇文章解决你的困扰,那开始吧!

在.Net1.1上取得所有磁盘机


在.Net1.1上有一个方法叫做 GetLogicalDrives 是可以取得所有的磁盘机代号,但是是以字符串的方式显示:

//1.寻找电脑上的所有磁盘机,在System.IO下
string[] drivesStr = Directory.GetLogicalDrives();

但是此方法因为是字符串所以我们还是无法辨识,到底这个磁盘机是可移除式随身碟,还是CDROM,或是软驱,并且如果是一般的硬盘空间,那么他到底是已经格式化可以用了,或是尚未格式化?

检测磁盘代号字符串的资讯状况-使用Interop透过Win32 API 调用 GetDriveType Function


是的,在.Net1.1上有许多不存在的方法,于是乎除了自己写以外,而在这个硬盘的部分,还有WMI可以使用,但是因为我是在NT上执行测试,所以NT内建没有WMI这套服务,所以还要多多使用Win32 API去Interop一下,也真的要感谢至少Win32 API上想要的功能到目前为止,都有,而且可以Interop一下,不然很惨..

让我们翻阅一下Win32 API的GetDriveType Function ,里面有提到: Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive.

所以,我们便可以透过Interop使用kernel32.dll中的GetDriveType,如下:

using System.Runtime.InteropServices;

/// 
/// 寻找磁盘机的类型,透过c++ API取得
/// 
/// 磁盘机
/// 回传类型码
[DllImport( "kernel32.dll", EntryPoint="GetDriveTypeA" )]
private static extern int CPlusPlusGetDriveType(string nDrive);

这边稍为介绍一下,使用Interop的部分

[DllImport( "kernel32.dll", EntryPoint="GetDriveTypeA" )]  是一个对应到外部COM的方法,kernel32.dll是要使用的DLL文件,EntryPoint是要对应的进入方法名称,通常Win32 API文档会提供,包含了W与A分别表示Unicode 与 ANSI,是意味着,指定传送字符串参数至Win32 API时的格式,而一般C#与VB都是默认ASNI,所以是填A。

或是可以另外指定 CharSet = CharSet.Unicode ,而后在EntryPoint="GetDriveType" ,便会自动依据Unicode或是ANSI,对应至W或A,如下:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetDriveType")]

而有时候,如果你的extern方法名称与Win32 API的方法名称一致,便不需要另外指定EntryPoint,除非如这边的例子,我有改过对应到C#的名称,才需要,如下:

 [DllImport( "kernel32.dll" )]
private static extern int GetDriveType(string nDrive);

回过头来,所以会发现在看Win32 API的GetDriveType时,最下面有提到一行是:GetDriveTypeW (Unicode) and GetDriveTypeA (ANSI) ,而这边,因为我的对应C#方法名称改变,所以要写EntryPoint,并指定A。

再来,由文档上写,回传的int值分别代表了不同磁盘机类型,所以我另外用一个enum包起来;

public enum DriveType
{
        DRIVE_UNKNOWN =0,
        DRIVE_NO_ROOT_DIR,
        DRIVE_REMOVABLE,
        DRIVE_FIXED,
        DRIVE_REMOTE,
        DRIVE_CDROM
}

其中,如果是一般的本机磁盘,这里是指DRIVE_FIXED ,因此便可以得到哪几台是本机磁盘。

但是,有没有格式化,从这边是看不出来的,如果是未格式化的本机磁盘,仍然会回传 3,也就是DRIVE_FIXED ,所以下一部我们要处理这个问题,并显示仍有多少可用空间。


判断是否有格式化,与可用空间-使用Win32 API的GetDiskFreeSpaceEx function


再次翻阅Win32 API,发现了有GetDiskFreeSpaceEx function 可以使用,介绍如下:

Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space, the total amount of free space, and the total amount of free space available to the user that is associated with the calling thread

可以取得剩余可用空间,所有空间,所有可用空间等资讯。

调用方式如下:

/// 
/// 寻找此硬盘机的可用空间
/// 
/// 要寻找的印碟机,格式为 硬盘机代号:\
/// 回传仍有多少可用Bytes
/// 回传此硬盘机的所有空间Bytes
/// 
/// 
[DllImport("kernel32.dll",EntryPoint="GetDiskFreeSpaceExA" , SetLastError = true)]
public static extern bool CPlusPlusGetDiskFreeSpaceEx(string lpDirectoryName,
    out ulong lpFreeBytesAvailable,
    out ulong lpTotalNumberOfBytes,
    out ulong lpTotalNumberOfFreeBytes);

这边传入的第一个参数变式磁盘机的字符串代号,后面三个out参数,便是取得硬盘目前空间状态,而回传值表示,是否在调用过程中成功或错误,而这个错误很重要,我们需要拿取他的错误资讯,因为这关系到我们的硬盘是否有格式化的判断依据。

另外,还需要透过 SetLastError = true,取得错误后的资讯,将会以代码来显示。

现在来看一下调用使用CPlusPlusGetDiskFreeSpaceEx的方式:

if(type == DriveType.DRIVE_FIXED){
           ulong freeBytesAvail;
           ulong totalNumOfBytes;
           ulong totalNumOfFreeBytes;
           //判断有无数据空间,错误表示可能尚未格式化,不寻找
           if (!CPlusPlusGetDiskFreeSpaceEx(driveStr, out freeBytesAvail, out totalNumOfBytes, out totalNumOfFreeBytes))
           {
               int win32ErrorCode = Marshal.GetLastWin32Error();
               Console.Error.WriteLine("Error occurred code: {0} ",win32ErrorCode.ToString());
               if(win32ErrorCode == 1005)
                    Console.Error.WriteLine("Error Meesage: ERROR_UNRECOGNIZED_VOLUME");
           }

}

上面的程序,在执行时,如果本机磁盘正处于未格式化,便会进到判断内,而此时由于我们有设定 SetLastError = true ,便能透过Interop的Marshal.GetLastWin32Error() 取得错误码。

在来从文档上比对错误码的消息,这里会看到,如果是1005即表示 ERROR_UNRECOGNIZED_VOLUME ,在System Erro Codes中的错误,以此方式去判断因为尚未格式化,所以无法对Volume有认知(说心里话,这也是我目前唯一可以辨认的方法..如果有更好的解法,欢迎跟我分享@@)。

最后附上一个判断的结图:

DriveInfo in Net1.1

最后,此方法已经在Windows 2000与NT上使用Work ok.

补充-Win32 API GetDiskFreeSpaceEx 与GetDiskFreeSpace function的差异


此部分在官方文章中有提到解说,主要是早期的操作系统95/98才会有影响,98后的系统都是可以使用GetDiskFreeSpaceEx ,如下是官方资讯:

一般而言,Win32 程序应该使用 GetDiskFreeSpaceEx 来决定一个磁盘区及调用端可以使用的可用空间数量的总大小。这不可能的其中一种情况时,程序会在零售版 Windows 95 (组建 950.6) 上执行,因为 GetDiskFreeSpaceEx 以后开始引进以 OEM 服务版本 2 (OSR2) 中的 Windows 95。


如果您的程序可以在零售版 Windows 95 上执行,您不应该直接调用 GetDiskFreeSpaceEx 因为零售版 Windows 95 中不会实践这个函数,并直接调用将会防止您的程序载入。相反地,您应该以动态方式连结至它透过 GetProcAddress。如果返回的指针为非 NULL,然后您的应用程序可以安全地调用 GetDiskFreeSpaceEx 透过指针 ;如果返回的指针为 NULL,您应该还原成 GetDiskFreeSpace。在本文稍后的程序范例示范如何执行这项操作。

参考数据

[C#]使用 DriveInfo 类取得磁盘资讯

Directory.GetLogicalDrives Method

GetDriveType Function

DllImportAttribute.EntryPoint 字段

DllImportAttribute.CharSet 字段

GetDiskFreeSpaceEx function

System Error Codes

http://www.java2s.com/Tutorial/CSharp/0520__Windows/Getfreediskspace.htm

了解,并使用 GetDiskFreeSpace 和 GetDiskFreeSpaceEx


文章中的叙述如有观念不正确错误的部分,欢迎告知指正 谢谢 =)

另外要转载请附上出处 感谢