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

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

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 &lt;array&gt;
#include &lt;string&gt;
#include &lt;windows.h&gt;
int main() {
std::string adbConnectCommand = &quot;adb connect localhost:5555&quot;;
STARTUPINFO startupInfo;
SECURITY_ATTRIBUTES secAttr;
PROCESS_INFORMATION procInfo;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
ZeroMemory(&amp;secAttr, sizeof(secAttr));
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process&#39;s STDOUT
if (!CreatePipe(&amp;stdoutReadHandle, &amp;stdoutWriteHandle, &amp;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(&amp;startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = &amp;stdoutWriteHandle;
startupInfo.hStdOutput = &amp;stdoutWriteHandle;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&amp;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&#39;s environment block
NULL,                               // Use parent&#39;s starting directory 
&amp;startupInfo,                       // Pointer to STARTUPINFO structure
&amp;procInfo)                          // Pointer to PROCESS_INFORMATION structure
)
{
// Close the handles we don&#39;t need so we can read from stdoutReadHandle
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
CloseHandle(stdoutWriteHandle);
std::array&lt;char, 512&gt; buffer;
std::string adbConnectOutput;
while(ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), NULL, NULL))
adbConnectOutput += buffer.data();
CloseHandle(stdoutReadHandle);
if (adbConnectOutput.find(&quot;connected to localhost:5555&quot;) == 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 变量,可以直接赋值给 hStdErrorhStdOutput,所以你需要去掉 &,因为它们不应该出现在这里,例如:

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++
&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:

startupInfo.hStdError = stdoutWriteHandle;
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:

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&lt;LPSTR&gt;(adbConnectCommand.c_str()),
...

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

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

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:

std::array&lt;char, 512&gt; buffer;
std::string adbConnectOutput;
DWORD dwNumRead;
while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &amp;dwNumRead, NULL))
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:

确定