- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
好的,所以我的基本任务是创建一个函数来运行一个进程并从中获取控制台输出(stdout 和 stderr),并可能指定创建的进程执行超时。
第一种方法是创建临时文件(使用FILE_FLAG_DELETE_ON_CLOSE
)并将其指定为hStdOutput/hStdError
for STARTUPINFO
,等待处理并读取文件的内容。这很好用。但我不喜欢创建临时文件的想法。我从 msdn 中找到了使用管道重定向控制台输出的示例(请参阅 here)。这是重写/修复的代码(作为测试的命令行工具):
#include <Windows.h>
#include <tchar.h>
#include <cassert>
#include <string>
#include <stdexcept>
#include <memory>
#include <type_traits>
#include <iostream>
using string_t = std::basic_string<TCHAR>;
#define THROW_E(X) throw std::runtime_error{(X) + std::string(": ") + std::to_string(::GetLastError())};
#define THROW(X) throw std::runtime_error{(X)};
namespace {
// From
// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
void ArgvQuote(
const string_t& Argument,
string_t& CommandLine,
bool Force)
{
if (Force == false &&
Argument.empty () == false &&
Argument.find_first_of (_T(" \t\n\v\"")) == Argument.npos)
{
CommandLine.append (Argument);
}
else {
CommandLine.push_back (_T('"'));
for (auto It = Argument.begin () ; ; ++It) {
unsigned NumberBackslashes = 0;
while (It != Argument.end () && *It == _T('\\')) {
++It;
++NumberBackslashes;
}
if (It == Argument.end ()) {
CommandLine.append (NumberBackslashes * 2, _T('\\'));
break;
}
else if (*It == _T('"')) {
CommandLine.append (NumberBackslashes * 2 + 1, _T('\\'));
CommandLine.push_back (*It);
}
else {
CommandLine.append (NumberBackslashes, _T('\\'));
CommandLine.push_back (*It);
}
}
CommandLine.push_back (_T('"'));
}
}
using handle_ptr = std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)>;
handle_ptr CreateHandlePtr(HANDLE h)
{
return handle_ptr{h, &::CloseHandle};
}
handle_ptr CreateChildProcess(string_t cmd_line, handle_ptr&& std_out, const string_t& dir)
{
assert(!cmd_line.empty());
assert(std_out.get());
PROCESS_INFORMATION pi = {};
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.hStdError = std_out.get();
si.hStdOutput = std_out.get();
si.wShowWindow = SW_HIDE;
si.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
LPTSTR p_cmd_line = &cmd_line[0];
auto created = CreateProcess(NULL,
p_cmd_line,
nullptr,
nullptr,
TRUE, // Handles are inherited
CREATE_NEW_CONSOLE,
NULL, // Use parent's environment
dir.empty() ? nullptr : dir.c_str(),
&si,
&pi);
if(!created)
THROW_E("CreateProcess()");
std_out.reset();
::CloseHandle(pi.hThread);
return CreateHandlePtr(pi.hProcess);
}
std::string ReadFromPipe(handle_ptr&& std_out)
{
std::string content;
DWORD read = 0;
CHAR buf[1 * 1024] = {};
BOOL success = FALSE;
for(;;)
{
success = ::ReadFile(std_out.get(), buf, _countof(buf), &read, nullptr);
if(!success && (::GetLastError() != ERROR_BROKEN_PIPE))
THROW_E("ReadFile()");
if(!success || (read == 0))
break;
content.append(buf, read);
}
return content;
}
} // namespace
std::string ConsoleOutFromExec(const string_t& cmd_line, std::size_t timeout_msecs = INFINITE,
const string_t& directory = string_t{})
{
auto child_stdout_r = CreateHandlePtr(nullptr);
auto child_stdout_w = CreateHandlePtr(nullptr);
{
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = nullptr;
HANDLE raw_child_stdout_r = nullptr;
HANDLE raw_child_stdout_w = nullptr;
if(!::CreatePipe(&raw_child_stdout_r, &raw_child_stdout_w, &sa, 0))
THROW_E("CreatePipe()");
child_stdout_r = CreateHandlePtr(raw_child_stdout_r);
child_stdout_w = CreateHandlePtr(raw_child_stdout_w);
if(!::SetHandleInformation(child_stdout_r.get(), HANDLE_FLAG_INHERIT, 0))
THROW_E("SetHandleInformation()");
}
auto child_proc = CreateChildProcess(cmd_line, std::move(child_stdout_w), directory);
auto status = ::WaitForSingleObject(child_proc.get(), timeout_msecs);
switch(status)
{
case WAIT_OBJECT_0:
break;
default:
THROW("WaitForSingleObject()");
}
return ReadFromPipe(std::move(child_stdout_r));
}
std::size_t ParseTimeout(const string_t& str)
{
std::size_t timeout = INFINITE;
if(str == _T("INFINITE"))
return timeout;
auto p_begin = str.c_str();
TCHAR* p_end = nullptr;
timeout = static_cast<std::size_t>(_tcstol(p_begin, &p_end, 10));
if((timeout == 0) || !p_end || (*p_end != _T('\0')))
THROW("Invalid timeout string");
return timeout;
}
int _tmain(int argc, TCHAR* argv[])
{
if(argc <= 2)
{
std::cout << "Invalid command line:" << "\n";
std::cout << "\t" << "<timeout msecs> (or INFINITE) <exe> <arg0> <arg1> ..." << "\n";
return EXIT_FAILURE;
}
try {
std::size_t timeout = ParseTimeout(argv[1]);
string_t cmd_line;
if(_tcslen(argv[2]) == 0)
THROW("Executable path is empty");
for(int i = 2; i < argc; ++i)
{
#if(1)
ArgvQuote(argv[i], cmd_line, true);
#else
cmd_line.append(argv[i]);
#endif
if(i < (argc - 1))
cmd_line.push_back(_T(' '));
}
auto content = ConsoleOutFromExec(cmd_line, timeout);
std::cout << content << "\n";
} catch(const std::exception& e) {
std::cout << "EXCEPTION: " << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
如您所见,没有什么新内容(与上面的链接相比),只是使用 RAII 以获得更清晰的代码并且没有标准输入重定向(ArgvQuote()
来自 this link)。
接下来,如果我要运行:
child_read.exe INFINITE cmd.exe/c whoami
child_read.exe INFINITE cmd.exe/c ipconfig
一切正常。但是下一个命令:
child_read.exe INFINITE cmd.exe/c ipconfig/all
将卡住执行。调试器显示问题是 ::WaitForSingleObject()
永远不会返回。而且,实际上,ipconfig
进程处于事件状态:
Windows 8.1 及更高版本会发生这种情况,但在 Windows 7 上一切正常,无需 ipconfig
卡住!!为什么 ?顺便说一句,如果我删除等待进程并开始从管道读取 - 一切正常,我将有应用程序输出(但我需要有执行超时)!
有人可以解释为什么会这样吗?这是仅与 ipconfig/all
命令有关,还是我的代码有问题,这也可能发生在任何其他应用程序上?
(如果 stdin 也被重定向,我在 google/stackoverflow 中发现的所有内容都与卡住有关...)
最佳答案
非常感谢@Cheers 和 hth。 - 阿尔夫。正如他在评论中所说:
You need to replace the infinite wait wait a wait-and-read loop. A pipe doesn't have an infinite buffer. –
为了避免::ReadFile()
在子进程控制台上没有数据时阻塞,需要使用PeekNamedPipe()
。函数,它将告诉我们管道中有多少字节可用(在我们的例子中是子进程控制台)。因此,这是我的问题中的固定 ReadFromPipe()
函数:
std::string ReadFromPipe(
handle_ptr&& std_out, // handle to child console
handle_ptr&& wait_on, // handle to child process
std::size_t timeout_msecs,
bool* timeout)
{
std::string content;
DWORD read = 0;
DWORD bytes_available = 0;
DWORD total_read = 0;
CHAR buf[1 * 1024] = {};
BOOL done = FALSE;
std::size_t current_waittime = 0;
while(!done)
{
bytes_available = static_cast<DWORD>(-1);
switch(::WaitForSingleObject(wait_on.get(), 1))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
if(!::PeekNamedPipe(std_out.get(), nullptr, 0, nullptr, &bytes_available, nullptr) &&
(::GetLastError() != ERROR_BROKEN_PIPE))
THROW_E("PeekNamedPipe()");
break;
}
total_read = 0;
while(total_read < bytes_available)
{
DWORD count = (std::min)(static_cast<DWORD>(_countof(buf)), static_cast<DWORD>(bytes_available - total_read));
if(!::ReadFile(std_out.get(), buf, count, &read, nullptr) &&
(::GetLastError() != ERROR_BROKEN_PIPE))
THROW_E("ReadFile()");
else if(read == 0)
{
done = TRUE;
break;
}
content.append(buf, read);
total_read += read;
}
if(++current_waittime >= timeout_msecs)
{
*timeout = true;
content.clear();
break;
}
}
return content;
}
这里是所有代码:
#include <Windows.h>
#include <tchar.h>
#include <cassert>
#include <string>
#include <stdexcept>
#include <memory>
#include <type_traits>
#include <iostream>
#include <algorithm>
using string_t = std::basic_string<TCHAR>;
#define THROW_E(X) THROW((X) + std::string(": ") + std::to_string(::GetLastError()))
#define THROW(X) throw std::runtime_error{(X)}
namespace {
using handle_ptr = std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::CloseHandle)>;
handle_ptr CreateHandlePtr(HANDLE h)
{
return handle_ptr{h, &::CloseHandle};
}
handle_ptr CreateChildProcess(string_t cmd_line, handle_ptr&& std_out, const string_t& dir)
{
assert(!cmd_line.empty());
assert(std_out.get());
PROCESS_INFORMATION pi = {};
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.hStdError = std_out.get();
si.hStdOutput = std_out.get();
si.wShowWindow = SW_HIDE;
si.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
LPTSTR p_cmd_line = &cmd_line[0];
auto created = CreateProcess(NULL,
p_cmd_line,
nullptr,
nullptr,
TRUE, // Handles are inherited
CREATE_NEW_CONSOLE,
NULL, // Use parent's environment
dir.empty() ? nullptr : dir.c_str(),
&si,
&pi);
if(!created)
THROW_E("CreateProcess()");
std_out.reset();
::CloseHandle(pi.hThread);
return CreateHandlePtr(pi.hProcess);
}
std::string ReadFromPipe(
handle_ptr&& std_out, // handle to child console
handle_ptr&& wait_on, // handle to child process
std::size_t timeout_msecs,
bool* timeout)
{
std::string content;
DWORD read = 0;
DWORD bytes_available = 0;
DWORD total_read = 0;
CHAR buf[1 * 1024] = {};
BOOL done = FALSE;
std::size_t current_waittime = 0;
while(!done)
{
bytes_available = static_cast<DWORD>(-1);
switch(::WaitForSingleObject(wait_on.get(), 1))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
if(!::PeekNamedPipe(std_out.get(), nullptr, 0, nullptr, &bytes_available, nullptr) &&
(::GetLastError() != ERROR_BROKEN_PIPE))
THROW_E("PeekNamedPipe()");
break;
}
total_read = 0;
while(total_read < bytes_available)
{
DWORD count = (std::min)(static_cast<DWORD>(_countof(buf)), static_cast<DWORD>(bytes_available - total_read));
if(!::ReadFile(std_out.get(), buf, count, &read, nullptr) &&
(::GetLastError() != ERROR_BROKEN_PIPE))
THROW_E("ReadFile()");
else if(read == 0)
{
done = TRUE;
break;
}
content.append(buf, read);
total_read += read;
}
if(++current_waittime >= timeout_msecs)
{
*timeout = true;
content.clear();
break;
}
}
return content;
}
} // namespace
std::string ConsoleOutFromExec(const string_t& cmd_line, std::size_t timeout_msecs = INFINITE,
const string_t& directory = string_t{})
{
auto child_stdout_r = CreateHandlePtr(nullptr);
auto child_stdout_w = CreateHandlePtr(nullptr);
{
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = nullptr;
HANDLE raw_child_stdout_r = nullptr;
HANDLE raw_child_stdout_w = nullptr;
if(!::CreatePipe(&raw_child_stdout_r, &raw_child_stdout_w, &sa, 0))
THROW_E("CreatePipe()");
child_stdout_r = CreateHandlePtr(raw_child_stdout_r);
child_stdout_w = CreateHandlePtr(raw_child_stdout_w);
if(!::SetHandleInformation(child_stdout_r.get(), HANDLE_FLAG_INHERIT, 0))
THROW_E("SetHandleInformation()");
}
auto child_proc = CreateChildProcess(cmd_line, std::move(child_stdout_w), directory);
bool timeout = false;
auto content = ReadFromPipe(std::move(child_stdout_r), std::move(child_proc), timeout_msecs, &timeout);
if(timeout)
THROW("Execution timeout");
return content;
}
std::size_t ParseTimeout(const string_t& str)
{
std::size_t timeout = INFINITE;
if(str == _T("INFINITE"))
return timeout;
auto p_begin = str.c_str();
TCHAR* p_end = nullptr;
timeout = static_cast<std::size_t>(_tcstol(p_begin, &p_end, 10));
if((timeout == 0) || !p_end || (*p_end != _T('\0')))
THROW("Invalid timeout string");
return timeout;
}
int _tmain(int argc, TCHAR* argv[])
{
if(argc <= 2)
{
std::cout << "Invalid command line:" << "\n";
std::cout << "\t" << "<timeout msecs> (or INFINITE) <exe> <arg0> <arg1> ..." << "\n";
return EXIT_FAILURE;
}
try {
std::size_t timeout = ParseTimeout(argv[1]);
string_t cmd_line;
if(_tcslen(argv[2]) == 0)
THROW("Executable path is empty");
for(int i = 2; i < argc; ++i)
{
// TODO: quote arguments correctly
cmd_line.append(argv[i]);
if(i < (argc - 1))
cmd_line.push_back(_T(' '));
}
auto content = ConsoleOutFromExec(cmd_line, timeout);
std::cout << content << "\n";
} catch(const std::exception& e) {
std::cout << "EXCEPTION: " << e.what() << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
再次,非常感谢@Cheers 和 hth。 - 阿尔夫的帮助!
关于c++ - 在某些情况下重定向子进程的 STDOUT 时应用程序卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32562909/
我是 Linux 的新手,并且继承了保持我们的单一 Linux 服务器运行的职责。这是我们的SVN服务器,所以比较重要。 原来在我之前维护它的人有一个 cron 任务,当有太多 svnserve 进程
Node 虽然自身存在多个线程,但是运行在 v8 上的 JavaScript 是单线程的。Node 的 child_process 模块用于创建子进程,我们可以通过子进程充分利用 CPU。范例:
Jenkins 有这么多进程处于事件状态是否正常? 我检查了我的设置,我只配置了 2 个“执行者”... htop http://d.pr/i/RZzG+ 最佳答案 您不仅要限制 Master 中的执
我正在尝试在 scala 中运行这样的 bash 命令: cat "example file.txt" | grep abc Scala 有一个特殊的流程管道语法,所以这是我的第一个方法: val f
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我需要一些帮助来理解并发编程的基础知识。事实上,我读得越多,就越感到困惑。因此,我理解进程是顺序执行的程序的一个实例,并且它可以由一个或多个线程组成。在单核CPU中,一次只能执行一个线程,而在多核CP
我的问题是在上一次集成测试后服务器进程没有关闭。 在integration.rs中,我有: lazy_static! { static ref SERVER: Arc> = {
我正在使用 Scala scala.sys.process图书馆。 我知道我可以用 ! 捕获退出代码和输出 !!但是如果我想同时捕获两者呢? 我看过这个答案 https://stackoverflow
我正在开发一个C++类(MyClass.cpp),将其编译为动态共享库(MyClass.so)。 同一台Linux计算机上运行的两个不同应用程序将使用此共享库。 它们是两个不同的应用程序。它不是多线程
我在我的 C 程序中使用 recvfrom() 从多个客户端接收 UDP 数据包,这些客户端可以使用自定义用户名登录。一旦他们登录,我希望他们的用户名与唯一的客户端进程配对,这样服务器就可以通过数据包
如何更改程序,以便函数 function_delayed_1 和 function_delayed_2 仅同时执行一次: int main(int argc, char *argv[]) {
考虑这两个程序: //in #define MAX 50 int main(int argc, char* argv[]) { int *count; int fd=shm
请告诉我如何一次打开三个终端,这样我的项目就可以轻松执行,而不必打开三个终端三次然后运行三个exe文件。请问我们如何通过脚本来做到这一点,即打开三个终端并执行三个 exe 文件。 最佳答案 在后台运行
我编写了一个监控服务来跟踪一组进程,并在服务行为异常、内存使用率高、超出 CPU 运行时间等时发出通知。 这在我的本地计算机上运行良好,但我需要它指向远程机器并获取这些机器上的进程信息。 我的方法,在
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 8年前关闭。 Improve this qu
我有一个允许用户上传文件的应用程序。上传完成后,必须在服务器上完成许多处理步骤(解压、存储、验证等...),因此稍后会在一切完成后通过电子邮件通知用户。 我见过很多示例,其中 System.Compo
这个问题对很多人来说可能听起来很愚蠢,但我想对这个话题有一个清晰的理解。例如:当我们在 linux(ubuntu, x86) 上构建一个 C 程序时,它会在成功编译和链接过程后生成 a.out。 a.
ps -eaf | grep java 命令在这里不是识别进程是否是 java 进程的解决方案,因为执行此命令后我的许多 java 进程未在输出中列出。 最佳答案 简答(希望有人写一个更全面的): 获
我有几个与内核态和用户态的 Windows 进程相关的问题。 如果我有一个 hello world 应用程序和一个暴露新系统调用 foo() 的 hello world 驱动程序,我很好奇在内核模式下
我找不到很多关于 Windows 中不受信任的完整性级别的信息,对此有一些疑问: 是否有不受信任的完整性级别进程可以创建命名对象的地方? (互斥锁、事件等) 不受信任的完整性级别进程是否应该能够打开一
我是一名优秀的程序员,十分优秀!