跟踪正在移动的文件(可能在磁盘之间),VB.NET(或C#)的最佳方法

时间:2014-03-17 04:31:33

标签: c# vb.net filesystems filesystemwatcher windows-shell

我正在开发一个“动态快捷方式”应用程序,它创建指向注册表项而不是实际文件/可执行文件的特殊快捷方式文件。注册表项包含所需文件的路径。我希望有一个守护程序运行,它监视链接文件并更新其注册表项(如果它们被移动或重命名)。重命名我可以使用System.IO.FileSystemWatcher处理,但处理移动文件的最佳方法是什么?

我知道这超出了FSW的基本功能(尽管是低级文件系统操作)。问题是,最好的方法是什么?

  1. 我读过的大多数帖子/文章都提出了完全“hacky”的方法,主要是寻找删除,然后在文件的新位置创建,并按文件大小连接两者,meta -data,删除/创建触发器,哈希等之间的时间。这可能是我必须采用的方法,在所有驱动器上设置FSW。但是,我希望有更好的方法。

  2. 是否可以:

    2.1。听取shell并“听”移动操作?

    2.2或者(甚至更激进)替换或添加一些shell移动操作,它会触发某种事件或执行注册表更新任务本身,从而排除对守护进程的需要?

  3. 我有一种感觉,每个人都会告诉我1.是唯一的课程,但我期待你的建议。 (VB.NET中的答案首选,但如果需要可以从C#翻译。)

4 个答案:

答案 0 :(得分:4)

[我不确定这是否应该附加为"更新"到我原来的帖子或作为一个单独的答案发布]

总结(所有两个)答案加上我自己的实验(试图给出这个问题的明确答案):

  • 似乎唯一的高级(.NET)解决方案是使用FileSystemWatcher,它不会检测"移动"开箱即用(尽管它是一个低级命令)。 FSW方法是非平凡的,相对资源昂贵,在某些地方(即使用计时器)邋and,并且有其局限性和警告。它也没有真正反映出"移动" - 它只是从非常可能是移动的症状推断它(并且在任何情况下对文件系统都有相同的影响),但理论上可以通过非移动动作产生。此外,看起来您必须知道在移动发生之前您想要观察哪些文件移动,因为它发生时无法告知。

  • 在较低级别(可能涉及C ++),可以挂钩API调用以获得有关何时"移动"叫做。这样做的好处是,您不必提前决定观看文件,并且比听取删除文件的资源更便宜。和"创造"并试图比较它们。

  • 在系统编程层面(如果您不知道自己在做什么,可能会涉及到C ++并且很容易破坏您的计算机),可以构建一个文件系统过滤器驱动程序:这将采用以下概念:检测移动到真正的肛门水平,检测即使没有内核也可以重新分配文件系统资源。

经过一些实验,这里是FileSystemWatcher方法(或者至少对我来说最明显的方法)如何工作,它的怪癖和它的局限性的一般结构。 [没有代码atm,它已经很好地集成到我的应用程序中,我还没有优化它,但我可能会在这里添加一些片段]。

FileSystemWatcher方法(用于检测文件移动或重命名的时间):

0.1。的 FileSystemWatchers。

您需要为要监视的每个最高级别目录创建一个FSW(例如,每个可写逻辑驱动器一个)。

0.2。的重命名。

直接重命名文件很简单。

0.3。 被移动。

这部分远非微不足道;它基本上涉及在三种不同场景中比较文件。

3.0.1。 确定删除/移动的文件是否与创建/移动的文件相同。

为了确定删除的文件和创建的文件是否匹配,文件名是无用的(可以在移动期间更改)。您可以使用文件大小和属性(如创建时间)的混合,甚至是整个文件的哈希。在我的特定解决方案中,我只需要观察特定文件的移动"已注册"在加载时间之前,所以我能够为这些文件提供一个独特的指纹作为元数据,然后我可以用它来比较文件(这在现实世界的场景中运行良好,但在测试中很容易被恶意破坏,这让我失望了完美。)

3.0.1.1。 何时读取filesize / attributes / take hash?

在我提出静态指纹想法之前,我正在使用简单的文件大小+创建日期验证检查来测试我的代码。我很快意识到,我必须记下已删除文件的文件大小和创建日期(或哈希或其他任何你想要使用的内容),然后才会显示为#34;已删除",因为你可以' ; t检查不存在的文件的大小。如果(像我一样)你知道你想要提前观看的文件,那么你需要在启用FileSystemWatchers之前读入这些值;你还需要倾听"改变"这些文件上的事件更新了文件大小和创建日期的值,采用了新的哈希等。这就引出了一个问题:如果你不知道你有兴趣观看哪些文件,看看它们是否移动,你会怎么做? ?如果您只知道自己可能有兴趣了解他们是否已经移动了#34;删除&#34 ;?不幸的是,这超出了我的范围(这不是我必须处理的事情。)除非你能想出解决这个问题的方法,否则继续使用FileSystemWatcher方法是没有意义的。此外,我猜想(虽然很容易出错),没有高水平的解决方案可以满足您的需求。如果你确实提出了一个解决方案(请在下面发表/发表评论/在这篇文章中对其进行编辑),我已经完成了其余部分的兼容。

3.1。 场景1:直接移动文件本身。

"删除"在检测到特定文件时,您需要开始监听"创建"一致的文件。而不是无限期地收听匹配"创建"一个可能刚被删除的文件(实际上涉及检查目录中创建的每个文件),你可以使用一个计时器来启动和停止"监听"旗帜(实用,但从纯粹主义的观点来看有点武断),决定在例如没有适当匹配的1000毫秒创建它可能不会成为一个。

3.2.0。 常见的误解。

在浏览文档后,很多人似乎都认为移动或重命名文件夹会触发所有子文件和子文件夹的重命名,而不是删除和创建。事实上,docs所说的是:

  

如果将包含文件的文件夹剪切并粘贴到正在观看的文件夹中,则FileSystemWatcher对象将 报告为新文件夹,但不报告其内容,因为它们是< strong> 基本上 仅重命名。

(即只有顶层文件夹才会重命名或创建/删除,子文件/子文件夹会抛出NOTHING)。这意味着如果你想知道移动某个文件的时间和地点,你也必须听取每个文件的上传文件。

3.2.1。 场景2:重命名包含文件夹。

在我的解决方案中,因为我知道我正在观看的所有文件,每当我的一个FileSystemWatchers报告重命名文件夹而不是文件时(字符串部分在最后一个&#34; /&#34;将不包含&#34;。&#34;)我检查了每个观看的文件,看看他们的路径是否在该目录中,如果是,则将文件路径的开头更改为新目录的路径et voila!我知道我的文件被移动到哪里。如果您现在没有提前查找要查找的文件,那么您将不得不递归搜索每个文件夹中的所有内容,以便重命名&#34;重命名&#34;。

3.2.2。 场景3:移动包含文件夹。

这个感觉就像一记耳光:为了建立你的移动检测程序,你必须能够检测到移动。这里的文件夹将抛出&#34;删除&#34;然后是&#34;创建&#34;。在我的情况下,解决方案只是回收3.1和3.2.1中的技术:当一个文件夹&#34;删除&#34;检测到,我检查它是否包含我的任何监视文件。如果确实如此,我会设置一个&#34; listen&#34; flag(以及一个用于监听它的计时器)并检查旧文件夹中我的文件的子目录路径,对应每个新文件夹&#34; create&#34;检测到它是否指向具有所需指纹的文件。如果是这样,我现在拥有文件的旧路径和新路径,并检测到移动。如果您不知道要监视哪些文件,则可能必须通过比较磁盘上的大小和&#34;已删除&#34;之间的子文件/子文件夹数来验证文件夹移动。文件夹和&#34;创建&#34;用于确认文件夹先移动的文件夹,然后递归搜索该文件夹以查找您感兴趣的文件。

3.3。 进一步复制:跨文件移动大文件。

这是一个问题,我很幸运没有遇到(因为我只是比较指纹元数据,并且不需要访问文件);但是在驱动器之间移动大文件(分阶段传输,触发创建事件然后发生一系列更改事件)可能会导致真正的麻烦。

3.3.1。 头痛1:&#34;创造&#34;目标文件不完整时触发。

这意味着将其大小与&#34;删除&#34;文件会产生假阴性。你甚至不能对文件的第一部分进行哈希,以向你的程序表明这个&#34;可能&#34;是已删除的文件,因为移动操作将锁定文件访问权限。您只需要尝试判断创建的文件是否仍在移动并等待它完成。

3.3.2。 头痛2:没有确定的方法&#34;告诉&#34;创建的文件仍在移动中。

有些人建议检查创建文件的文件访问权限,但它们可能与创建的文件无法区分,并且仍然被任何随机应用程序使用。其他人建议为&#34;更改&#34;设置短时间限制的监听标志。在文件上,但这又与应用程序修改的文件无法区分。实际上,如果文件恰好是一个日志文件,并且通过某个进程不断地快速更新,那么等待&#34;更改&#34;到超时的文件可能永远不会结束。

3.3.3。 头痛3 :(未经测试)可能是这些动作&#34;删除&#34; 之后的文件&#34;创建&#34;目标文件*。

虽然我还没有对它进行测试,但情况确实如此。 [如果有人知道,请随意编辑(或删除)此部分]

3.4。 一个哲学上的quandry:两个相同的文件是一样的吗?

这是一个非常迂腐和随意的思想实验,但是说你有两个驱动器,每个驱动器都有一个相同的File.txt副本。您运行批处理文件删除第一个驱动器上的副本,然后立即将第二个驱动器上的文件的副本复制到第二个驱动器上的同一文件夹中,并将其命名为Copy of File.txt。除非您使用指纹,否则您的代码将识别删除然后创建相同的文件,并且无法区分从第一个驱动器到第二个驱动器的文件移动(重命名)所发生的情况。文件系统的最终状态在两种情况下都是相同的,因此它不应该导致您的应用程序出现意外行为,但是您真的满足于称之为“移动”#34;纯粹基于同构? (特别是当你知道内核看到它的方式不同时)?

答案 1 :(得分:0)

使用C#提供的高级无限制api - 不,你不能。使用FileSystemWatcher ..在移动文件的同一驱动器操作不是“删除并创建” - 它是“重命名”。

如果您可以/想要进入更低级别,那么您可以挂钩IFileOperation shell接口的MoveItem和MoveItems,以及来自Kernel32.dll的MoveFile ......它可以与大多数应用程序一起使用,但需要扩展安全权限对于您的应用程序,这在法人环境中几乎是不可接受的。

答案 2 :(得分:0)

该任务有两个难以实现的缺陷:(a)跨磁盘的移动操作实际上是一系列读/写操作,然后是删除而不是移动。在那些读/写操作期间,可以对数据进行一些转换; (b)移动不仅可以通过外壳进行。

您可以使用文件系统过滤器驱动程序在文件操作发生时立即拦截它们。然后,您需要检测由同一进程对您的文件执行的读取和写入操作的顺序。即如果您的代码检测到,文件是按顺序读取的(注意:某些复制工具可以并行读取多个线程中的文件)然后将类似的数据块写入另一个文件并在读取所有内容后删除源文件并完成文件内容已写入其他地方,然后你可以猜到你已经过了文件移动操作。

答案 3 :(得分:0)

凹凸&amp;更新:这可能违反了StackOverflow的规则,但我想指出登陆这个页面的很多人(以及关于SO的无数类似问题),我已经开始在MicroSoft UserVoice上添加MOVE检测功能请求到FileSystemWatcher。从长远来看,最好的解决方案,而不是试图解决问题,可能是请求MicroSoft解决它。如果您来这里是因为您也需要解决此问题,请考虑clicking here并对此功能进行投票。