检测并删除被占用的文件

来源:互联网 发布:文库财富值 淘宝项目 编辑:程序博客网 时间:2024/05/02 21:59

        在操作系统使用过程中,经常会遇到一些文件被某些程序占用而无法被删除的事情。这个时候,如果是手动进行的删除可能影响还小,因为有很多方式可以解除引用,比如借助于其它的某软件工具。但是在实际编程中,如果给一个文件重命名,升级的时候替换掉原来老的文件等等就成了一个致命的缺陷,由于一些不必要操作或其它软件造成的这种情况而使得我们程序不能完整进行,就会成为比较棘手的问题。恰巧这样的问题就在我们开发的软件(这里简称 TL)升级过程中会经常遇到,那么探究一下这其中的原理并尝试解决,无疑是一件非常有趣且有用的事情。

        在 TL 升级过程中,删除某些程序产生的文件必不可少的操作。但我们经常遇到某些文件无法被替换成功。一来可能是因为用户用其它软件打开了某文件而造成文件被占用,二来可能是某些程序后台读取到这个文件而造成了占用。总而言之,文件被其它进程打开。这些都会导致这个文件无法被删除。但有些时候删除这样的文件是安全且是用户友好的行为。

        那么操作系统如何判断文件被占用而不让用户进行删除操作呢?

        答案就是句柄表。

        在 Windows 操作系统中,如果一个进程打开了一个文件,系统就会创建一个文件对象,并且在该进程的句柄表中插入这个文件的一个句柄表示该进程对文件的合法访问权限。因为句柄是相对安全的,所以用户程序都是通过句柄来操作文件。文件对象的引用计数也会随着句柄的添加而进行累加,这样就可以记录每个文件有多少进程引用。当用户尝试删除一个文件时,如果文件对象的引用计数不能清为零,即还有其它进程正在使用这个文件,那么系统将不会将其删除且给出文件被占用的提示。

        那么要想删除被占用的文件的解决就非常明朗了,那就是解除其它进程对该文件的引用。所以解决该问题的方法就可以分为两步。

                1 .      找到占用该文件的进程;

                2.      解除该进程对该文件的引用。

        解决方法:

                1.      找到用该文件的进程

                如何找到有哪些进程占用了该文件呢?其实系统提供了相应的接口,不过这些接口都是 undocument 的。这个接口就是

NTSTATUSNTAPI NtQuerySystemInformation (    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,    OUT PVOID SystemInformation,    IN ULONG SystemInformationLength,    OUT PULONG ReturnLength OPTIONAL    );

        该函数的第二个参数传入 0x10 就是查询系统中所有句柄表的意思。它会返回一些系统中所有句柄的一些信息,包括这些句柄的值,以及拥有该句柄的进程的 id。那么如何判断该句柄指向的就是我们所要查询的文件对象呢,接下来还有个接口。

NTSTATUSNTAPINtQueryObject(    __in_opt HANDLE Handle,    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,    __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,    __in ULONG ObjectInformationLength,    __out_opt PULONG ReturnLength    );

        这个接口可以查询本进程中某个句柄的名字,只需要给第二个参数传入 0x01 即可。由于上一个接口中返回的是整个系统中句柄的信息。

                1.      解除该进程对该文件的引用

                NtQueryObject 只能查询本进程的句柄的名字,所以还需一步操作就是,把句柄从其它进程中复制过来,就是下面的接口

BOOL DuplicateHandle(  HANDLE hSourceProcessHandle,  HANDLE hSourceHandle,  HANDLE hTargetProcessHandle,  LPHANDLE lpTargetHandle,  DWORD dwDesiredAccess,  BOOL bInheritHandle,  DWORD dwOptions);

        这个接口可以将其它进程中的句柄复制到本进程中,如果 hSourceProcessHandle 传入 0,dwOptions 传入 DUPLICATE_CLOSE_SOURCE ,就会把句柄从其它进程中清除。

        这样问题就变得简单了,通过查询句柄的名字以确定是否为我们想要得到的文件的对象,然后清除掉目标进程中对该句柄的引用,那么就没有进程会占用我们将要删除的文件了。 

        经过测试,上面的方法是可以解决掉某些文件被其它进程占用的情况的。

        不过,这种方法还是存在一些不足的地方。

                1.      hSourceProcessHandle 必须具备 PROCESS_DUP_HANDLE 的权限;

                2.      该方法对一些数据文件的占用有效,对一些程序文件,如 DLL 的占用会更复杂一些;

                3.      由于第一个接口是未文档化的,也就是操作系统不保证它的稳定和有效性,结果证明,在遍历句柄的时候会出现系统死锁这样的已知 Bug,使得进程无法退出。但可以在内核态安全地完成相同的操作。

        句柄表是 Windows 系统中一个非常重要的概念,它透明地向用户态程序授予了一些内核对象的操作权限,在它背后建立起来的复杂的名字空间机制也给这些对象通过句柄来快速引用提供了可靠的保证。本案例中只使用了一种简单的方法来解决一些通用的问题,还有其它一些更加有效的方法也陆续被一些安全厂商发现并产品化。而种种这些方法的发现,都是建立在对程序的理解之上。所以要想编写出强大稳定的程序,这些机制也是我们必须掌握的。


0 0
原创粉丝点击