進程管道通訊-偽終端方式
傳統(tǒng)CreateProcess傳遞StdIN StdOUT 管道方式存在進程輸出不能及時刷管道問題導致ReadFile阻塞傳統(tǒng)解決方法是子進程強制進行fflush(stdout), 如果不能改變子進程就沒辦法了從win10開始 windows支持偽終端可以完全模擬終端管道的行為傳統(tǒng)管道crt會使用GetFileType檢測STDHANDLE 對于控制臺調用檢測到的是字符設備而對于改變后的STDHANDLE 檢測到的是管道設備對發(fā)現管道設備時緩沖方式就變成了塊緩沖方式遇到換行符也不會及時flush這就導致預期輸出不能直接被ReadFile讀取到Win10上的偽終端方式接管進程輸入輸出代碼//Kernel32.dll #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE #define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x20016 #endif typedef HRESULT(WINAPI* PFN_CreatePseudoConsole)( _In_ COORD size, _In_ HANDLE hInput, _In_ HANDLE hOutput, _In_ DWORD dwFlags, _Out_ HPCON* phPC ); HRESULT MyCreatePseudoConsole( _In_ COORD size, _In_ HANDLE hInput, _In_ HANDLE hOutput, _In_ DWORD dwFlags, _Out_ HPCON* phPC ) { static PFN_CreatePseudoConsole fn (PFN_CreatePseudoConsole)::GetProcAddress(GetModuleHandle(LKernel32.dll), CreatePseudoConsole); if (fn) return fn(size, hInput, hOutput, dwFlags, phPC); return E_FAIL; } typedef BOOL(WINAPI* PFN_InitializeProcThreadAttributeList)( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize ); BOOL MyInitializeProcThreadAttributeList( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize ) { static PFN_InitializeProcThreadAttributeList fn (PFN_InitializeProcThreadAttributeList)::GetProcAddress(GetModuleHandle(LKernel32.dll), InitializeProcThreadAttributeList); if (fn) return fn(lpAttributeList, dwAttributeCount, dwFlags, lpSize); return 0; } typedef BOOL(WINAPI* PFN_UpdateProcThreadAttribute)( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue, PSIZE_T lpReturnSize ); BOOL MyUpdateProcThreadAttribute( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue, PSIZE_T lpReturnSize ) { static PFN_UpdateProcThreadAttribute fn (PFN_UpdateProcThreadAttribute)::GetProcAddress(GetModuleHandle(LKernel32.dll), UpdateProcThreadAttribute); if (fn) return fn(lpAttributeList, dwFlags, Attribute, lpValue, cbSize, lpPreviousValue, lpReturnSize); return 0; } typedef void(WINAPI* PFN_ClosePseudoConsole)(_In_ HPCON hPC); void MyClosePseudoConsole(_In_ HPCON hPC) { static PFN_ClosePseudoConsole fn (PFN_ClosePseudoConsole)::GetProcAddress(GetModuleHandle(LKernel32.dll), ClosePseudoConsole); if (fn) fn(hPC); } typedef VOID(WINAPI*PFN_DeleteProcThreadAttributeList)( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList ); VOID MyDeleteProcThreadAttributeList( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList ) { static PFN_DeleteProcThreadAttributeList fn (PFN_DeleteProcThreadAttributeList)::GetProcAddress(GetModuleHandle(LKernel32.dll), DeleteProcThreadAttributeList); if (fn) fn(lpAttributeList); } typedef struct _MySTARTUPINFOEXW { STARTUPINFOW StartupInfo; LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; } MySTARTUPINFOEXW; BidirectionalPipe::~BidirectionalPipe() { Cleanup(); } bool BidirectionalPipe::StartProcess(bool bPsConsole, const std::wstring command) { SECURITY_ATTRIBUTES sa { 0 }; sa.nLength sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle TRUE; sa.lpSecurityDescriptor NULL; // 創(chuàng)建輸入管道父進程寫子進程讀 if (!CreatePipe(m_hChildStdinRead, m_hParentWrite, sa, 0)) { Cleanup(); return false; } // 創(chuàng)建輸出管道父進程讀子進程寫 if (!CreatePipe(m_hParentRead, m_hChildStdoutWrite, sa, 0)) { Cleanup(); return false; } if (bPsConsole) { // 2. 創(chuàng)建偽控制臺 (ConPTY) COORD consoleSize { 80, 25 }; // 初始控制臺大小 HRESULT hr MyCreatePseudoConsole(consoleSize, m_hChildStdinRead, m_hChildStdoutWrite, 0, m_hPC); if (FAILED(hr)) { Cleanup(); return false; } } // 3. 準備 STARTUPINFOEXW 結構體 MySTARTUPINFOEXW si { 0 }; if (bPsConsole) { si.StartupInfo.cb sizeof(MySTARTUPINFOEXW); } else { si.StartupInfo.cb sizeof(STARTUPINFOW); } si.StartupInfo.dwFlags STARTF_USESTDHANDLES; if (bPsConsole) { // 關鍵將 ConPTY 句柄附加到 StartupInfo SIZE_T size 0; MyInitializeProcThreadAttributeList(NULL, 1, 0, size); m_lpAttributeList (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, size); MyInitializeProcThreadAttributeList(m_lpAttributeList, 1, 0, size); MyUpdateProcThreadAttribute(m_lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, m_hPC, sizeof(HPCON), NULL, NULL); si.lpAttributeList m_lpAttributeList; si.StartupInfo.wShowWindow SW_HIDE; si.StartupInfo.dwFlags STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; } else { si.StartupInfo.wShowWindow SW_HIDE; si.StartupInfo.dwFlags STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.StartupInfo.hStdError m_hChildStdoutWrite; si.StartupInfo.hStdOutput m_hChildStdoutWrite; si.StartupInfo.hStdInput m_hChildStdinRead; } // 啟動進程 if (!CreateProcessW(NULL, const_castwchar_t*(command.c_str()), NULL, NULL, bPsConsole ? FALSE : TRUE, bPsConsole ? EXTENDED_STARTUPINFO_PRESENT : 0, NULL, NULL, si.StartupInfo, m_pi)) { Cleanup(); return false; } // 【至關重要】CreateProcess 成功后父進程必須關閉它不需要的管道句柄 // 否則 ReadFile 永遠不會收到 EOF導致卡死。 if (m_hChildStdinRead) { CloseHandle(m_hChildStdinRead); m_hChildStdinRead 0; } if (m_hChildStdoutWrite) { CloseHandle(m_hChildStdoutWrite); m_hChildStdoutWrite 0; } return true; } void BidirectionalPipe::WriteInput(const std::string input) { if (m_hParentWrite NULL) return; DWORD bytesWritten; WriteFile(m_hParentWrite, input.c_str(), (DWORD)input.size(), bytesWritten, NULL); FlushFileBuffers(m_hParentWrite); } __declspec(noinline) CStringA BidirectionalPipe::ReadOutput() { CStringA strRet; const int bufMaxChars 4095; char buffer[bufMaxChars 10] { 0 }; DWORD bytesRead; //FlushFileBuffers(m_hChildStdoutWrite); // 第一次讀取 if (!ReadFile(m_hParentRead, buffer, bufMaxChars, bytesRead, NULL) || bytesRead 0) { return strRet; // 讀取失敗或無數據 } buffer[bytesRead] \0; strRet buffer; //// 檢查是否讀取完所有數據 //if (bytesRead bufMaxChars) { // return strRet; // 緩沖區(qū)未滿說明數據已讀完 //} // 緩沖區(qū)已滿檢查管道內是否還有剩余數據 while (true) { DWORD bytesAvailable 0; BOOL peekResult PeekNamedPipe( m_hParentRead, NULL, // 不讀取數據只查詢狀態(tài) 0, // 緩沖區(qū)大小為0 NULL, // 讀取的字節(jié)數 bytesAvailable, // 可用字節(jié)數 NULL // 消息剩余字節(jié)數用于消息模式 ); if (!peekResult || bytesAvailable 0) { break; // 查詢失敗或無剩余數據 } // 動態(tài)分配緩沖區(qū)讀取剩余數據最多讀取bufMaxChars字節(jié) DWORD readSize min(bytesAvailable, (DWORD)bufMaxChars); if (ReadFile(m_hParentRead, buffer, readSize, bytesRead, NULL) bytesRead 0) { buffer[bytesRead] \0; strRet buffer; // 如果讀取的字節(jié)數等于緩沖區(qū)大小說明可能還有更多數據 if (bytesRead readSize) { break; // 數據已讀完 } } else { break; // 讀取失敗 } } return strRet; } void BidirectionalPipe::Terminate() { if (m_pi.hProcess) { ::TerminateProcess(m_pi.hProcess, 0); WaitForSingleObject(m_pi.hProcess, 10000); } } void BidirectionalPipe::Cleanup() { if (m_hChildStdinRead) { CloseHandle(m_hChildStdinRead); m_hChildStdinRead 0; } if (m_hParentWrite) { CloseHandle(m_hParentWrite); m_hParentWrite 0; } if (m_hParentRead) { CloseHandle(m_hParentRead); m_hParentRead 0; } if (m_hChildStdoutWrite) { CloseHandle(m_hChildStdoutWrite); m_hChildStdoutWrite 0; } if (m_pi.hProcess) { CloseHandle(m_pi.hProcess); m_pi.hProcess 0; } if (m_pi.hThread) { CloseHandle(m_pi.hThread); m_pi.hThread 0; } if (m_lpAttributeList) { MyDeleteProcThreadAttributeList(m_lpAttributeList); HeapFree(GetProcessHeap(), 0, m_lpAttributeList); m_lpAttributeList 0; } if (m_hPC) { MyClosePseudoConsole(m_hPC); m_hPC 0; } } ///////////////////////////////////Header///////////////////////////// #pragma once typedef void* HPCON; class BidirectionalPipe { private: HANDLE m_hChildStdinRead NULL; HANDLE m_hParentWrite NULL; HANDLE m_hParentRead NULL; HANDLE m_hChildStdoutWrite NULL; PROCESS_INFORMATION m_pi { 0 }; LPPROC_THREAD_ATTRIBUTE_LIST m_lpAttributeList 0; HPCON m_hPC 0; public: ~BidirectionalPipe(); bool StartProcess(bool bPsConsole, const std::wstring command); void WriteInput(const std::string input); CStringA ReadOutput(); void Terminate(); private: void Cleanup(); };

