- 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/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!