- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
How to read output from cmd.exe using CreateProcess() and CreatePipe()
我一直在尝试创建一个子进程来执行 cmd.exe
,命令行指定 /K dir
。目的是使用管道将命令的输出读回父进程。
我已经让 CreateProcess()
正常工作,但是涉及管道的步骤给我带来了麻烦。使用管道,新的控制台窗口不显示(就像以前一样),父进程卡在对 ReadFile()
的调用中。
有人知道我做错了什么吗?
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define BUFFSZ 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
int wmain(int argc, wchar_t* argv[])
{
int result;
wchar_t aCmd[BUFFSZ] = TEXT("/K dir"); // CMD /?
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
printf("Starting...\n");
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
// Create one-way pipe for child process STDOUT
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure read handle to pipe for STDOUT is not inherited
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
// Create one-way pipe for child process STDIN
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0)) {
printf("CreatePipe() error: %ld\n", GetLastError());
}
// Ensure write handle to pipe for STDIN is not inherited
if (!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0)) {
printf("SetHandleInformation() error: %ld\n", GetLastError());
}
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
// Pipe handles are inherited
sa.bInheritHandle = true;
// Creates a child process
result = CreateProcess(
TEXT("C:\\Windows\\System32\\cmd.exe"), // Module
aCmd, // Command-line
NULL, // Process security attributes
NULL, // Primary thread security attributes
true, // Handles are inherited
CREATE_NEW_CONSOLE, // Creation flags
NULL, // Environment (use parent)
NULL, // Current directory (use parent)
&si, // STARTUPINFO pointer
&pi // PROCESS_INFORMATION pointer
);
if (result) {
printf("Child process has been created...\n");
}
else {
printf("Child process could not be created\n");
}
bool bStatus;
CHAR aBuf[BUFFSZ + 1];
DWORD dwRead;
DWORD dwWrite;
// GetStdHandle(STD_OUTPUT_HANDLE)
while (true) {
bStatus = ReadFile(g_hChildStd_OUT_Rd, aBuf, sizeof(aBuf), &dwRead, NULL);
if (!bStatus || dwRead == 0) {
break;
}
aBuf[dwRead] = '\0';
printf("%s\n", aBuf);
}
// Wait until child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("Stopping...\n");
return 0;
}
最佳答案
解决问题的微妙方法是确保关闭不需要的管道末端。
您的父进程有四个句柄:
g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
g_hChildStd_IN_Rd
g_hChildStd_OUT_Wr
╔══════════════════╗ ╔══════════════════╗
║ Parent Process ║ ║ Child Process ║
╠══════════════════╣ ╠══════════════════╣
║ ║ ║ ║
║ g_hChildStd_IN_Wr╟───────────────>║g_hChildStd_IN_Rd ║
║ ║ ║ ║
║g_hChildStd_OUT_Rd║<───────────────╢g_hChildStd_OUT_Wr║
║ ║ ║ ║
╚══════════════════╝ ╚══════════════════╝
您的父进程只需要每个管道的一端:
g_hChildStd_IN_Wr
g_hChildStd_OUT_Rd
启动子进程后:确保关闭不再需要的管道末端:
CloseHandle(g_hChildStd_IN_Rd)
CloseHandle(g_hChildStd_OUT_Wr)
离开:
╔══════════════════╗ ╔══════════════════╗
║ Parent Process ║ ║ Child Process ║
╠══════════════════╣ ╠══════════════════╣
║ ║ ║ ║
║ g_hChildStd_IN_Wr╟───────────────>║ ║
║ ║ ║ ║
║g_hChildStd_OUT_Rd║<───────────────╢ ║
║ ║ ║ ║
╚══════════════════╝ ╚══════════════════╝
或更完整:
STARTUP_INFO si;
PROCESS_INFO pi;
result = CreateProcess(..., ref si, ref pi);
//Bonus chatter: A common bug among a lot of programmers:
// they don't realize they are required to call CloseHandle
// on the two handles placed in PROCESS_INFO.
// That's why you should call ShellExecute - it closes them for you.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
/*
We've given the console app the writable end of the pipe during CreateProcess; we don't need it anymore.
We do keep the handle for the *readable* end of the pipe; as we still need to read from it.
The other reason to close the writable-end handle now is so that there's only one out-standing reference to the writeable end: held by the child process.
When the child processes closes, it will close the pipe, and
your call to ReadFile will fail with error code:
109 (The pipe has been ended).
That's how we'll know the console app is done. (no need to wait on process handles with buggy infinite waits)
*/
CloseHandle(g_hChildStd_OUT_Wr);
g_hChildStd_OUT_Wr = 0;
CloseHandle(g_hChildStd_IN_Rd);
g_hChildStd_OUT_Wr = 0;
大多数解决方案的共同问题是 people try等待进程句柄。
这是错误的。 都错了。
这些想法有很多问题;主要的是:
如果 child 试图通过管道向您发送输出,而您正在INFINITE
等待,那么您并没有清空管道的末端。最终, child 正在写入的管道变满了。当子进程尝试写入已满的管道时,它的 WriteFile
调用会等待(即 block )让管道有一些空间。
因此,子进程将永不终止;你让一切都陷入僵局。
正确的解决方案来自简单地从管道中读取。
CloseHandle
。ERROR_BROKEN_PIPE
)。
String outputText = "";
//Read will return when the buffer is full, or if the pipe on the other end has been broken
while (ReadFile(stdOutRead, aBuf, Length(aBuf), &bytesRead, null)
outputText = outputText + Copy(aBuf, 1, bytesRead);
//ReadFile will either tell us that the pipe has closed, or give us an error
DWORD le = GetLastError;
//And finally cleanup
CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
if (le != ERROR_BROKEN_PIPE) //"The pipe has been ended."
RaiseLastOSError(le);
所有这些都没有危险的 MsgWaitForSingleObject - 它容易出错,难以正确使用,并且会导致您想要避免的错误。
我们都知道我们用它做什么:运行一个子进程,并捕获它的控制台输出。
这是一些示例 Delphi 代码:
function ExecuteAndCaptureOutput(CommandLine: string): string;
var
securityAttributes: TSecurityAttributes;
stdOutRead, stdOutWrite: THandle;
startupInfo: TStartupInfo;
pi: TProcessInformation;
buffer: AnsiString;
bytesRead: DWORD;
bRes: Boolean;
le: DWORD;
begin
{
Execute a child process, and capture it's command line output.
}
Result := '';
securityAttributes.nlength := SizeOf(TSecurityAttributes);
securityAttributes.bInheritHandle := True;
securityAttributes.lpSecurityDescriptor := nil;
if not CreatePipe({var}stdOutRead, {var}stdOutWrite, @securityAttributes, 0) then
RaiseLastOSError;
try
// Set up members of the STARTUPINFO structure.
startupInfo := Default(TStartupInfo);
startupInfo.cb := SizeOf(startupInfo);
// This structure specifies the STDIN and STDOUT handles for redirection.
startupInfo.dwFlags := startupInfo.dwFlags or STARTF_USESTDHANDLES; //The hStdInput, hStdOutput, and hStdError handles will be valid.
startupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE); //don't forget to make it valid (zero is not valid)
startupInfo.hStdOutput := stdOutWrite; //give the console app the writable end of the pipe
startupInfo.hStdError := stdOutWrite; //give the console app the writable end of the pipe
// We also want the console window to be hidden
startupInfo.dwFlags := startupInfo.dwFlags or STARTF_USESHOWWINDOW; //The nShowWindow member member will be valid.
startupInfo.wShowWindow := SW_HIDE; //default is that the console window is visible
// Set up members of the PROCESS_INFORMATION structure.
pi := Default(TProcessInformation);
//WARNING: The Unicode version of CreateProcess can modify the contents of CommandLine.
//Therefore CommandLine cannot point to read-only memory.
//We can ensure it's not read-only with the RTL function UniqueString
UniqueString({var}CommandLine);
bRes := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, nil, startupInfo, {var}pi);
if not bRes then
RaiseLastOSError;
//CreateProcess demands that we close these two populated handles when we're done with them. We're done with them.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
{
We've given the console app the writable end of the pipe during CreateProcess; we don't need it anymore.
We do keep the handle for the *readable* end of the pipe; as we still need to read from it.
The other reason to close the writable-end handle now is so that there's only one out-standing reference to the writeable end: held by the console app.
When the app closes, it will close the pipe, and ReadFile will return code 109 (The pipe has been ended).
That's how we'll know the console app is done. (no need to wait on process handles)
}
CloseHandle(stdOutWrite);
stdOutWrite := 0;
SetLength(buffer, 4096);
//Read will return when the buffer is full, or if the pipe on the other end has been broken
while ReadFile(stdOutRead, buffer[1], Length(buffer), {var}bytesRead, nil) do
Result := Result + string(Copy(buffer, 1, bytesRead));
//ReadFile will either tell us that the pipe has closed, or give us an error
le := GetLastError;
if le <> ERROR_BROKEN_PIPE then //"The pipe has been ended."
RaiseLastOSError(le);
finally
CloseHandle(stdOutRead);
if stdOutWrite <> 0 then
CloseHandle(stdOutWrite);
end;
end;
关于c++ - 如何使用 CreateProcess() 和 CreatePipe() 从 cmd.exe 读取输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35969730/
我正在使用 Android Studio。我想在 github 上分享我的项目,但我遇到了很多问题。 首先,Android studio找不到git.exe。我可以用某种方式解决那个问题,但现在是下一
今天,我刚刚使用 Android SDK 管理器更新了 Windows 7 上的 Android SDK,并在 Eclipse 中构建期间开始出现错误 "Error executing aapt: C
由于某种原因创建进程打开cmd即使未指定按 Enter 键会导致命令行中弹出更多 cmd 实例。 #include #include void main() { PROCESS_INFOR
这个问题已经有答案了: Unhandled Error with CreateProcess [duplicate] (2 个回答) 已关闭 4 年前。 我的目标是在我的程序中执行外部可执行文件。首先
当使用 CreateProcess 运行另一个程序时,捕获标准输出的推荐方法是什么?也就是说,将第二个程序打印到 stdout 的任何内容,并以第一个程序可以分析它的数组结束? 这两个程序都是用 C
这个问题在这里已经有了答案: Unhandled Error with CreateProcess [duplicate] (2 个答案) 关闭 4 年前。 我的目标是在我的程序中执行一个外部可执行
我的以下代码有问题: int main(int argc, char **argv) { PROCESS_INFORMATION pi; STARTUPINFO si; prin
forking() 和 CreateProcess(带有所有必需的参数)对于 Linux 和 WinXP 是否分别是相同的? 如果它们不同,那么有人可以解释两种情况下发生的情况的差异吗? 谢谢 最佳答
我正在寻找 Delphi 中 CreateProcess 的快速替代方案,以在 exe 中执行某些计算,包括 XML 中的多个返回值。目前,我正在调用带有特定参数的 C#-exe。这些电话之一需要大约
我有我的主应用程序,从我的主应用程序我将调用另一个 模块(第三方)在我的主应用程序中执行一个小操作,当我调用该模块时..它处理特定时间说 5 秒。而它的处理它在命令窗口中显示了一些信息..现在我的主应
我注意到了这种行为: 当前目录下有2个可执行文件,分别名为“somefile”和“somefile.abc”。 CreateProcessA(NULL, "somefile", ...) - 失败,错
C 初学者警告!!! 我正在用 c 编写一个应用程序,该应用程序应该在“cmd.exe”上下文中运行用户定义的命令并将输出写入变量。例如,如果命令变量是“dir C:\”,则 c 程序应使用 Crea
我正在尝试使用 CreateProcess() 启动服务器。这是代码: int APIENTRY WinMain(HINSTANCE hInstance, H
所以我使用这段代码来启动带有参数的控制台应用程序: #include #include using namespace std; void StartProgram(char argv[]) {
我刚刚安装了代码块 我在安装时遇到错误(无法在 gcc gnu 中配置的路径中找到编译器可执行文件) 所以我安装了编译器MinGW出现这个错误 错误: *CreateProcess:没有这样的文件或目
我正在尝试弄清楚如何使用 CreateProcess() 函数,但我不太精通 C++。我已经尝试了一些方法来尝试让错误消失,但应用程序似乎没有按照我期望的方式执行。 我想做的是将“cmd.exe/c
我有一个应用程序,用户可以在其中将文件上传到远程服务器,接收该文件的同一台服务器应该运行该应用程序。我正在使用 CreateProcess 方法。问题是,文件目录已经在 std::字符串中定义,我很难
如果程序A被用户以管理员身份执行,程序A是否可以使用CreateProcess() windows函数以管理员权限启动程序B?提前谢谢你 最佳答案 答案是肯定的。如果启动程序以提升的方式运行,您可以以
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 9 年前。 Improve t
我正在尝试在 windows 7 上的 windows visual studio 2012 professional 上使用 visual c++ 打开一个程序。代码将顺利运行,但实际上不会打开程序
我是一名优秀的程序员,十分优秀!