我想创建一个运行程序7秒的批处理文件,而不管其他程序的执行是否完成。我还希望程序应该输入并将输出保存在外部文件中。这就是我试过的:
start program.exe
ping 1.1.1.1 -n 1 -w 7000 > nul
taskkill /im program.exe /f
rem continue here
以上工作正常,但当我用以下代码替换第1行时
start program.exe < in.txt > out.txt
然后从文件输入和文件中的输出都不起作用。
答案 0 :(得分:3)
cmd未设置STARTF_USESTDHANDLES
CreateProcess
结构的STARTUPINFO
标记。相反,它暂时重定向自己的标准句柄并依赖继承。即使cmd必须调用ShellExecuteEx
,这种方法仍然有效,因为它缺少明确设置标准句柄的方法。
但是,在进程创建标志中设置CREATE_NEW_CONSOLE
时,重定向自己的标准句柄不起作用,这是start
命令的默认值。要避免此问题,请使用/b
选项阻止创建新控制台。
您可能还想将stderr重定向到stdout或文件。这可以防止将错误写入控制台。例如:
start /b program.exe <in.txt >out.txt 2>&1
start /b program.exe <in.txt >out.txt 2>err.txt
start /b program.exe <in.txt >out.txt 2>nul
(test) C:\>cdb -Goxi ld cmd
Microsoft (R) Windows Debugger Version 6.12.0002.633 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: cmd
Symbol search path is: symsrv*symsrv.dll*
C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
(ed0.1770): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
0:000> .reload /f
Reloading current modules
.....
0:000> bp CreateProcessW
0:000> g
在新控制台中运行where.exe。
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
(test) C:\>start /w where.exe <nul >nul
Breakpoint 0 hit
kernel32!CreateProcessW:
00000000`775a0660 4883ec68 sub rsp,68h
请注意,cmd.exe会在调用StandardOutput
之前重定向其CreateProcess
:
0:000> ?? ((ntdll!_PEB *)@$peb)->ProcessParameters->StandardOutput
void * 0x00000000`00000060
0:000> !handle 60 3
Handle 60
Type File
Attributes 0
GrantedAccess 0x120196:
ReadControl,Synch
Write/Add,Append/SubDir/CreatePipe,WriteEA,ReadAttr,WriteAttr
HandleCount 2
PointerCount 3
进程创建标志,即dwCreationFlags
,第6个参数:
0:000> dd (@rsp + 6*8) l1
00000000`00182c58 00080410
传递为0x80410,它是以下标志的按位OR:
EXTENDED_STARTUPINFO_PRESENT
CREATE_UNICODE_ENVIRONMENT
CREATE_NEW_CONSOLE
因为创建了一个新的控制台,where.exe不会继承cmd的标准句柄:
0:000> g
Symbol search path is: symsrv*symsrv.dll*
C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
(1550.1a80): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
1:001> ?? ((ntdll!_PEB *)@$peb)->ProcessParameters->StandardOutput
void * 0x00000000`00000007
注意:在Windows 8+中,控制台句柄只是一个常规文件句柄,所以你必须要更深入。
我在这个例子中使用的是Windows 7,因此控制台句柄是通过设置低2位标记的伪句柄(例如3,7,11 =&gt; 0b0011,0b0111,0b1011)。 '假'我的意思是它们不在用于内核对象句柄的进程句柄表中。因此,例如,您无法使用调试器!handle
命令来检查句柄7:
1:001> !handle 7 f
Could not duplicate handle 7, error 87
在Windows 7中,控制台句柄由控制台主机进程conhost.exe分配和管理。它们被标记,因此Windows基本功能可以通过NtRequestWaitReplyPort
对conhost.exe进行必要的LPC调用。
上面的示例演示了如何创建新控制台来覆盖继承cmd的重定向标准句柄。现在让我们添加/b
选项以防止创建新控制台。
1:001> g
(test) C:\>start /b /w where.exe <nul >nul
Breakpoint 0 hit
kernel32!CreateProcessW:
00000000`775a0660 4883ec68 sub rsp,68h
dwCreationFlags
是0x80600:
0:000> dd (@rsp + 6*8) l1
00000000`00182c58 00080600
是以下创建标志的按位OR:
EXTENDED_STARTUPINFO_PRESENT
CREATE_UNICODE_ENVIRONMENT
CREATE_NEW_PROCESS_GROUP
(指定/b
的副作用是将进程创建为新进程组的领导者。如果是控制台进程,则允许生成以组为目标的Ctrl + Break事件。)
在这种情况下,where.exe会从cmd.exe继承重定向的标准句柄:
0:000> g
Symbol search path is: symsrv*symsrv.dll*
C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
(1508.1534): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`77848700 cc int 3
1:001> ?? ((ntdll!_PEB *)@$peb)->ProcessParameters->StandardOutput
void * 0x00000000`00000064
1:001> !handle 64 3
Handle 64
Type File
Attributes 0
GrantedAccess 0x120196:
ReadControl,Synch
Write/Add,Append/SubDir/CreatePipe,WriteEA,ReadAttr,WriteAttr
HandleCount 3
PointerCount 4
同样,在Windows 7中,很容易发现控制台伪句柄,因为它通过设置句柄值的低2位来标记。对于Windows 8+,快速检查是查看文件被授予访问权限的低半字节(4位),读取数据访问权限为1,写入数据访问权限为2,附加数据访问权限为4。控制台缓冲区具有读写访问权限,而cmd的重定向使用read(<
)或write(>
),但不能同时使用。以上是重定向输出,您可以看到文件是通过写入和追加访问(2 + 4)打开的,但不是读取访问权限。这是一个快速检查,但如果你想确定你可以使用内核调试器,如kd.exe,或Sysinternals Process Explorer或handle.exe等工具。这些可以显示NT内核对象路径,例如Windows {8}控制台输入句柄的\Device\ConDrv\Input
。