返回首页

{A}背景
符号链接,连接点,并挂载点,统称为重分析点,转换成NTFS版本5(或第3版,这取决于你读),因此,从2000年开始在Windows操作系统。然而,他们并没有真正影响的公众意识,直到Vista的抵达,在微软,他们的智慧,决定移动或重命名所有的'特殊'的文件夹,如"我的文档"(其中没有一个人真正理解反正需要),然后,只是为了最大限度地混乱,提供quot; shortcutsquot;这些目录下原来的名称。然而,经仔细检查,这些quot; shortcutsquot;没有快捷方式,但重分析点(我会在后两者之间的差异)。 简介
在应用程序的运行Vista下(不行使胆怯!)升级,它成为我必须采取这些特殊的实体,我发现,为了正确执行的应用程序,我需要一种方法来找到一个重新分析点的目标。有一定量的有关重分析点的网页上的信息,但其中很多是混乱和矛盾。不过,我坚持了下来,并在这个过程中,发现了不少有趣的事实对这些难以捉摸的生物。这篇文章是我探索的结果。我可能已经得到了几件事情是错误的,而且,正如我们将看到的,也有一定的问题尚未答复。在这些,我很高兴地收到反馈。
最后的结果是写在C#中使用的测试程序。NET 2.0中,显示系统的目录层次结构,并显示所有的重分析点的属性。重分析点 - 它们是什么?
重分析点,基本上都是重定向,在功能上非常类似我们所熟悉的快捷键。然而,快捷方式,其实很正常的文件(。LNK扩展名)和操作系统处理,重新分析点的实施在一个文件系统(NTFS)本身较低的水平。
是另一个很大的差异,以及快捷键是相当quot; intelligentquot;,如果一个快捷方式移动的目标(例如,远程驱动器,从而改变它的驱动器盘符),Windows可以经常跟踪改变和更新快捷方式文件quot; flyquot。这样什么也没有发生重分析点 - 他们指定的目标路径,但没有知识,是否该路径的退出,甚至无论是形成良好的Windows路径。
有四种口味重分析点,我知道 - 符号链接,连接点,挂载点,和远程存储服务器。我会集中在挂载点一目了然,符号链接和交接点。有关远程存储服务器,我怕我什么都不知道。符号链接
一个符号链接的概念将Unix用户熟悉。符号链接是一个quot; shortcutquot;系统上的其他文件或文件夹。交接点
交接点是非常喜欢,除了一个符号链接,它只能指向一个文件夹,而不是一个文件。挂载点
一个挂载点,点到磁盘卷。这使得整个驱动器或分区加以解决,就好像它是另一个驱动器上的一个文件夹。这使得系统超过26个驱动器的字母限制。在Vista上的重分析点
任何人如已采取的大幅下挫,并安装了Vista将几乎肯定会遇到的重分析点。老最喜欢的文件和设置,例如,不再是一个文件夹,但交接点,点到新文件夹C:\用户。在C:\用户,我们发现,现在被称为默认默认的用户文件夹,但有一个结点称为默认用户指向它。同样,All Users文件夹现在是一个符号链接横跨海湾到一个全新的文件夹的方式,在C:\ PROGRAM数据。为什么其中之一是一个结点,一个是一个符号链接是一个谜我。
我们可以看到更好的是怎么回事,如果我们打开一个命令提示符。运行DIR /?告诉我们,/ a参数,现在有一个新的选项/ AL。这列出了重新分析点,而如果我们运行在C:\用户,我们看到以下:

C:\Users>dir /al

Volume in drive C has no label.

Volume Serial Number is 8C6D-6991

Directory of C:\Users

02/11/2006 13:02 <symlinkd> All Users [C:\ProgramData]

02/11/2006 13:02 <junction> Default User [C:\Users\Default]

0 File(s) 0 bytes

2 Dir(s) 117,946,601,472 bytes free

