【问题标题】:Need to run .exe file for 7 seconds and then kill the exe while performing input and output in text files需要运行 .exe 文件 7 秒,然后在文本文件中执行输入和输出时杀死 exe
【发布时间】:2015-06-05 14:22:03
【问题描述】:

我想创建一个批处理文件,它运行一个程序 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

然后从文件输入和在文件中输出不起作用。

【问题讨论】:

  • This 看起来非常相似 - 当前的问题是您正在为start 重定向标准输入/标准输出,而不是为start 正在执行的程序重定向
  • 试试:start "" "program.exe &lt; in.txt &gt; out.txt"
  • @eryksun 您应该发布这两个 cmets 作为答案。当你这样做时标记我,我会投票。

标签: windows batch-file cmd


【解决方案1】:

cmd 没有设置 CreateProcess STARTUPINFO 结构的 STARTF_USESTDHANDLES 标志。相反,它会暂时重定向自己的标准句柄并依赖继承。即使 cmd 必须调用 ShellExecuteEx,这种方法也有效,因为它缺少显式设置标准句柄的方法。

但是,如果在进程创建标志中设置了CREATE_NEW_CONSOLE(这是start 命令的默认设置),则重定向其自己的标准句柄不起作用。要避免此问题,请使用/b 选项来防止创建新控制台。

您可能还想将 stderr 重定向到 stdout 或文件。这可以防止错误被写入控制台。例如:

  • start /b program.exe &lt;in.txt &gt;out.txt 2&gt;&amp;1
  • start /b program.exe &lt;in.txt &gt;out.txt 2&gt;err.txt
  • start /b program.exe &lt;in.txt &gt;out.txt 2&gt;nul

使用Debugging Tools for Windows的示例

(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 在调用CreateProcess 之前会重定向其StandardOutput

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 传递,这是以下标志的按位或:

  • 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 => 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

这是以下创建标志的按位或:

  • 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 的重定向使用读取 (&lt;) 或写入 (&gt;),但不能同时使用两者。以上是重定向的输出,您可以看到文件以写入和附加访问权限(2+4)打开,但没有读取访问权限。这是一个快速检查,但如果您想确定可以使用内核调试器(如 kd.exe)或工具(如 Sysinternals Process Explorer 或 handle.exe)。这些可以向您显示 NT 内核对象路径,例如 Windows 8+ 控制台输入句柄的\Device\ConDrv\Input

【讨论】:

  • 嗨。我很想知道 cmd.exe 如何管理放置在“链”中的几个标准句柄的重定向的详细信息,就像this post 中描述的那样;这些知识将使我们能够使用基于真实数据的此类功能,而不是我们现在使用的试错法。我查看了您链接中描述的 Windows 调试工具,但这些工具巨大!不知道您是否有兴趣帮助我掌握 cmd.exe 的这一特殊功能的运行机制。感谢您的关注! :-)
  • @Aacini,0 (stdin)、1 (stdout) 和 2 (stderr) 等文件编号不是 Windows 句柄。它们是 C 运行时的低 I/O 文件描述符。每个都与一个 Windows 句柄相关联。请参阅 _dup and _dup2_open_osfhandle 的文档。像1&gt;&amp;2这样的重定向基本上会调用_dup(1)(保存到命令返回后恢复)、_close(1)_dup2(2, 1)
  • @Aacini,注意_dup_dup2 调用Windows DuplicateHandle 来复制底层Windows 文件句柄。而_open_osfhandle在分配文件描述符0、1或2时调用SetStdHandle,分别对应STD_INPUT_HANDLESTD_OUTPUT_HANDLESTD_ERROR_HANDLE
  • 是的。我知道这个重定向1&gt;&amp;2是通过第一个_dup(1)实现的,并保存返回的数字,但是假设_dup(1)返回的CRT文件描述符是下一个可用的数字,就像在DuplicateHandle函数中发生的那样旧的 MS-DOS INT 21H,即 3。如果完整的重定向是1&gt;&amp;2 3&gt;&amp;1,文件号3 是否也通过_dup(3) 复制?是否将此文件编号 3 引用到 _dup(1) 先前返回的相同的文件描述符?如您所见,如果我们引入更多重定向和/或更改它们的顺序,最终结果可能会非常复杂!
  • @Aacini,调试重定向 1&gt;null 3&gt;out.txt 的命令,并在 msvcrt!dupmsvcrt!dup2msvcrt!closemsvcrt!open_osfhandle 上设置断点。顺序为dup(1) -> 3, close(1), open_osfhandle(hNull) -> 1, dup(3) -> 4, close(3), open_osfhandle(hOut) -> 3。命令运行,然后保存的文件描述符为已恢复:dup2(3, 1)close(3)dup2(4, 3)close(4)。这是一个问题。恢复顺序有什么搞笑错误?
猜你喜欢
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多