从程序员的角度看NTFS 2000:流和硬连接【1】

modico
介绍
自从1994年以来,一个完全面向对象版本的Microsoft Windows NT的神话一直在流传。Cairo,这个传说中的操作系统的代码的名字,从来没有在Redmond以外的一个实验室实现过。从Cairo开始到现在,它的一些基本思想就常常被介绍。
在Cairo背后的基本思想是:把文件和文件夹看成是对象和对象的集合。文件夹的内容不必受到下层的文件系统存储机制的束缚,你可以把它们作为独立的实体来访问和复制。文件和文件夹对象可以按照方法和属性来说明一个可编程的API,不论是标准的还是由所有者或作者定义的。
然而,我们今天拥有的文件系统是把文件和文件夹注册在一些内部结构中的,当文件和文件夹在磁盘间移动时,这些内部结构同时被复制。文件和文件夹有一套固定的特性, 但是这些特性对于现代的应用程序来说太少了。作为部分工作区,我们在过去的几年时间里提供了一些技术来给文件和文件夹增加额外的信息。外壳和命名空间的扩展,桌面初始化文件,文件系统对象以及自动对象模块外壳就是几个例子。然而,所有这些功能只是部分的解决办法。 完全没达到一个重新设计的有组织的Windows文件系统的要点。因为向后兼容性是一个严重的问题,从Microsoft的MS-DOS 2。0版开始到现在。Windows还在使用一个建立在文件分配表(FAT)上的过时的文件系统,即使有一些最近的改进,例如支持高容量的硬盘,FAT仍然是一种相当不完善的存储文件和文件夹信息的方法。
真实世界的几年经验说明我们所受到的最重要的限制与程序员所需要的适当的管理和识别文件的附加信息相关。最近,我被要求尝试得到一个Word文件的真正的创建时的日期。你可能认为这是一个简单的任务,因为创建的日期是一种可以通过一些API函数轻易找到的属性。但这并不完全是正确的。尝试拷贝相同的Word文件在不同的机器上,甚至是在相同的文件夹中,然后比较不同拷贝的创建日期。你会惊奇的发现,它们是不同的。当你拷贝一个文件时,你创建了一个新文件,它的时间戳为创建发生的时间。当你建立一个拷贝的时候,你丢失了那些关于文件最初创建时间的有潜在价值的信息。
幸运的是,一个Word文档在内部的 SummaryInformation 处保留了这些信息。所以在这种情况下,我可以解决这个问题,并成功的满足客户的要求。要是一个Access或者是文本文件的话,我的努力就白费了。
在Windows NT系统中,Microsoft介绍了一个新的文件系统叫做NTFS。其中最显著的特性就是B树结构,它可以加速在一个大的文件夹中对一个文件的恢复,以文件为基础的安全性,日志记录,提高文件系统可恢复性,和比FAT或者FAT32更好的对磁盘空间的利用。(顺便说一下,Windows 2000提供了对FAT卷的完全的支持和访问。)
由于它来源于Windows 3.1,NTFS卷也有另一个经常被低估的特性:它支持一个文件中的多个数据流。在Windows 2000中,流的支持得到了提高, 并且加入了其他一些更便利的特性帮助你进行天衣无缝的文件操作。 让我们看一下来源于Windows 2000 的NTFS的版本,NTFS 2000的主要特性。

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
NTFS 2000的概貌
如果多流数据不是NTFS 2000卷文件唯一的特性,那还有其他一些特性需要Windows 2000。它们是:

  • 文件和目录的加密
  • 每个用户,每卷的磁盘配额
  • 重新分析点和分层存储管理
  • 装配点
  • 硬连接
  • 改变日志