所有用户是作为一个LT; symlinkdgt;(即,符号链接到一个目录),默认用户为LT; junctiongt;标记。目标目录也显示。 (奇怪的是,默认用户已经给定,默认情况下,一组安全属性,实际上从资源管理器无法访问,即使管理员权限运行,虽然我可以在它的罚款的总指挥官,我选择资源管理器替代。 )
Vista中也提供了一个名为MKLINK一个工具,它允许你创建你自己的连接点和符号链接。我创建了几个,发现了一些有趣的事情。首先,如果你看一个符号链接的属性,你会得到一个快捷方式"选项卡,告诉你的目标,而一个结点,你会不会。其次,你可以指定几乎任何一个符号链接或交界点的目标 - 这是只有当系统试图访问它,它会失败,如果它不存在或无效的路径。当我们在编程方面,这将成为显著。
是这一切的另一个潜在的可怕后果。有看看在C:\ PROGRAM数据。这被称为应用程序数据包含一个结点,它的目标是。 。 。 C:\ PROGRAM数据。现在,如果你是一个开发商,你已经写了一个可爱的小递归方法来建立一个目录树,你需要回去看看你的代码之前会导致一些非常严重的堆栈溢出。而且不要忘了,理论上至少,这些小的小动物可以弹出XP和2000上了。我没有勇于尝试设立的符号链接实际上是指向本身 - 如果有人勇于尝试,请让我知道会发生什么。
还有一个在Vista上所谓MOUNTVOL的工具,它允许你设置挂载点。我没有尝试过这样的 - 任何人应该感到自由添加后。在XP中重新分析点
MKLINK和MOUNTVOL公用事业不存在下XP(XP下创建的重分析点的方法,但它们涉及到一些严重的阀帽*摆弄下)。然而,有趣的是,给Dir / AL参数,虽然它不显示在帮助,不实际工作,但它显示了作为它的所有重分析点; junctiongt;第
,但是,可以设置在XP下创建磁盘管理下的一个分区,并指定它应该是一个安装分区,而不是一个逻辑驱动器安装点。你给它一个目标目录,你瞧,它在资源管理器中弹出,就像一个目录。我没有在本地非系统盘,可见XP和Vista,通过创建两个分区,一个逻辑驱动器(H:),这一点,一个在H的根装入的驱动器,称为H:\ MountedDrive 。猜测 - 当我启动Vista中,它似乎是无法看到的安装在所有的驱动器,只是H:。然后,我再看看,发现,更奇怪的是,这是无法看到的基极驱动,并装入的驱动器,它可以。上在XP的基础驱动器上创建的文件到Vista完全看不见,而文件创建在H:\ Vista下出现在H:\ MountedDrive \ XP下。奇怪!
另外一个有趣的发现是,创造了一个重​​新分析点我共享Vista下,当我返回到XP的驱动器的负载,尽管他们所有可见从命令提示符下,从资源管理器,他们没有实际工作。双击单击它们只是给出了一个邦,和编程访问抛出一个IOException异常:quot;的文件不能被访问的systemquot;。
[*作为一个英国人,我有一个帽子和引导,而不是一个引擎盖和后备箱]。为开发的重分析点
至于说上面的,我的主要要求是我的彗星#程序的属性,具体的目标路径重新分析点,的一种方式的,其优秀的文章,我必须马上信用的FlexHex的传播者,{ A2},给我最初的腿,我需要。
重分析点竟然是微不足道的访问属性。有没有托管的API调用来覆盖它(毫无疑问,有人现在将弹出,并告诉我有!),所以大幅剂量调用需要的平台。事实上,甚至没有一个非托管函数的目的,事实证明,你必须调用DeviceIoControl,传递FSCTL_GET_REPARSE_POINT控制代码。
为了使问题复杂化,在网站上公布的REPARSE_DATA_BUFFER结构有两种不同的版本。这是由DeviceIoControl的填充结构,微软定义了一个方法,而FlexHex家伙使用不同的版本。最后,我使用Microsoft版本,但挂载点的东西,这是作为联盟(在C#中没有)实现的,反正是只有一个命名方便。我的版本看起来像这样(请注意使用,这是非常重要的CHARSET = CharSet.Unicode):{C}
调用DeviceIoControl之前,有必要与打开文件,标志作为传递FILE_FLAG_OPEN_REPARSE_POINT和FILE_FLAG_BACKUP_SEMANTICS。 FILE_FLAG_BACKUP_SEMANTICS是为了得到一个目录句柄,这意味着,在打开文件之前,我们必须调整的特权,从而:
    bool success;

    IntPtr token;

    TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();

    tokenPrivileges.Privileges = new LUID_AND_ATTRIBUTES[1];



    success = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, 

                               out token);

    if (success)

    {

        // null for local system



        success = LookupPrivilegeValue(null, SE_BACKUP_NAME, 

                                       out tokenPrivileges.Privileges[0].Luid);

        if (success)

        {

            tokenPrivileges.PrivilegeCount = 1;

            tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            success = AdjustTokenPrivileges(

                token,

                false,

                ref tokenPrivileges,

                Marshal.SizeOf(tokenPrivileges),

                IntPtr.Zero,

                IntPtr.Zero);

        }

        CloseHandle(token);

    }

