C++ WinApi的ReadFile函数无法读取任何内容,并报告错误109 ERROR_BROKEN_PIPE。

huangapple go评论110阅读模式
英文:

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的输出也不起作用。

代码:

  1. #include <array>
  2. #include <string>
  3. #include <windows.h>
  4. int main() {
  5. std::string adbConnectCommand = "adb connect localhost:5555";
  6. STARTUPINFO startupInfo;
  7. SECURITY_ATTRIBUTES secAttr;
  8. PROCESS_INFORMATION procInfo;
  9. HANDLE stdoutReadHandle = NULL;
  10. HANDLE stdoutWriteHandle = NULL;
  11. ZeroMemory(&secAttr, sizeof(secAttr));
  12. secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  13. secAttr.bInheritHandle = TRUE;
  14. secAttr.lpSecurityDescriptor = NULL;
  15. // Create a pipe for the child process's STDOUT
  16. if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &secAttr, 0))
  17. return 2; // error
  18. // Ensure the read handle to the pipe for STDOUT is not inherited
  19. if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
  20. return 2; // error
  21. ZeroMemory(&startupInfo, sizeof(startupInfo));
  22. startupInfo.cb = sizeof(startupInfo);
  23. startupInfo.hStdError = &stdoutWriteHandle;
  24. startupInfo.hStdOutput = &stdoutWriteHandle;
  25. startupInfo.dwFlags |= STARTF_USESTDHANDLES;
  26. ZeroMemory(&procInfo, sizeof(procInfo));
  27. // Start the child process
  28. if (CreateProcessA(NULL, // No module name (use command line)
  29. (TCHAR*)adbConnectCommand.c_str(), // Command line
  30. NULL, // Process handle not inheritable
  31. NULL, // Thread handle not inheritable
  32. TRUE, // Set handle inheritance
  33. 0, // No creation flags
  34. NULL, // Use parent's environment block
  35. NULL, // Use parent's starting directory
  36. &startupInfo, // Pointer to STARTUPINFO structure
  37. &procInfo) // Pointer to PROCESS_INFORMATION structure
  38. )
  39. {
  40. // Close the handles we don't need so we can read from stdoutReadHandle
  41. CloseHandle(procInfo.hProcess);
  42. CloseHandle(procInfo.hThread);
  43. CloseHandle(stdoutWriteHandle);
  44. std::array<char, 512> buffer;
  45. std::string adbConnectOutput;
  46. while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
  47. adbConnectOutput += buffer.data();
  48. CloseHandle(stdoutReadHandle);
  49. if (adbConnectOutput.find("connected to localhost:5555") == std::string::npos)
  50. return 1; // not found
  51. m_Connected = true;
  52. return 0; // success
  53. }
  54. return 2; // error
  55. }