在Windows 2000的安装中,你被要求指定是否把Windows 2000卷转变成NTFS 2000。然而,使用NTFS 2000文件系统,仅仅要求机器作为域控制器。你可以随时将FAT分区转变成NTFS,使用命令行工具convert.exe:
CONVERT volume /FS:NTFS [/V]
卷参数用一个跟着冒号的驱动器字母。它也可以是一个装载点,或者是一个卷名。/FS:NTFS选项指定该卷必须转变成NTFS。最后,如果你希望工具在详细的模式下运行,就使用/V结尾。当你运行convert.exe时,它要进行一些初始化,然后要求你重新启动。转化会在下次启动时立即发生。
除了上述已经列举的特性以外,Windows 2000总的文件夹管理的一个值得注意的方面就是对桌面初始化文件完全的和部分的扩展支持。在这篇文章余下的部分,我将着重于流和硬连接。表1,总结了一些其它的重要的NTFS 2000的关键特性。
表1. NTFS 2000的关键特性



特性描述
加密文件系统
在NTFS 2000中管理者可以选择性的加密文件和文件夹。加密对使用这是透明的。Windows API的核心知道指定文件的加密属性。为了保护它,不要使用自己的函数去移动/拷贝文件,而是依赖系统的CopyFile() 或MoveFile()。
磁盘配额
你可以根据磁盘配额给每个用户分配最大的磁盘空间,而且当他或她超过了一个中间的警告水平时得到通报。配额对使用者是透明的,它们可以简单的看到剩余磁盘空间的量。
重新分析点
一个重新分析点是你分配给文件或者文件夹的由使用者定义的数据的集合。数据的格式由应用程序解释,应用程序存储着数据和一个文件系统过滤器。这个过滤器是你为了解释和进行它而安装。
稀疏文件
一个稀疏文件是一个非常大的文件而其中却没有很多的数据。当一个稀疏文件设备被使用时,系统不会分配硬盘空间给文件,除了内部几乎为零的区域。
装载点
一个卷的装载点是因"装载"其他卷而存在的路径。因此,使用者和应用程序可以通过这个路径参考装载的卷。它允许你把完全不同的文件系统统一为一个逻辑文件系统。
改变日志
改变日志是一个数据库,其中包含每个变化了的NTFS 2000的卷的文件和目录的列表。每个卷都有他自己的日志,其中的记录反映了所有文件和文件夹所发生的变化。
desktop.ini
文件夹定制的所有内在的东西都保存在desktop。ini中。它是一个存储了关于文件夹的图标和信息提示信息的文本文件。在Windows 2000中,这个文件以UI 级被完全支持。

※编辑: modico (modico) 于 2000-05-07 14:07:41 在 [202.117.82.2] 编辑本文

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
多文件流
在NTFS文件系统下,每个文件都可以有多个数据流。值得指出的是,流并不是NTFS 2000的特性,而是从Windows NT 3.1 的时代就已经存在的.当你在一个非NTFS的卷阅读一个文件的内容(例如,磁盘中Windows 98的部分),你只能访问一个数据流.随之发生的是,你感觉到它是该文件的真实的和"独特"的内容.这样的一个主要的流是没有名字的,而且是非NTFS文件系统仅能处理的一个流.但是当我们在NTFS卷中创建一个文件时,事情就可能不同了.请参看图1去理解该图.
图1.多流文件的结构