然后我们打开文件:
    IntPtr handle = CreateFile(

                     path, 

                     FileAccess.Read, 

                     FileShare.None, 

                     0, FileMode.Open, 

                     FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 

                     IntPtr.Zero);

,然后我们得到了重新分析缓冲区:
    REPARSE_DATA_BUFFER buffer = new REPARSE_DATA_BUFFER();

    // Make up the control code - see CTL_CODE on ntddk.h



    uint controlCode = (

        FILE_DEVICE_FILE_SYSTEM << 16) | (FILE_ANY_ACCESS << 14) |

        (FSCTL_GET_REPARSE_POINT << 2) | METHOD_BUFFERED;

    uint bytesReturned;

    success = DeviceIoControl(

                handle, 

                controlCode, 

                IntPtr.Zero, 

                0, 

                out buffer, 

                MAXIMUM_REPARSE_DATA_BUFFER_SIZE, 

                out bytesReturned, 

                IntPtr.Zero);

现在,有趣的开始。调用成功后,该缓冲区包含一个Unicode字符称为ReparseTarget数组。定义为一个字符串,因为它实际上包含两个字符串,可能会或可能不会空终止,不要被诱惑。这些被称为打印名称和字幕名称,偏移和长度每个缓冲结构。然而,有一个问题。出于某种原因,符号链接在前面的两个额外的字符。这些似乎是'\ 1',\ 0 \或'\ 0','\ 0'。我不知道他们正在做什么,如果任何人都可以解释,我会很高兴听到的​​。
重分析点的类型是ReparseTag领域举行。 WINNT.H中定义了几个常量,可能会出现在这里:
    private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;

    private const uint IO_REPARSE_TAG_HSM = 0xC0000004;

    private const uint IO_REPARSE_TAG_SIS = 0x80000007;

    private const uint IO_REPARSE_TAG_DFS = 0x8000000A;

    private const uint IO_REPARSE_TAG_FILTER_MANAGER = 0x8000000B;

我们感兴趣的只有IO_REPARSE_TAG_MOUNT_POINT,还定义了一个结点,和IO_REPARSE_TAG_SYMLINK这不是在WINNT.H中定义,但有0xA000000C价值。我不知道别人是什么 - 再这些知识比我邀请后。
的代码得到两个字符串,因此标签:
    if (buffer.ReparseTag == IO_REPARSE_TAG_SYMLINK)

    {

        subsString = new string(

            buffer.ReparseTarget, 

            (buffer.SubsNameOffset / 2 + 2), 

            buffer.SubsNameLength / 2);

        printString = new string(

            buffer.ReparseTarget, 

            (buffer.PrintNameOffset / 2 + 2), 

            buffer.PrintNameLength / 2);

        tag = TagType.SymbolicLink;

    }

    else if (buffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)

    {

        subsString = new string(

            buffer.ReparseTarget, 

            buffer.SubsNameOffset/2, buffer.SubsNameLength/2);

        printString = new string(

            buffer.ReparseTarget, 

            buffer.PrintNameOffset / 2, buffer.PrintNameLength / 2);

        tag = subsString.StartsWith(@"\??\Volume") ? 

            TagType.MountPoint : 

            TagType.JunctionPoint;

    }