英文:

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:

  1. #include &lt;array&gt;
  2. #include &lt;string&gt;
  3. #include &lt;windows.h&gt;
  4. int main() {
  5. std::string adbConnectCommand = &quot;adb connect localhost:5555&quot;;
  6. STARTUPINFO startupInfo;
  7. SECURITY_ATTRIBUTES secAttr;
  8. PROCESS_INFORMATION procInfo;
  9. HANDLE stdoutReadHandle = NULL;
  10. HANDLE stdoutWriteHandle = NULL;
  11. ZeroMemory(&amp;secAttr, sizeof(secAttr));
  12. secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  13. secAttr.bInheritHandle = TRUE;
  14. secAttr.lpSecurityDescriptor = NULL;
  15. // Create a pipe for the child process&#39;s STDOUT
  16. if (!CreatePipe(&amp;stdoutReadHandle, &amp;stdoutWriteHandle, &amp;secAttr, 0))
  17. return 2; // error
  18. // Ensure the read handle to the pipe for STDOUT is not inherited
  19. if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
  20. return 2; // error
  21. ZeroMemory(&amp;startupInfo, sizeof(startupInfo));
  22. startupInfo.cb = sizeof(startupInfo);
  23. startupInfo.hStdError = &amp;stdoutWriteHandle;
  24. startupInfo.hStdOutput = &amp;stdoutWriteHandle;
  25. startupInfo.dwFlags |= STARTF_USESTDHANDLES;
  26. ZeroMemory(&amp;procInfo, sizeof(procInfo));
  27. // Start the child process
  28. if (CreateProcessA(NULL, // No module name (use command line)
  29. (TCHAR*)adbConnectCommand.c_str(), // Command line
  30. NULL, // Process handle not inheritable
  31. NULL, // Thread handle not inheritable
  32. TRUE, // Set handle inheritance
  33. 0, // No creation flags
  34. NULL, // Use parent&#39;s environment block
  35. NULL, // Use parent&#39;s starting directory
  36. &amp;startupInfo, // Pointer to STARTUPINFO structure
  37. &amp;procInfo) // Pointer to PROCESS_INFORMATION structure
  38. )
  39. {
  40. // Close the handles we don&#39;t need so we can read from stdoutReadHandle
  41. CloseHandle(procInfo.hProcess);
  42. CloseHandle(procInfo.hThread);
  43. CloseHandle(stdoutWriteHandle);
  44. std::array&lt;char, 512&gt; buffer;
  45. std::string adbConnectOutput;
  46. while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
  47. adbConnectOutput += buffer.data();
  48. CloseHandle(stdoutReadHandle);
  49. if (adbConnectOutput.find(&quot;connected to localhost:5555&quot;) == std::string::npos)
  50. return 1; // not found
  51. m_Connected = true;
  52. return 0; // success
  53. }
  54. return 2; // error
  55. }
  56. </details>
  57. # 答案1
  58. **得分**: 4
  59. 在这段代码中:
  60. ```c++
  61. startupInfo.hStdError = &stdoutWriteHandle;
  62. startupInfo.hStdOutput = &stdoutWriteHandle;

HANDLE 被定义为 void*,因此任何指针都可以赋值给它。但并不是每个指针都是有效的 HANDLE 值。在这种情况下,stdoutWriteHandle 是一个 HANDLE 变量,可以直接赋值给 hStdErrorhStdOutput,所以你需要去掉 &,因为它们不应该出现在这里,例如:

  1. startupInfo.hStdError = stdoutWriteHandle;
  2. startupInfo.hStdOutput = stdoutWriteHandle;

另外,你的代码还存在其他问题。

在这段代码中:

  1. // Start the child process
  2. if (CreateProcessA(NULL, // No module name (use command line)
  3. (TCHAR*)adbConnectCommand.c_str(), // Command line
  4. ...

这里的类型转换 TCHAR* 是错误的。CreateProcessA() 期望的是 LPSTR(也就是 char*),但是 TCHAR* 可能是 char*,也可能不是,这取决于你的项目配置。所以应该使用正确的类型,例如:

  1. CreateProcessA(NULL,
  2. (LPSTR)adbConnectCommand.c_str(),
  3. ...

然而,你应该使用 C++ 风格的 const_cast 而不是 C 风格的转换,例如:

  1. CreateProcessA(NULL,
  2. const_cast<LPSTR>(adbConnectCommand.c_str()),
  3. ...

或者,使用 std::string::data()(C++17 及更高版本)或 std::string::operator[],例如:

  1. CreateProcessA(NULL,
  2. adbConnectCommand.data(),
  3. ...
  1. CreateProcessA(NULL,
  2. &adbConnectCommand[0],
  3. ...

在这段代码中:

  1. while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
  2. adbConnectOutput += buffer.data();

ReadFile() 写入到缓冲区的数据没有以空字符结尾,但是你却将一个以空字符结尾的 char* 指针追加到输出的 std::string 中。你需要注意 ReadFile() 实际报告给你的大小,特别是因为它不需要空字符结尾,并且可能比你请求的大小小,例如:

  1. std::array<char, 512> buffer;
  2. std::string adbConnectOutput;
  3. DWORD dwNumRead;
  4. while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &dwNumRead, NULL))
  5. adbConnectOutput.append(buffer.data(), dwNumRead);
英文:

In this code:

> c++
&gt; startupInfo.hStdError = &amp;stdoutWriteHandle;
&gt; startupInfo.hStdOutput = &amp;stdoutWriteHandle;
&gt;

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 &amp;s as they don't belong there, eg:

  1. startupInfo.hStdError = stdoutWriteHandle;
  2. startupInfo.hStdOutput = stdoutWriteHandle;

That being said, there are some other problems with your code.

In this code:

>
&gt; // Start the child process
&gt; if (CreateProcessA(NULL, // No module name (use command line)
&gt; (TCHAR*)adbConnectCommand.c_str(), // Command line
&gt; ...
&gt;

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:

  1. CreateProcessA(NULL,
  2. (LPSTR)adbConnectCommand.c_str(),
  3. ...

However, you should be using the C++-style const_cast instead of a C-style cast, eg:

  1. CreateProcessA(NULL,
  2. const_cast&lt;LPSTR&gt;(adbConnectCommand.c_str()),
  3. ...

Alternatively, use std::string::data() (C++17 and later) or std::string::operator[] instead, eg:

  1. CreateProcessA(NULL,
  2. adbConnectCommand.data(),
  3. ...
  1. CreateProcessA(NULL,
  2. &amp;adbConnectCommand[0],
  3. ...

And, in this code:

>
&gt; while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
&gt; adbConnectOutput += buffer.data();
&gt;

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:

  1. std::array&lt;char, 512&gt; buffer;
  2. std::string adbConnectOutput;
  3. DWORD dwNumRead;
  4. while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &amp;dwNumRead, NULL))
  5. adbConnectOutput.append(buffer.data(), dwNumRead);

huangapple
  • 本文由 发表于 2023年8月9日 06:36:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76863557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定