相關新聞

計算機自學路線:從零基礎到進階的完整指南

計算機自學路線:從零基礎到進階的完整指南

1. 計算機課程自學路線規(guī)劃作為一名在IT行業(yè)摸爬滾打十年的老程序員,我經常被問到"如何系統(tǒng)學習計算機知識"。市面上課程琳瑯滿目,但缺乏體系化的學習路徑往往讓初學者陷入"東一榔頭西一棒子"的困境。這里分享我總結的計算機課程學習…

2026/7/30 4:11:47 閱讀更多
錫膏管理企業(yè)居然能決定良品率?

錫膏管理企業(yè)居然能決定良品率?

在電子制造行業(yè),有一句老話叫“七分錫膏、三分焊接”。盡管說法稍顯夸張,但錫膏管理的質量與規(guī)范性,確實直接關系到SMT產線的良品率。很多工程師和管理者都遇到過這樣的場景:明明鋼網開孔、印刷參數都沒問題,但上線的板…

2026/7/30 4:11:47 閱讀更多
國密SM2性能認證:緩存?zhèn)刃诺琅c恒定時間編程實戰(zhàn)

國密SM2性能認證:緩存?zhèn)刃诺琅c恒定時間編程實戰(zhàn)

1. 項目概述:國密性能認證的“隱形門檻”最近和幾個做國密產品認證的老朋友聊天,大家不約而同地提到了一個現象:很多基于C語言開發(fā)的國密算法庫,在功能測試上都能順利過關,但一到商用密碼檢測中心的性能認證環(huán)節(jié)&#…

