[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[系统相关] 【已解】如何在命令行中使指定文件处于独占状态

我的需求是
在命令行中用任意命令打开指定文件
在该命令执行期间
该文件无法被其它程序复制、改名、删除以及覆盖
类似C:\WINDOWS\system32\config\sam文件的状态

如果上述需求是实现有困难
可以考虑用命令行程序独占该文件
不允许任意程序复制、改名、删除以及覆盖
除非接触独占状态
————————————————————————————
结语

关于这个需求
CrLf巡查付出了不懈的努力
我从中收获良多
虽然最终没有采用这些方案
但是这可求索的过程
让我不虚此问
你若看到此贴
如果也与我有一样的想法
那么请点赞 ^_^
1

评分人数

    • Batcher: 感谢给帖子标题标注[已解决]字样PB + 2
天的白色影子

我的分析都来自于Procmon的监测数据

对应的命令操作如下
  1. Microsoft Windows XP [版本 5.1.2600]
  2. (C) 版权所有 1985-2001 Microsoft Corp.
  3. C:\Documents and Settings\Administrator>d:
  4. D:\>cd test
  5. D:\test>dir
  6. 驱动器 D 中的卷没有标签。
  7. 卷的序列号是 5491-6D80
  8. D:\test 的目录
  9. 2014-04-13  17:15    <DIR>          .
  10. 2014-04-13  17:15    <DIR>          ..
  11. 2014-04-13  17:14                 3 testfile.dat
  12.                1 个文件              3 字节
  13.                2 个目录 15,396,376,576 可用字节
  14. D:\test>type testfile.dat >> testfile.dat
  15. D:\test>type testfile.dat > testfile.dat
  16. D:\test>
复制代码
进一步的测试发现
如果不存在的对应的文件
>预先的CreateFile会失败
自然也没有随后的查询与关闭
天的白色影子

TOP

另外,要实现楼主所谓的“独占状态”,只需用CreateFile函数打开文件,dwShareMode传入0即可。

TOP

>和>>最本质的不同在于调用CreateFile函数时的参数不同。

>的参数:
  1. 002FEF50   00446C10  |FileName = "a.txt"
  2. 002FEF54   40000000  |Access = GENERIC_WRITE
  3. 002FEF58   00000001  |ShareMode = FILE_SHARE_READ
  4. 002FEF5C   002FEF78  |pSecurity = 002FEF78
  5. 002FEF60   00000002  |Mode = CREATE_ALWAYS
  6. 002FEF64   00000080  |Attributes = NORMAL
  7. 002FEF68   00000000  \hTemplateFile = NULL
复制代码
>>的参数:
  1. 0023F148   00326C10  |FileName = "b.txt"
  2. 0023F14C   C0000000  |Access = GENERIC_READ|GENERIC_WRITE
  3. 0023F150   00000001  |ShareMode = FILE_SHARE_READ
  4. 0023F154   0023F170  |pSecurity = 0023F170
  5. 0023F158   00000003  |Mode = OPEN_EXISTING
  6. 0023F15C   00000080  |Attributes = NORMAL
  7. 0023F160   00000000  \hTemplateFile = NULL
复制代码
当然,>>打开文件后还会移动文件指针到文件尾部。

至于你最后说的意外的发现,我调试的时候没有发现,不知道你是如何得出的结论。

TOP

Demon辛苦了
经过的你的分析
受到一点启发
终于比较明白>和>>的不同了

它们最本质的不同在于锁创建文件句柄是Desired Access参数不同
>创建的是Genric Write|Read Attributes模式的文件句柄
它会Overwrite原文件
即对文件长度置0
因此等到type再次打开该文件时
文件内容已被“清零”
>>创建的是Genric Read/Write模式的文件句柄
它会保持文件长度
并移动文件指针到原文件尾部
因此等到type再次打开该文件时
文件内容可以再次读取并写入

可以确认的是
在>和>>打开文件后
一直等待type再次打开并关闭同一文件后
才关闭文件
也就是>和>>并未以所谓“独占”的方式打开文件

另外一点意外的发现
在>正式打开文件之前
会预先打开一次文件查询文件的属性
主要包括长度、分配块大小、链接数等等
然后在关闭文件后再次正式打开
而>>是在正式打开文件的过程中查询该属性的
天的白色影子

TOP

至于用VB6写CLI程序,也不见得有多复杂,有现成的插件可以用,只不过调试起来确实蛋疼。

TOP

还是简单说几句吧。

重定向符号的分析必然优先于前面的语句命令,这是无需争议的。

重定向输出>和>>都会用CreateFile函数打开目标文件,dwDesiredAccess参数为GENERIC_WRITE,dwShareMode参数为FILE_SHARE_READ,所以打开之后文件是只读的,无法写入。(详见批处理技术内幕:重定向与句柄

for /f也会用CreateFile函数打开目标文件,dwDesiredAccess参数为GENERIC_READ,dwShareMode参数为FILE_SHARE_READ | FILE_SHARE_DELETE,如果文件已经在重定向的时候被打开,则CreateFile函数调用会失败。
  1. (for /f %%a in (a.txt) do xxx)>>a.txt
复制代码
代码运行后的错误信息是“系统找不到文件 a.txt。”,与a.txt不存在的错误信息是一样的,但是正确的原因应该是“另一个程序正在使用此文件,进程无法访问。 ”

CMD的错误处理真是渣渣,CreateFile函数失败以后也不调用GetLastError看看是什么原因,直接说系统找不到文件。

至于type命令,用CreateFile函数打开目标文件时,dwDesiredAccess参数为GENERIC_READ,dwShareMode参数为FILE_SHARE_READ | FILE_SHARE_WRITE,所以即使文件已经在重定向的时候被打开,CreateFile函数调用也不会失败。

可以用C程序验证如下:
  1. #include <stdio.h>
  2. #include <windows.h>
  3. // by Demon
  4. // http://demon.tw
  5. int main()
  6. {
  7.     HANDLE hFile;
  8.     hFile = CreateFile(
  9.         "a.txt",
  10.         GENERIC_WRITE,
  11.         // 重定向的dwShareMode
  12.         FILE_SHARE_READ,
  13.         NULL,
  14.         CREATE_ALWAYS,
  15.         FILE_ATTRIBUTE_NORMAL,
  16.         NULL);
  17.     if (hFile == INVALID_HANDLE_VALUE) {
  18.         printf("1: %d\n", GetLastError());
  19.     }
  20.     hFile = CreateFile(
  21.         "a.txt",
  22.         GENERIC_READ,
  23.         // FOR /F命令的dwShareMode
  24.         FILE_SHARE_READ | FILE_SHARE_DELETE,
  25.         NULL,
  26.         OPEN_EXISTING,
  27.         FILE_ATTRIBUTE_NORMAL,
  28.         NULL);
  29.     if (hFile == INVALID_HANDLE_VALUE) {
  30.         printf("2: %d\n", GetLastError());
  31.     }
  32.     hFile = CreateFile(
  33.         "a.txt",
  34.         GENERIC_READ,
  35.         // TYPE命令的dwShareMode
  36.         FILE_SHARE_READ | FILE_SHARE_WRITE,
  37.         NULL,
  38.         OPEN_EXISTING,
  39.         FILE_ATTRIBUTE_NORMAL,
  40.         NULL);
  41.     if (hFile == INVALID_HANDLE_VALUE) {
  42.         printf("3: %d\n", GetLastError());
  43.     }
  44.     return 0;
  45. }
复制代码

TOP

我也来谈谈:
1 首先我想到了ntfs权限法,用bat实现也简单。取消权限,pause,还原权限。
2 霸占文件法,诸位已经发了很多了,哪个灵我也没细看,最好总结出来。霸占文件还有一法,就是内存映射文件。这不是swap。 。net有此类,和方法。我的理解是内存映射文件期间,无法用file相关的io函数来读写,来复制。
3其他用户法。恐怕要结合上面2法用。即把ntfs权限搞成其他用户,pause,在搞回来。让其他用户霸占文件的打开句柄。这样我就或许不能读写。
4等级用户法。意为我用system账户霸占一个文件的打开句柄,administrator 就不能读写,复制。
5底层注入过滤法。


不论你怎么搞,大方向也就这些。

1不是啥东西都要拿出代码来,
2当然你也可以拿出代码,但是若不灵,或者不太灵,然后你又换,那就难免 破铜烂铁,一堆堆,。。。当然你本意是好地,赞你的本意。
3奇技淫巧+代码 绕来绕去把大家弄蒙了,推上了歧路,饶了弯子,还真不如不看代码,看【大方向语言描述】。
脚本是写给人看的,是写给用户看的,而不是写给机子看的
用户能看懂、会修改的脚本,才是好脚本。
写易懂的powershell脚本帮人解决问题,进而让用户学会自渔,吾所愿也

TOP

回复 9# Demon


    又一枚大神粗线!真是良辰吉日

TOP

我就看看,不说话。
1

评分人数

    • CrLf: 我就加分,不说话技术 + 1

TOP

回复 6# CrLf
根据以往的分析结果
重定向符号的分析必然优先于前面的语句命令
这也是type test.txt>test.txt会清空内容的本质原因
而不含()的for /f也与这个原则并不悖逆
因为可以认为那个>位于for/f的“子句”中
它仍然优先于子句的命令被解析了
但是type test.txt>>test.txt的结果
与上面这个原则似乎存在矛盾
这其中一定有什么我还不清楚的逻辑
看起来>>没有想象中的那样简单

lock.exe的CLI化我已经放弃了
希望有别人有这个兴趣吧
关于准确结束占用的话题
你想的可能有些复杂了
可以把lock改成一个指定的名字
比如lock_testfile.exe
然后再用它锁住文件
lock_testfile.exe testfile.txt
结束时只需要按进程名kill就可以了
我之前就是这样设想的

不过还是觉得杀进程太暴力了
而且为了一个并非主体的需求
消耗一个第三方程序代价有些高了
我回头由重新梳理了一下我的需求出发点

我只是需要在我的代码执行的某个周期内
某个指定的文件不会被其他程序有意无意的读写而已
于是我重新想到了一个不需使用文件占用的方法
这个方法也是收到7楼的Spring上尉的启发

就是修改文件的NTFS权限禁止当前用户读写
有了思路代码其实很简单了
  1. ::锁定的
  2. cacls testfile.txt /e /d %userdomain%\%username%
  3. ::解锁的
  4. cacls testfile.txt /e /p %userdomain%\%username%:f
复制代码
虽然代码并不十分完美和通用
但是在我所应用的情境下
这段代码完全可以胜任了

在此非常感谢两位兄弟
我的问题至此已经了结了
虽然留下一些未竟的遗憾
但是——
有时候就是这样
理论需要为实践做出让步
理想需要为现实做出妥协

不过——
如果大家有兴趣
还可以就文中的一些话题延伸讨论
我有时间也会尽可能关注的
1

评分人数

    • CrLf: “理想需要为现实做出妥协”技术 + 1
天的白色影子

TOP

本帖最后由 Spring 于 2014-4-10 19:50 编辑

我之前也试过,好像不能达到无法读取的目的,
要想不被修改,比如删除、重命名、追加内容之类的,好多方式都可以,原理可能都是一样的,只要占住这个文件就行了。
试了好些,发现下面这样可以让文件不被修改,而且执行之后不会对原文件内容造成影响,并且要结束只需要按回车就行了
  1. set/p=>>a.txt
复制代码
2

评分人数

    • CrLf: 狂欢~技术 + 1
    • qzwqzw: 茫茫人海难得同好PB + 4 技术 + 1

TOP

本帖最后由 CrLf 于 2014-4-10 10:34 编辑

回复 5# qzwqzw
  1. for /f %%a in (a.txt) do echo %%a>>a.txt
复制代码
逻辑上 for /f 是先把 a.txt 读入内存再 >>a.txt 的,证据:
  1. for /f %%a in (a.txt) do echo %%a>a.txt
复制代码
这样也能读到 a.txt,for /f 读入文件和句柄占用的先后次序就很清楚了
----------------------------------------------------------------------------------
确实是个没窗口的gui,话说我试着用 Me.Caption = Argv(1) 来将窗口标题修改为正在锁定的文件路径,以便 taskkill /f /im /fi "windowtitle eq 路径" 来准确结束占用,但实测中 tasklist 不知道为什么读不到 form 的标题,why?!
----------------------------------------------------------------------------------
     vb6 写 cli 挺蛋疼,用过两种方案:
  1. 1、完全编译成 cli,要把 link.exe 替换掉,需要的话我发给你。此方案进程和界面貌似得同生共死。
  2. 2、中途可以创建、退出命令行界面,进程可以在退出界面后继续运行,参考:http://hi.baidu.com/console_app。不过这个方案比较繁琐,要附加在父进程窗口下还要多加几行代码,而且设置退出码需要 terminalprocess,所以好像无法通过退出码传出 pid 后仍保持运行。
复制代码
----------------------------------------------------------------------------------
     试过改后缀名仍被墙,感觉单位防火墙是过滤文件头的,奇葩得很。本来想把所有字节 +1,但后来还是觉得用 gethex.exe 更省事(俺自写的命令行工具~)...反正楼主肯定能还原
1

评分人数

    • qzwqzw: 这是加给你们单位的“防火墙”的技术 + 1

TOP

CrLf的敬业让我佩服啊!

for /f改成下面这样就内容加倍了
  1. for /f %%a in (a.txt) do echo %%a>>a.txt
复制代码
4>>%0 是为了保证脚本在该目录下运行时不会被删。

从上下文看这个脚本肯定是在for /r的根上
如果这个目录树下与脚本同名的文件组
那脚本必然是第一个枚举并被占用保护的啊
不过为了脚本通用性这样设计也可以理解

那个vb程序应该不算是纯CLI程序
不过管它GUI,CLI
达到目的就行
要想改成CLI有难度
关键是程序退出前怎么处理打开的文件句柄
我再想想吧
也许有人可以帮忙

不过下次上传别绕这么多弯好不好
不让rar上传那直接改后缀名不行吗?
天的白色影子

TOP

1、呃,没细看顶楼描述,我好像又跑题了...
for /f 会受影响,但 type 和 find 等命令都能使用,不晓得为什么:
  1. (for /f %%a in (a.txt) do xxx)>>a.txt
复制代码
2、4>>%0 是为了保证脚本在该目录下运行时不会被删。
3、那个 l 确系笔误。
4、百度到一个 function OpenFile(const lpFileName: LPCSTR; var lpReOpenBuff: TOFStruct; uStyle: UINT): HFILE; stdcall;
UStyle 有这两个常量,不知道是不是楼主说的那种:
  1. Of_Share_Deny_Read
  2. 禁止其它程序读该文件
  3. Of_Share_Exclusive
  4. 独占方式打开文件,其它程序不得再打开该文件
复制代码
---------------------------------------------------------------------------------------------
妈蛋,加班熬夜干活
休息的时候拼拼凑凑了一下,vb6 折腾了个,可实现楼主要求
把源文件与 exe 打包了一份,受网络限制,rar 上传不了,gethex 了一份,qzw 要是解压不了一定是假冒的:
原来把 rar 加密就可以了,这是压缩包,密码 bathome:
[attach]7157[/attach]
  1. lock.exe File [wStyle]
  2.         wStyle = exclusive|read|write
复制代码
不干掉进程就一直 lock 着,源文件没啥注释但不复杂,楼主有兴趣用 c 写个完整的呗,坐等开源
1

评分人数

    • qzwqzw: 为了逝去的熊猫眼和回笼觉技术 + 1

TOP

返回列表