现在,根据不同的网络文章,符号链接保存相对路径,而路口使用绝对路径。这是不正确的的。符号链接的基本存储你到他们的任何字符串,它是由谁就解码他们决定,如果目标是相对的,绝对的,或只是垃圾。交接点都略有不同,因为它们总是返回,至于我可以告诉大家,是工作时创建的交界点,是一个绝对路径,。尽管如此,仍不能保证它是一个有效的路径,或它的存在当结点访问(记住实际的目标可能已被删除或移动,因为重新分析点创建)。我已经实现的功能需要的路径,如果它是一个符号链接和相对路径,"; normalisesquot;追加到通过重新分析点的驱动器和路径,将其转换为绝对路径。但是请注意,目标路径可能很合法包含。..第这些也可以被quot; normalisedquot,但我不打扰。
打印名称和字幕名称之间的区别似乎是替补名称,如果它包含一个绝对路径,是一个quot;未解析pathquot;,这意味着它开始/ /之前的驱动器盘符?? 。现在,据我所关注的,它是几乎猜测工作,从这里就进来,我不知道,如果这两个名字将永远存在,或者他们是否会始终是相同的(除了/?/ ),所以我曾试图应付所有的可能性。我用的是打印的名称,如果它的存在,否则,我使用的替补名,去掉不必要的东西。
第三种可能性是,它是一个挂载点。这相同的标记作为一个路口,但似乎没有返回打印字符串和一个潜艇字符串开始quot; \ \ \ \ Volumequot;再次,这是纯粹的猜测,我欢迎任何记录的事实。
最后,请注意我的程序不处理符号链接指向文件。重分析点,指向目录,或看起来像目录(安装点),在代码目录,即行为,他们将被列入Directory.GetDirectories(路径),这是我使用的调用。演示程序
我提供了一个演示程序,无论是作为一个完整的VS2005项目,并作为一个EXE文件。演示程序基本上建立了一个驱动器和目录treelist - 这是任何看起来像一个目录。然后,它从目录中提取的目标,如果它是一个ReparsePoint类进行实例化,事实上,重新分析点。
程序GUI提供的treelist,彩色的重分析点,表明它们是什么。在底部的标签显示实际的目标路径,路径后,我的功能已quot; normalisedquot;,即制成一个绝对路径。最后,我添加了一个单选按钮来控制程序是否使用符号链接或访问的文件夹,它的目标是其目标。结果应该是相同的,但是,XP中所述,不能实际访问的符号链接,所以必须使用的目标。
{S0}附录
现在,这里的一件有趣的事。虽然运行进一步的测试,我发现(你可以在家里尝试这个),如果一个文件夹在资源管理器(或类似)来看,CreateFile调用返回一个错误,32(ERROR_SHARING_VIOLATION)。不幸的是,我的错误报告是不辉煌,但明智的插入System.Windows.Forms.MessageBox.Show()将揭示问题(如果有足够的需求,我会后方案的一个修订版)。
总之,要解决这个问题似乎是与任何其他文件共享,以取代在CreateFile调用FileShare.None参数(我们不读取或写入该文件,因此它似乎相当合理使用FileShare.ReadWrite)。现在,我无法解释这一点 - 我的文件共享价值的理解是,它指定其他方案是允许这样做,而我已打开文件。如果任何人都可以摆脱任何光线,我喜欢听到他们。

回答