一个多流文件是嵌入同一个文件系统的入口的一种单流文件的集合。它们看上去很象一个独特的原子单元,而且包含一定数目的独立子单元,你分别对它们进行创建,删除和修改。在很多通常的程序场景中流是很方便的。然而,如果你打算使用他们,请记住,一旦你拷贝一个多流文件到一个非NTFS的存储设备上(如CD,软盘或非NTFS分区),所有额外的流一旦丢失就无法恢复了。不幸的是,这种兼容性上的问题使得流在实际应用中缺乏吸引力。对于服务器端的应用程序设计和必然要运行于NTFS卷的程序,流是一种很好的工具,用来平衡建立重大的和创造性的解决方法。

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
流的基本原理
当你拷贝一个多流文件到非NTFS卷时,只有主要的流被拷贝了。这意味着你丢失了额外的数据,因为即使再次被拷贝回NTFS的磁盘,他们也不能被恢复。现在,让我们假设,你工作在完全的NTFS的机器上,让我们看看如何创建命名的流。在代码实例1中,你可以看到一个Windows Script Host (WSH), Microsoft Visual Basic Scripting Edition (VBScript)文件说明如何从一个NTFS文件中阅读和书写流。
要在一个文件中定义一个有名字的流,你必须遵循一个特定的命名规则,并且在文件名的结尾处加上一个冒号后面跟上流的名字。例如,要在文件test.txt中访问一个叫做VersionInfo的流,你就要使用如下的文件名:
Test.txt:VersionInfo
在任何Microsoft Win32 API的函数中使用它操作文件.为了访问流VersionInfo的内容,把它的名字传递给CreateFile()然后和通常一样使用 ReadFile()和 WriteFile()去完成读写操作.如果你想要检查在一个文件中一个特定的流是否存在,文件流的名字如上所述,可以使用CreateFile()去检查它的存在.
HANDLE hfile = CreateFile(szFileStreamName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
CloseHandle(hfile);
if (hfile == NULL)
MessageBox(hWnd, "Error", NULL, MB_OK);
使用流工作,你不必是一个熟练的C++程序员.你在Visual Basic 甚至是 script代码同样也能享受到流的优势,如代码实例1所示. 使得这类透明的关键的因素是所有底层的Win 32 API函数,特别是CreateFile(),在NTFS的部分支持以流为基础的文件名.如果你试图在非NTFS的部分打开一个文件叫做Test.txt:VersionInfo,例如在Windows 98的机器上,你将会得到"file not found"的错误信息.注意关键只是包含该文件的卷的文件系统,而与Windows平台或者主机调用应用程序的磁盘部分属性无关.换句话说,你既可以成功的访问一个在NTFS部分共享文件夹中的特定名字的流,也可以从一台相连的Windows 98的机器上访问它.此外,考虑到冒号不是一个有效的字符,即使对于常文件名也是如此.所以,当CreateFile()在文件命中遇到冒号时,它就知道他有其特殊的意义了.
在代码实例1中,你也可以在VBScript中使用流,因为文件系统对象的对象模式中完全使用了CreateFile()去打开,书写,创建和检验文件.在实例代码中,我们创建了一个文本文件带有一个空的,长度为零的主要流和许多你希望的有名字的流.尝试运行演示程序,创建一些流.假设你称它们为VersionInfo和 VersionInfoEx.在Windows的外壳中没有什么使你假设在一个特定的文件中流的存在.在图2中你可以看到文件test.txt在资源管理器中看起来是什么样子.

图2.一个文件长度为零但有有名字的流

在Size栏中所示的只是主要的,未命名的流的大小,而且即使在Properties对话框中也不会得到更多的关于流的信息.虽然,只有在NTFS卷中, Windows 2000 Properties的对话框中为你提供机会了解所有文件的总结信息,包括文本文件.例如点击Summary栏,然后键入一个作者的名字,如图3所示.
顺便提及,由于外壳UI的提高Windows 2000中这样一个名字可以被显示在特定的作者栏中.请前往http://msdn.microsoft.com/msdnmag/阅读著名的杂志MSDN.

图3.在NTFS卷中了解文件a.txt的额外信息

嘿,等一下,总结信息是你为Word或Excel文档设置的典型数据,但是它一定是文档本身的一部分.怎么可能在没有改变其简单的内容的情况下通过一个文本文件了解他呢?基本上, Watson.外壳通过流进行了这些操作.一旦你应用那些变化,尝试拷贝文件到作者的非NTFS的部分.图4的对话框就显示了这些.

图4. Windows 2000 可能的流的数据丢失的预先警告

它证明了文件test.txt包含一个带有文档总结信息的流.当你要拷贝一个带有额外信息的文件到不支持它的卷上时,系统就会发现.在非NTFS的部分,只有主要的未命名的流被拷贝,而其他的则被丢弃掉了.由于这个原因,如果目标不相适应的话以流为基础的文件几乎不能被拷贝.

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
流的备份和列举
有什么方法可以使用API函数来列举一个特定文件所有的流? 是的,有这样函数,但不象它应该的那么简单和容易理解.Win32的备份API函数(BackupRead, BackupWrite等等)可以用来列举一个文件中的流.然而,它们有一些奇怪,看上去与其说是一个有效的和确定的解决方法还不如说是一个工作区.
当你想要备份你一个文件或者是整个的文件夹时,你需要打包并且存储所有可能的信息.由于这个原因, BackupRead()是你要列举一个文件的流时最好的办法.让我们仔细看一下这个函数的原型:

BOOL BackupRead(
HANDLE hFile,
LPBYTE lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
BOOL bAbort,
BOOL bProcessSecurity,
LPVOID *lpContext
);

出于我们的目的,你可以忽略这里如上下文和安全性的方面. hFile参数必须通过调用CreateFile()而被包含,并且lpBuffer必须指向WIN32_STREAM_ID的数据结构.

typedef struct _WIN32_STREAM_ID {
DWORD dwStreamId;
DWORD dwStreamAttributes;
LARGE_INTEGER Size;
DWORD dwStreamNameSize;
WCHAR cStreamName[ANYSIZE_ARRAY];
} WIN32_STREAM_ID, *LPWIN32_STREAM_ID;

这个结构的最初的20字节代表每个流的标题. 流的名字紧接在dwStreamNameSize域之后,然后跟的是流的内容.因为文件的传统内容可以被看成是一个流,尽管是一个未命名的流.要列举所有的流,你只要循环直到BackupRead返回False.实际上BackupRead可以看成读到了一个特定的文件或者文件夹相关的所有信息.

WIN32_STREAM_ID sid;
ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
DWORD dwStreamHeaderSize = (LPBYTE)&sid.cStreamName - (LPBYTE)&sid+ sid.dwStreamNameSize;
bContinue = BackupRead(hfile, (LPBYTE) &sid,dwStreamHeaderSize, &dwRead, FALSE, FALSE,
&lpContext);

前面所述的片断是至关重要的代码,读入了流的标题.如果操作成功的话,你可以尝试读入流的实际的名字:

WCHAR wszStreamName[MAX_PATH];
BackupRead(hfile, (LPBYTE) wszStreamName, sid.dwStreamNameSize,&dwRead, FALSE, FALSE, &lpContext);

在处理下一个流之前,首先向前移动并调用BackupSeek()以备份指针:

BackupSeek(hfile, sid.Size.LowPart, sid.Size.HighPart,&dw1, &dw2, &lpContext);

在大多数情况下,你可以象处理常规文件一样处理流,例如采用DeleteFile()删除一个流.如果你想要刷新它们的内容,可以使用ReadFile()和WriteFile().没有一个正式的和被支持的方法移动和重命名流.在本文的最后部分,我将使用这个代码来建立NTFS 2000特定的Windows外壳扩展,它可以给带有流信息的所有文件增加一个新的属性页.同时,让我们大致了解一下NTFS的其他特性.
※编辑: modico (modico) 于 2000-05-07 16:52:15 在 [202.117.82.2] 编辑本文

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
硬连接
你知道关于快捷方式—也就是那些little .lnk文件,它们大多数分散在你的桌面上,你使用他们去调用一些其他的东西?毫无疑问,快捷方式是一个非常有用的特性,但是它们也有一些缺点.首先,如果你有多个快捷方式指向不同文件夹中的同一个目标,实际上你有同样的多个拷贝,幸运的是文件还是比较小的.更重要的是,快捷方式的目标对象过一段时间可能改变.它可能被移动,删除或是简单的重命名了.那你的快捷方式怎么样?他们是否能够检查到这些变化,并且跟踪它们,(自动)进行适当的升级?不幸的是,他们不能.这个问题的主要的原因是快捷方式的一个应用级特性.从系统的观点来看,它们只是用户定义的文件,当你试图打开它们的时候,只需要一些额外的工作.作为一个快捷方式,是一种你可以决定分配给其他的文件类的特权.除非它有意义,否则你可以创建你自己的除了扩展名为.lnk的类以外的快捷方式.所需要做的就是在注册表的入口类节点下命名IsShortcut.假设你想要把.xyz的文件作为快捷方式,在 HKEY_CLASSES_ROOT下创建一个.xyz的节点注册文件类,并且使它指向另一个节点,通常是xyzfile.然后加一个空的REG_SZ入口:
HKEY_CLASSES_ROOT\xyzfile
这样就完成了.
其他的操作系统,特别是Posix 和 OS/2,有一个相似的特点,那就是运行在系统级.特别是OS/2,把它们叫做shadows.一个硬连接是一个特定文件的文件系统级的快捷方式.通过创建一个已经存在的文件的硬连接,你既没有复制文件,也没有复制以文件为基础的参考信息(也就是说,快捷方式).然而,你在它的目录的入口添加了NTFS级的信息.物理文件仍完好地保存在它的原始位置.简单的说,现在可以通过两个或更多的名字访问同样的内容.
一个硬连接通过保存一个文件的多个副本来挽救了你,使得系统负责管理指向一个物理内容的各种路径名.这很大程度上简化了你的工作,节约了宝贵的磁盘空间.不仅如此,硬连接作为一个系统级的快捷方式,总是指向特定的目标文件,不论你是否重命名或者是移动了他.因为连接存在于所有的文件系统级,所有的变化自动的和明确的应用它.硬连接必须在同一个NTFS卷中创建.比如说,你不能有硬连接从drive C:指向drive D:的一个文件.
如果想要听起来更熟悉,不妨把硬连接想象成一个文件的别名.你可以使用任何别名去访问它,并且只有当它的所有的别名都被删掉后,该文件才被删掉.因为硬连接是别名,同步的内容是简单的工作。
CreateHardLink()是用来创建硬连接的API函数.它的原型如下:

BOOL CreateHardLink(
LPCTSTR lpFileName,
LPCTSTR lpExistingFileName,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

作为老的MIND的文章的伙伴代码,(参看"Windows 2000 for Web Developers," MIND, March 1999),我提供了一个COM对象,允许你用script代码创建硬连接.代码实例2展示了一个VBScript的程序,它实现了为一个文件创建硬连接的功能.而且很容易发现一个文件有多少个硬连接,把他们全部列举出来并没有什么实际价值.API的函数GetFileInformationByHandle() 填充了 BY_HANDLE_FILE_INFORMATION结构,它的 nNumberOfLinks 域中提示你有关信息.列举所有的连接文件的名字有一些困难.基本上,你必须扫描整个的卷,对每一个文件明确分配给它的特有的ID,并跟踪它.当你运行到一个存在的ID时,你会发现那个文件的硬连接.文件特有的ID是由系统分配的,存储在BY_HANDLE_FILE_INFORMATION 的 nFileIndexHigh和nFileIndexLow中.

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
享受NTFS的特性
流是一种特别有用的方式,可以在不改变或者破坏原始格式并且不占用更多磁盘空间的情况下给文件增加额外的信息.当然流占用它们的自然空间,但是Windows的资源管理器察觉不到这些.流对于资源管理器是不可见的,所以当显示还有充裕的磁盘空间时,实际上空闲的空间已经非常之低了.你可以给任何文件增加额外的信息(而且是不可见的),包括文本文件和可执行文件.
另一方面,硬连接是一个非常大的资源其中集中了共享信息.你只有一个真正的信息储存库,可以从很多不同的路径访问它.硬连接对于Windows NT技术不是一个全新的概念.硬连接在最初的Windows NT中就存在着,但是直到Windows 2000 Microsoft才提供了一个公共的函数去创建它们.每个文件至少有一个连接连接到他自己,所以GetFileInformationByHandle总是返回一个大于零的连接数.你不可以给文件夹建立连接,只能是文件.
流和硬连接都共同存在着一个实际问题,就是来自外壳的非常有限的支持.为了努力弥补这一点,我写了一个扩展的外壳来提供一个特定文件的关于流和硬连接的信息.图5说明了它的外观和感觉.

图5. 流的表显示了关于流和硬连接的信息

外壳扩展的源代码使用API函数BackupRead()列举流.简单调用DeleteFile()就可删除所选择的流的内容. Edit Streams按钮运行代码实例1中的script代码,使用这个方法可以增加和升级流.同样的Create Hard Link按钮运行代码实例2中的代码,用来创建附加的连接.只有当你刷新的时候,用户界面才会反映所有的变化.最后提醒你一下,请记住如果你删掉一个硬连接(也就是说删掉一个文件)总的连接数并没有升级只要回收站里的被删除文件还存在.
在这篇文章中,我只是大致勾画了NTFS 2000的表象,重点放在流和硬连接等关键特性.从一个更关阔的角度来看Windows 2000的文件系统中的新东西,我假定你参考了Jeff Richter 和 Luis Cabrera的文章"A File System for the 21st Century: Previewing the Windows NT 5.0 File System," ( November 1998 ) (http://www.microsoft.com/msj/1198/ntfs/ntfs.htm).另外一些有趣的问题,特别是关于稀疏流和重新分析点在这里没有涉及,但是如果你对这篇文章感兴趣的话请于我们联系,我们将很快就提供给你。
※编辑: modico (modico) 于 2000-05-07 17:51:57 在 [202.117.82.2] 编辑本文

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
代码实例1
' CreateStream.vbs
' Demonstrates streams on NTFS volumes
' --------------------------------------------------------
Option Explicit
' Some constants
Const L_NotNTFS = "Sorry, the current volume is not NTFS."
Const L_EnterFile = "Enter a file name"
Const L_TestNTFS = "Test NTFS"
Const L_StdFile = "c:\testntfs\test.txt"
Const L_EnterStream = "Enter a stream name"
Const L_StdStream = "VersionInfo"
Const L_EnterTextStream = "Enter the text of the stream"
Const L_StdContent = "1.0"
' Makes sure the current volume is NTFS
if Not IsNTFS() then
MsgBox L_NotNTFS
WScript.Quit
end if
' Query for a file name
dim sFileName
sFileName = InputBox(L_EnterFile, L_TestNTFS, L_StdFile)
if sFileName = "" then WScript.Quit
' Query for the stream to be written
dim sStreamName
sStreamName = InputBox (L_EnterStream, L_TestNTFS, L_StdStream)
if sStreamName = "" then WScript.Quit
' Initializes the FS object model
dim fso, bExist
set fso = CreateObject("Scripting.FileSystemObject")
' Creates the file if it doesn't exist
dim ts
if Not fso.FileExists(sFileName) then
set ts = fso.CreateTextFile(sFileName)
ts.Close
end if
' Try to read the current content of the stream
dim sFileStreamName, sStreamText
sFileStreamName = sFileName & ":" & sStreamName
if Not fso.FileExists(sFileStreamName) then
sStreamText = L_StdContent
else
set ts = fso.OpenTextFile(sFileStreamName)
sStreamText = ts.ReadAll()
ts.Close
end if
' Query for the content of the stream to be written
sStreamText = InputBox (L_EnterTextStream, L_TestNTFS, sStreamText)
if sStreamText = "" then WScript.Quit
' Try to write to the stream
set ts = fso.CreateTextFile(sFileStreamName)
ts.Write sStreamText
' Close the app
set ts = Nothing
set fso = Nothing
WScript.Quit
' ////////////////////////////////////////////////////////
' // Helper functions
' IsNTFS() - Verifies whether the current volume is NTFS
' --------------------------------------------------------
function IsNTFS()
dim fso, drv
IsNTFS = False
set fso = CreateObject("Scripting.FileSystemObject")
set drv = fso.GetDrive(fso.GetDriveName(WScript.ScriptFullName))
set fso = Nothing
if drv.FileSystem = "NTFS" then IsNTFS = True
end function

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通
modico
代码实例2
' Hardlinks.vbs
' Demonstrates hard links on NTFS volumes
' --------------------------------------------------------
Option Explicit
' Some constants
Const L_NoHardLinkCreated = "Unable to create hard link"
Const L_EnterTarget = "Enter the file name to hard-link to"
Const L_HardLinks = "Creating hard link"
Const L_EnterHardLink = "Name of the hard link you want to create"
Const L_CannotCreate = "Make sure that both files are on the same volume and the volume is NTFS"
Const L_NotExist = "Sorry, the file doesn't exist"
Const L_SameName = "Target file and hard link cannot have the same name"
' Determine the existing file to (hard) link to
dim sTargetFile
if WScript.Arguments.Count >0 then
sTargetFile = WScript.Arguments(0)
else
sTargetFile = InputBox(L_EnterTarget, L_HardLinks, "")
if sTargetFile = "" then WScript.Quit
end if
' Does the file exist?
dim fso
set fso = CreateObject("Scripting.FileSystemObject")
if Not fso.FileExists(sTargetFile) then
MsgBox L_NotExist
WScript.Quit
end if
' Main loop
while true
QueryForHardLink sTargetFile
wend
' Close up
WScript.Quit
////////////////////////////////////////////////////////
' // Helper Functions
' Create the hard link
'------------------------------------------------------------
function QueryForHardLink(sTargetFile)
' Extract the hard link name if specified on the command line
dim sHardLinkName
if WScript.Arguments.Count >1 then
sHardLinkName = WScript.Arguments(1)
else
dim buf
buf = L_EnterHardLink & " for" & vbCrLf & sTargetFile
sHardLinkName = InputBox(buf, L_HardLinks, sTargetFile)
if sHardLinkName = "" then WScript.Quit
if sHardLinkName = sTargetFile then
MsgBox L_SameName
exit function
end if
end if
' Verify that both files are on the same volume and the
' volume is NTFS
if Not CanCreateHardLinks(sTargetFile, sHardLinkName) then
MsgBox L_CannotCreate
exit function
end if
' Creates the hard link
dim oHL
set oHL = CreateObject("HardLink.Object.1")
oHL.CreateNewHardLink sHardLinkName, sTargetFile
end function
' Verify that both files are on the same NTFS disk
'------------------------------------------------------------
function CanCreateHardLinks(sTargetFile, sHardLinkName)
CanCreateHardLinks = false
dim fso
set fso = CreateObject("Scripting.FileSystemObject")
' same drive?
dim d1, d2
d1 = fso.GetDriveName(sTargetFile)
d2 = fso.GetDriveName(sHardLinkName)
if d1 <> d2 then exit function
' NTFS drive?
CanCreateHardLinks = IsNTFS(sTargetFile)
end function
' IsNTFS() - Verifies whether the file's volume is NTFS
' --------------------------------------------------------
function IsNTFS(sFileName)
dim fso, drv
IsNTFS = False
set fso = CreateObject("Scripting.FileSystemObject")
set drv = fso.GetDrive(fso.GetDriveName(sFileName))
set fso = Nothing
if drv.FileSystem = "NTFS" then IsNTFS = True
end function

欲求无限
立地成佛
.
有时候
交谈变得空洞
沉默却像沟通