You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++调用WinApi执行adb时ReadFile无输出,报错ERROR_BROKEN_PIPE(109)

问题分析与修复方案

核心错误点

  • STARTUPINFO句柄赋值错误:startupInfo.hStdError和startupInfo.hStdOutput错误传入句柄的地址(&stdoutWriteHandle),这两个字段需要的是HANDLE类型的值,而非指针。此错误会导致子进程无法绑定标准输出/错误管道,直接引发管道断裂。
  • CreateProcessA命令行参数非法:第二个参数要求传入可修改的字符串(LPSTR),但直接强制转换adbConnectCommand.c_str()返回的const char*会触发未定义行为,系统无法正确解析命令。
  • ReadFile未处理实际读取字节数:循环中未获取每次读取的字节数,直接将整个buffer追加到结果会包含未初始化的垃圾数据;同时未正确判断ReadFile返回FALSE的场景,ERROR_BROKEN_PIPE是管道正常关闭的信号,并非错误。
  • 过早关闭进程句柄:读取输出前就关闭procInfo.hProcess,无法等待子进程执行完成,可能导致输出还未写入管道就被终止。

修复后的代码

#include <array>
#include <string>
#include <windows.h>

bool m_Connected = false;

int main() {
    std::string adbConnectCommand = "adb connect localhost:5555";
    // 复制可修改的命令行字符串,适配CreateProcessA要求
    char* cmdLine = new char[adbConnectCommand.size() + 1];
    strcpy_s(cmdLine, adbConnectCommand.size() + 1, adbConnectCommand.c_str());

    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;

    // 创建标准输出管道
    if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &secAttr, 0)) {
        delete[] cmdLine;
        return 2;
    }

    // 设置读句柄不继承,避免子进程占用
    if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0)) {
        CloseHandle(stdoutReadHandle);
        CloseHandle(stdoutWriteHandle);
        delete[] cmdLine;
        return 2;
    }

    ZeroMemory(&startupInfo, sizeof(startupInfo));
    startupInfo.cb = sizeof(startupInfo);
    // 修正句柄赋值:直接传入HANDLE值
    startupInfo.hStdError = stdoutWriteHandle;
    startupInfo.hStdOutput = stdoutWriteHandle;
    startupInfo.dwFlags |= STARTF_USESTDHANDLES;

    ZeroMemory(&procInfo, sizeof(procInfo));

    // 启动子进程
    if (CreateProcessA(
        NULL,
        cmdLine,               // 传入可修改的命令行字符串
        NULL,
        NULL,
        TRUE,
        0,
        NULL,
        NULL,
        &startupInfo,
        &procInfo
    )) {
        // 关闭子进程端的写句柄,释放管道资源
        CloseHandle(stdoutWriteHandle);

        std::array<char, 512> buffer;
        std::string adbConnectOutput;
        DWORD bytesRead = 0;

        // 循环读取输出,直到管道正常关闭
        while (ReadFile(stdoutReadHandle, buffer.data(), buffer.size(), &bytesRead, NULL)) {
            adbConnectOutput.append(buffer.data(), bytesRead);
        }

        // 过滤正常的管道关闭错误
        DWORD lastError = GetLastError();
        if (lastError != ERROR_BROKEN_PIPE) {
            adbConnectOutput.clear();
        }

        // 等待子进程执行完成后再关闭句柄
        WaitForSingleObject(procInfo.hProcess, INFINITE);
        CloseHandle(procInfo.hProcess);
        CloseHandle(procInfo.hThread);
        CloseHandle(stdoutReadHandle);

        if (adbConnectOutput.find("connected to localhost:5555") != std::string::npos) {
            m_Connected = true;
            delete[] cmdLine;
            return 0;
        } else {
            delete[] cmdLine;
            return 1;
        }
    }

    // 异常情况下清理资源
    CloseHandle(stdoutReadHandle);
    CloseHandle(stdoutWriteHandle);
    delete[] cmdLine;
    return 2;
}

额外说明

  • 测试cmd /c echo Hello World时,只需修改adbConnectCommand的值为该命令即可,修复后的代码同样适用。
  • 若adb路径未正确加入PATH,可直接使用adb完整路径(如"C:\\adb\\adb.exe connect localhost:5555")避免路径解析问题。

内容的提问来源于stack exchange,提问作者Cosmin Polifronie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:53:11