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
相关产品推荐
相关产品推荐