评论会员:游客 时间:2011/12/15
leandrobecker:WIN32_FIND_DATA.dwReserved0|使用WIN32_FIND_DATA.dwFileAttributes==FILE_ATTRIBUTE_REPARSE_POINT的FindFirstFileAPI,可用于检测的符号链接(IO_REPARSE_TAG_SYMLINK)或路口(IO_REPARSE_TAG_MOUNT_POINT)。当dwReserved0是IO_REPARSE_TAG_MOUNT_POINT时,文件夹,可以一个路口或卷装入点(文章作者)。要检查,如果是一个路口或卷装入点,通过文件夹的路径GetVolumeNameForVolumeMountPoint。如果GetVolumeNameForVolumeMountPoint返回TRUE,该文件夹的卷装入点,如果假文件夹的回报是一个结点,如果GetLastErrorERROR_INVALID_PARAMETER(0x57)。我认为这是容易处理特权和DeviceIoControl的。的问候,
Jeffy210
评论会员:游客 时间:2011/12/15
这,正是我一直在寻找和作品完美
!CIDev
评论会员:游客 时间:2011/12/15
文章写得清清楚楚。我不知道任何有关重分析点,现在我做imgsrc=http://www.orcode.com/upimg/2011_12_15_06_27_10_1.gif感谢仅仅因为代码的工作,它并不意味着它是良好的代码
。亚历山德罗男
评论会员:游客 时间:2011/12/15
仍然是任何人访问此页?我是不是能够得到一个结LinkMagic其他合著者的反应,想到这个地方,但最后发表的文章日期是2009年8月!詹姆斯
米歇尔Ruiter
评论会员:游客 时间:2011/12/15
喜戴夫,我在写一个小程序,在我打算创建一些链接(快捷方式在开始)菜单,复制的文件。我遇到不同的问题,上面我不为所有用户创建成功,我想,因为你强调XP和Vista之间的差异。我该怎么办?我希望你能帮助我。,亚历山德罗imgsrc=http://www.orcode.com/upimg/2011_12_15_06_27_10_2.gif
戴夫米奇利
评论会员:游客 时间:2011/12/15
我的管理,以创建到自身的链接,这样,:-创建一个名为的文件夹-创建一个链接名为b,-删除一个-重命名为B到A使用链接总是返回这个错误,但是:"文件名不能系统解决"这既适用于一个结点和一个symlinkd
。乔斯消费力
评论会员:游客 时间:2011/12/15
不包含在ZIP存档的依赖DLL当你有时间,您可以修复它​​。"被叫方(服务器[不是服务器应用程序])是不是和消失;所有连接都无效。IE浏览器,没有执行呼叫,说:"当我试图访问一个死者的[窗口]使用JavaScript对象。::..:.:..::KiRtANGOR::..:.:..::{BR}
尼古拉斯巴特勒
评论会员:游客 时间:2011/12/15
抱歉。感谢您让我知道。现在应该是OK。戴夫
乔斯消费力
评论会员:游客 时间:2011/12/15
也许这种情况已经改变,因为Vista的beta版,但我的电脑上,"所有用户"和"默认用户"的交接点,以及"所有用户"是不是一个符号链接乔斯
科林MAHARAJ
评论会员:游客 时间:2011/12/15
我一直在看这呢!我想你一个月前发布本文-它的伟大imgsrc=http://www.orcode.com/upimg/2011_12_15_06_27_10_1.gif一个情侣增加供参考:硬链接:另一种类型的链接,只有文件。在Vista中的新FindFirstFileName和FindNextFileName函数枚举所有的硬链接文件。imgsrc=重新分析点标记:您可以使用FindFirstFile的重新分析点的类型,它返回一个WIN32_FIND_DATA。dwReserved0成员持有的标记。{A6}符号链接:这些都是新的Vista中,并可以将目录或文件,并可以绝对或相对的。我不能让他们的工作横跨映射网络驱动器。{A7}启用特权:这是在框架中,但内部的,所以你必须访问它使用反射。codepreTypeprivilegeType=Type.GetType(spanclass="code-string""/spanspanclass="code-string"System.Security.AccessControl.Privilege"/span);MethodInfoenable=privilegeType.GetMethod(spanclass="code-string""/spanspanclass="code-string"Enable"/span);MethodInforevert=privilegeType.GetMethod(spanclass="code-string""/spanspanclass="code-string"Revert"/span);objectseBackupPrivilege=Activator.CreateInstance(privilegeType,spanclass="code-string""/spanspanclass="code-string"SeBackupPrivilege"/span);enable.Invoke(seBackupPrivilege,null);...revert.Invoke(seBackupPrivilege,null);/pre/code一个伟大的文章再次表示感谢。----------------------------{BR}是优秀的相互imgsrc=http://www.orcode.com/upimg/2011_12_15_06_27_10_1.gif