2026/7/30 4:11:47 閱讀更多
FPGA設計核心:硬件并發(fā)思維與資源優(yōu)化實踐指南

FPGA設計核心:硬件并發(fā)思維與資源優(yōu)化實踐指南

FPGA的設計本質是什么?這個問題看似簡單,卻觸及了硬件工程師從入門到精通必須跨越的核心認知鴻溝。與軟件編程不同,FPGA設計不是簡單的代碼編寫,而是硬件電路的時空重構——你需要同時考慮邏輯功能的正確性、時序的穩(wěn)定性、資源的…

2026/7/30 5:11:50 閱讀更多
共享充電寶專業(yè)款價格是多少

共享充電寶專業(yè)款價格是多少

1. 共享充電寶價格亂象:便宜的可能更“貴”逛街時手機沒電,租個共享充電寶卻發(fā)現價格參差不齊——從每小時2元到8元,甚至部分景區(qū)高達10元。低價設備充電慢、電量虛,租用兩小時才充30%,反而耽誤時間;高價設…

2026/7/30 5:11:50 閱讀更多
從頁面到駕駛艙:交互范式變遷的兩種路徑

從頁面到駕駛艙:交互范式變遷的兩種路徑

從頁面到駕駛艙:交互范式變遷的兩種路徑 2026-07-29 過去二十多年,軟件界面遵循著一種底層邏輯:時間被凍結在一張張頁面里,用戶通過空間導航在功能之間移動。無論是打開一個App、進入一個菜單、還是找到某個按鈕,本質上…

2026/7/30 5:11:49 閱讀更多
[GESP202606 四級] 掃雷

[GESP202606 四級] 掃雷

B4557 [GESP202606 四級] 掃雷 https://www.luogu.com.cn/problem/B4557 中國計算機學會(CCF)2026年6月C四級講解——掃雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四級] 掃雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:01:06 閱讀更多