CreateProcess cmd.exe read/write pipes deadlock
您好,我正在尝试为cmd.exe创建前端GUI,这样我可以使其更宽,但被卡住了。
我尝试设计这样的API
1 2 3 | char* Directory = WriteCommand("dir"); printf("- %s\ ", Directory); |
和输出看起来完全一样,将在cmd窗口中,除了我在字符串中,所以它将是
1 2 | DATE TIME FILESIZE FILENAME etc etc etc |
然后我可以发出
1 | char* Up = WriteCommand ("cd .."); |
它会给我上面的目录清单。因此,我希望通过使用管道进行读取和写入的终端控件。
我已经基于此MSDN示例代码尝试了很多事情-https://msdn.microsoft.com/zh-cn/library/ms682499.aspx
但是我认为此代码仅是发出一个命令并读取一个响应的好方法,因为它紧接着如此处所述死锁-https://blogs.msdn.microsoft.com/oldnewthing/20110707-00/?p=10223
我在这里看到其他几个问题,例如类似的问题-如何使用CreateProcess()和CreatePipe()从cmd.exe读取输出,但是没有解决方案适合我。
这是我的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | #include <windows.h> #include <tchar.h> #include <stdio.h> #include <strsafe.h> #define BUFSIZE 4096 HANDLE g_hChildStd_IN_Rd = NULL; HANDLE g_hChildStd_IN_Wr = NULL; HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; HANDLE g_hInputFile = NULL; void CreateChildProcess(void); void WriteToPipe(char* Arg1); void ReadFromPipe(void); void ErrorExit(PTSTR); int _tmain(int argc, TCHAR *argv[]) { SECURITY_ATTRIBUTES saAttr; printf("\ ->Start of parent execution.\ "); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) ErrorExit(TEXT("StdoutRd CreatePipe")); // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) ErrorExit(TEXT("Stdout SetHandleInformation")); // Create a pipe for the child process's STDIN. if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) ErrorExit(TEXT("Stdin CreatePipe")); // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) ErrorExit(TEXT("Stdin SetHandleInformation")); // Create the child process. CreateChildProcess(); // Get a handle to an input file for the parent. // This example assumes a plain text file and uses string output to verify data flow. /*if (argc == 1) ErrorExit(TEXT("Please specify an input file.\ ")); g_hInputFile = CreateFile( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (g_hInputFile == INVALID_HANDLE_VALUE) ErrorExit(TEXT("CreateFile"));*/ // Write to the pipe that is the standard input for a child process. // Data is written to the pipe's buffers, so it is not necessary to wait // until the child process is running before writing data. // Read from pipe that is the standard output for child process. ReadFromPipe(); WriteToPipe("ipconfig"); // THIS IS WHERE DEADLOCK OCCURS, FROM HERE // PROGRAM BECOMES UNRESPONSIVE - HOW TO FIX THIS? ReadFromPipe(); printf("\ ->End of parent execution.\ "); // The remaining open handles are cleaned up when this process terminates. // To avoid resource leaks in a larger application, close handles explicitly. return 0; } void CreateChildProcess() // Create a child process that uses the previously created pipes for STDIN and STDOUT. { TCHAR szCmdline[] = TEXT("cmd.exe /k"); PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; // Set up members of the PROCESS_INFORMATION structure. ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = g_hChildStd_OUT_Wr; siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; siStartInfo.hStdInput = g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. bSuccess = CreateProcess(NULL, "cmd.exe", // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION // If an error occurs, exit the application. if (!bSuccess) ErrorExit(TEXT("CreateProcess")); else { // Close handles to the child process and its primary thread. // Some applications might keep these handles to monitor the status // of the child process, for example. CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); } } void WriteToPipe(char* Command) // Read from a file and write its contents to the pipe for the child's STDIN. // Stop when there is no more data. { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL); if (bSuccess == FALSE) printf("write fail\ "); printf("written = %i\ ", dwWritten); //for (;;) //{ //bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL); //if (!bSuccess || dwRead == 0) break; //bSuccess = WriteFile(g_hChildStd_IN_Wr, Command, strlen(Command), &dwWritten, NULL); //if (bSuccess == FALSE) //printf("write fail\ "); //printf("written = %i\ ", dwWritten); //} // Close the pipe handle so the child process stops reading. //if (!CloseHandle(g_hChildStd_IN_Wr)) //ErrorExit(TEXT("StdInWr CloseHandle")); } void ReadFromPipe(void) // Read output from the child process's pipe for STDOUT // and write to the parent process's pipe for STDOUT. // Stop when there is no more data. { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); int i; for (i = 0; i < 4; i++) { /*DWORD dwAvail = 0; if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &dwAvail, NULL)) { // error, the child process might have ended break; } if (!dwAvail) { // no data available in the pipe break; }*/ bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); if (!bSuccess || dwRead == 0) break; /*bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL); if (!bSuccess) break;*/ chBuf[dwRead] = '\\0'; printf("%i - %s\ ", i, chBuf); } printf("done\ "); } |
我发出了初始的" cmd.exe"命令,该命令为我提供了命令提示符的开始。我现在想发出" ipconfig"(或任何其他命令)以获取网络信息。程序陷入僵局,无法响应。我无法再读取子进程的输出。我怎样才能解决这个问题?谢谢你的帮助。
避免死锁的最强大,最有效的解决方案-使用异步io。永远不要等待IO(读取,写入,ioctl)完成,而要在回调中处理。
还请注意使用管道进行重定向输出-非常常见的错误,我们需要对STDIN和STDOUT使用不同的句柄,并且需要创建2对不同的管道对-一个用于STDIN,另一个用于STDOUT。这是错误的。我们可以对STDIN和STDOUT(以及STDERROR)使用单个管道句柄。
标志。通过使用
结果,服务器和客户端进程都可以读取和写入
到管道。和
模式。我们也不能使这个句柄可继承,所以不需要调用
将其都分配给stdin和stdout。因为客户(例如
cmd.exe)通常假定为同步io-我们不使用
只要使此句柄可继承即可。
结束了。在这里,我们有3个变体-使用
线程池,但是对于重定向子进程输出,通常是这种方式
不需要。
(我们复制给子对象),然后在管道上调用
处理。此
一直有来自管道的活动读取请求,直到断开连接。
导致死锁,因为我们使用异步io。
数据(基于以前的结果和状态),这更容易
在普通过程中处理但不在回调中处理,我们可以创建纤程
为此任务(
阅读完成后-首次调用
对于同一工作线程多次调用此命令-将出错
在第二个和下一个呼叫中
所有这些工作仅从Vista开始。关于XP错误)。记得
当前的光纤,到需要退回的位置(
致电
我们可以处理读取结果,然后通过调用返回
在非常罕见和特定的情况下确实需要。通常
处理所有是与管道句柄相关的对象中具有状态的回调-绰绰有余。
cmd的简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | #define _XP_SUPPORT_ struct IO_COUNT { HANDLE _hFile; HANDLE _hEvent; LONG _dwIoCount; IO_COUNT() { _dwIoCount = 1; _hEvent = 0; } ~IO_COUNT() { if (_hEvent) { CloseHandle(_hEvent); } } ULONG Create(HANDLE hFile); void BeginIo() { InterlockedIncrement(&_dwIoCount); } void EndIo() { if (!InterlockedDecrement(&_dwIoCount)) { SetEvent(_hEvent); } } void Wait() { WaitForSingleObject(_hEvent, INFINITE); } }; struct U_IRP : OVERLAPPED { enum { read, write }; IO_COUNT* _pIoObject; ULONG _code; LONG _dwRef; char _buffer[256]; void AddRef() { InterlockedIncrement(&_dwRef); } void Release() { if (!InterlockedDecrement(&_dwRef)) delete this; } U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject) { _dwRef = 1; pIoObject->BeginIo(); RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED)); } ~U_IRP() { _pIoObject->EndIo(); } ULONG CheckIoResult(BOOL fOk) { if (fOk) { #ifndef _XP_SUPPORT_ OnIoComplete(NOERROR, InternalHigh); #endif return NOERROR; } ULONG dwErrorCode = GetLastError(); if (dwErrorCode != ERROR_IO_PENDING) { OnIoComplete(dwErrorCode, 0); } return dwErrorCode; } ULONG Read() { _code = read; AddRef(); return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this)); } ULONG Write(const void* pvBuffer, ULONG cbBuffer) { _code = write; AddRef(); return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this)); } VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered) { switch (_code) { case read: if (dwErrorCode == NOERROR) { if (dwNumberOfBytesTransfered) { if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0)) { PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR)); if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar)) { if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0)) { PSTR sz = (PSTR)alloca(cbMultiByte); if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0)) { DbgPrint("%.*s", cbMultiByte, sz); } } } } } Read(); } break; case write: break; default: __debugbreak(); } Release(); if (dwErrorCode) { DbgPrint("[%u]: error=%u\ ", _code, dwErrorCode); } } static VOID WINAPI _OnIoComplete( DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered); } }; ULONG IO_COUNT::Create(HANDLE hFile) { _hFile = hFile; // error in declaration LPOVERLAPPED_COMPLETION_ROUTINE : // second parameter must be DWORD_PTR but not DWORD return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) && #ifndef _XP_SUPPORT_ SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) && #endif (_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError(); } void ChildTest() { static const WCHAR name[] = L"\\\\\\\\?\\\\pipe\\\\somename"; HANDLE hFile = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { IO_COUNT obj; if (obj.Create(hFile) == NOERROR) { BOOL fOk = FALSE; SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE }; STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi; si.dwFlags = STARTF_USESTDHANDLES; si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0); if (si.hStdError != INVALID_HANDLE_VALUE) { si.hStdInput = si.hStdOutput = si.hStdError; WCHAR ApplicationName[MAX_PATH]; if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName))) { if (CreateProcessW(ApplicationName, 0, 0, 0, TRUE, 0, 0, 0, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); fOk = TRUE; } } CloseHandle(si.hStdError); } if (fOk) { STATIC_ASTRING(help_and_exit,"help\ \ exit\ \ "); U_IRP* p; if (p = new U_IRP(&obj)) { p->Read(); p->Release(); } obj.EndIo(); //++ simulate user commands static PCSTR commands[] = {"help\ \ ","ver\ \ ","dir\ \ ","exit\ \ " }; ULONG n = RTL_NUMBER_OF(commands); PCSTR* psz = commands; do { if (MessageBoxW(0,0, L"force close ?", MB_YESNO) == IDYES) { DisconnectNamedPipe(hFile); break; } if (p = new U_IRP(&obj)) { PCSTR command = *psz++; p->Write(command, (ULONG)strlen(command) * sizeof(CHAR)); p->Release(); } } while (--n); //-- obj.Wait(); } } CloseHandle(hFile); } } |