英文:
C++ WInApi ReadFile reads nothing and reports error 109 ERROR_BROKEN_PIPE
问题
我一直在尝试从C++应用程序中调用adb
并读取其输出,但一直没有成功。adb
已经在PATH中。
即使我等待,ReadFile
调用也从未读取任何内容。
GetLastError()
返回109 ERROR_BROKEN_PIPE。
我尝试了所有现有的关于这个问题的StackOverflow问题中提出的解决方案,但都没有成功。
甚至尝试捕获cmd /c echo Hello World
的输出也不起作用。
代码:
#include <array>
#include <string>
#include <windows.h>
int main() {
std::string adbConnectCommand = "adb connect localhost:5555";
STARTUPINFO startupInfo;
SECURITY_ATTRIBUTES secAttr;
PROCESS_INFORMATION procInfo;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
ZeroMemory(&secAttr, sizeof(secAttr));
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &secAttr, 0))
return 2; // error
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
return 2; // error
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = &stdoutWriteHandle;
startupInfo.hStdOutput = &stdoutWriteHandle;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&procInfo, sizeof(procInfo));
// Start the child process
if (CreateProcessA(NULL, // No module name (use command line)
(TCHAR*)adbConnectCommand.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&procInfo) // Pointer to PROCESS_INFORMATION structure
)
{
// Close the handles we don't need so we can read from stdoutReadHandle
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
CloseHandle(stdoutWriteHandle);
std::array<char, 512> buffer;
std::string adbConnectOutput;
while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
adbConnectOutput += buffer.data();
CloseHandle(stdoutReadHandle);
if (adbConnectOutput.find("connected to localhost:5555") == std::string::npos)
return 1; // not found
m_Connected = true;
return 0; // success
}
return 2; // error
}
英文:
I have been trying to call adb
from a C++ app and read its output, without success. adb
is present on PATH.
The ReadFile call never reads anything, even if I wait.
GetLastError()
returns 109 ERROR_BROKEN_PIPE.
I have tried all the solutions proposed in all existing StackOverflow questions on this matter, without any success.
Even trying to capture output from cmd /c echo Hello World
doesn't work.
Code:
#include <array>
#include <string>
#include <windows.h>
int main() {
std::string adbConnectCommand = "adb connect localhost:5555";
STARTUPINFO startupInfo;
SECURITY_ATTRIBUTES secAttr;
PROCESS_INFORMATION procInfo;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
ZeroMemory(&secAttr, sizeof(secAttr));
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &secAttr, 0))
return 2; // error
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
return 2; // error
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = &stdoutWriteHandle;
startupInfo.hStdOutput = &stdoutWriteHandle;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&procInfo, sizeof(procInfo));
// Start the child process
if (CreateProcessA(NULL, // No module name (use command line)
(TCHAR*)adbConnectCommand.c_str(), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&procInfo) // Pointer to PROCESS_INFORMATION structure
)
{
// Close the handles we don't need so we can read from stdoutReadHandle
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
CloseHandle(stdoutWriteHandle);
std::array<char, 512> buffer;
std::string adbConnectOutput;
while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
adbConnectOutput += buffer.data();
CloseHandle(stdoutReadHandle);
if (adbConnectOutput.find("connected to localhost:5555") == std::string::npos)
return 1; // not found
m_Connected = true;
return 0; // success
}
return 2; // error
}
</details>
# 答案1
**得分**: 4
在这段代码中:
```c++
startupInfo.hStdError = &stdoutWriteHandle;
startupInfo.hStdOutput = &stdoutWriteHandle;
HANDLE
被定义为 void*
,因此任何指针都可以赋值给它。但并不是每个指针都是有效的 HANDLE
值。在这种情况下,stdoutWriteHandle
是一个 HANDLE
变量,可以直接赋值给 hStdError
和 hStdOutput
,所以你需要去掉 &
,因为它们不应该出现在这里,例如:
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
另外,你的代码还存在其他问题。
在这段代码中:
// Start the child process
if (CreateProcessA(NULL, // No module name (use command line)
(TCHAR*)adbConnectCommand.c_str(), // Command line
...
这里的类型转换 TCHAR*
是错误的。CreateProcessA()
期望的是 LPSTR
(也就是 char*
),但是 TCHAR*
可能是 char*
,也可能不是,这取决于你的项目配置。所以应该使用正确的类型,例如:
CreateProcessA(NULL,
(LPSTR)adbConnectCommand.c_str(),
...
然而,你应该使用 C++ 风格的 const_cast
而不是 C 风格的转换,例如:
CreateProcessA(NULL,
const_cast<LPSTR>(adbConnectCommand.c_str()),
...
或者,使用 std::string::data()
(C++17 及更高版本)或 std::string::operator[]
,例如:
CreateProcessA(NULL,
adbConnectCommand.data(),
...
CreateProcessA(NULL,
&adbConnectCommand[0],
...
在这段代码中:
while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
adbConnectOutput += buffer.data();
ReadFile()
写入到缓冲区的数据没有以空字符结尾,但是你却将一个以空字符结尾的 char*
指针追加到输出的 std::string
中。你需要注意 ReadFile()
实际报告给你的大小,特别是因为它不需要空字符结尾,并且可能比你请求的大小小,例如:
std::array<char, 512> buffer;
std::string adbConnectOutput;
DWORD dwNumRead;
while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &dwNumRead, NULL))
adbConnectOutput.append(buffer.data(), dwNumRead);
英文:
In this code:
> c++
> startupInfo.hStdError = &stdoutWriteHandle;
> startupInfo.hStdOutput = &stdoutWriteHandle;
>
HANDLE
is defined as a void*
, so any pointer can be assigned to it. But not every pointer is a valid HANDLE
value. In this case, stdoutWriteHandle
is a HANDLE
variable and can be assigned as-is to hStdError
and hStdOutput
, so you need to drop the &
s as they don't belong there, eg:
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
That being said, there are some other problems with your code.
In this code:
>
> // Start the child process
> if (CreateProcessA(NULL, // No module name (use command line)
> (TCHAR*)adbConnectCommand.c_str(), // Command line
> ...
>
TCHAR*
is the wrong type to cast to here. CreateProcessA()
expects LPSTR
(aka char*
) instead, but TCHAR*
may or may not be char*
depending on your project configuration. So use the proper type, eg:
CreateProcessA(NULL,
(LPSTR)adbConnectCommand.c_str(),
...
However, you should be using the C++-style const_cast
instead of a C-style cast, eg:
CreateProcessA(NULL,
const_cast<LPSTR>(adbConnectCommand.c_str()),
...
Alternatively, use std::string::data()
(C++17 and later) or std::string::operator[]
instead, eg:
CreateProcessA(NULL,
adbConnectCommand.data(),
...
CreateProcessA(NULL,
&adbConnectCommand[0],
...
And, in this code:
>
> while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
> adbConnectOutput += buffer.data();
>
The data that ReadFile()
writes to your buffer is not null-terminated, but you are appending a null-terminated char*
pointer to your output std::string
. You need to pay attention to the actual size that ReadFile()
reports to you, especially since it doesn't require a null terminator, and it may be smaller than what you asked for, eg:
std::array<char, 512> buffer;
std::string adbConnectOutput;
DWORD dwNumRead;
while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &dwNumRead, NULL))
adbConnectOutput.append(buffer.data(), dwNumRead);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论