RunAndWait.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <windows.h>
  3. #include <string>
  4. #include <psapi.h>
  5. #include <shlobj.h>
  6. #include <algorithm>
  7. #include <stdio.h>
  8. #include <time.h>
  9. static bool CreatePipeWithSecurityAttributes(HANDLE& hReadPipe, HANDLE& hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, int nSize)
  10. {
  11. hReadPipe = 0;
  12. hWritePipe = 0;
  13. bool ret = ::CreatePipe(&hReadPipe, &hWritePipe, lpPipeAttributes, nSize);
  14. if (!ret || (hReadPipe == INVALID_HANDLE_VALUE) || (hWritePipe == INVALID_HANDLE_VALUE))
  15. return false;
  16. return true;
  17. }
  18. // Using synchronous Anonymous pipes for process input/output redirection means we would end up
  19. // wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since
  20. // it will take advantage of the NT IO completion port infrastructure. But we can't really use
  21. // Overlapped I/O for process input/output as it would break Console apps (managed Console class
  22. // methods such as WriteLine as well as native CRT functions like printf) which are making an
  23. // assumption that the console standard handles (obtained via GetStdHandle()) are opened
  24. // for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchrnously!
  25. bool CreatePipe(HANDLE& parentHandle, HANDLE& childHandle, bool parentInputs)
  26. {
  27. SECURITY_ATTRIBUTES securityAttributesParent = { 0 };
  28. securityAttributesParent.bInheritHandle = 1;
  29. HANDLE hTmp = INVALID_HANDLE_VALUE;
  30. if (parentInputs)
  31. CreatePipeWithSecurityAttributes(childHandle, hTmp, &securityAttributesParent, 0);
  32. else
  33. CreatePipeWithSecurityAttributes(hTmp, childHandle, &securityAttributesParent, 0);
  34. HANDLE dupHandle = 0;
  35. // Duplicate the parent handle to be non-inheritable so that the child process
  36. // doesn't have access. This is done for correctness sake, exact reason is unclear.
  37. // One potential theory is that child process can do something brain dead like
  38. // closing the parent end of the pipe and there by getting into a blocking situation
  39. // as parent will not be draining the pipe at the other end anymore.
  40. if (!::DuplicateHandle(GetCurrentProcess(), hTmp,
  41. GetCurrentProcess(), &dupHandle,
  42. 0, false, DUPLICATE_SAME_ACCESS))
  43. {
  44. return false;
  45. }
  46. parentHandle = dupHandle;
  47. if (hTmp != INVALID_HANDLE_VALUE)
  48. ::CloseHandle(hTmp);
  49. return true;
  50. }
  51. struct ProcParams
  52. {
  53. HANDLE mReadHandle;
  54. HANDLE mWriteHandle;
  55. };
  56. DWORD WINAPI ReadProc(void* lpThreadParameter)
  57. {
  58. ProcParams* procParams = (ProcParams*)lpThreadParameter;
  59. while (true)
  60. {
  61. char buffer[2048];
  62. DWORD bytesRead = 0;
  63. if (::ReadFile(procParams->mReadHandle, buffer, (DWORD)2048, &bytesRead, NULL))
  64. {
  65. DWORD bytesWritten;
  66. ::WriteFile(procParams->mWriteHandle, buffer, bytesRead, &bytesWritten, NULL);
  67. }
  68. else
  69. {
  70. int err = GetLastError();
  71. break;
  72. }
  73. }
  74. return 0;
  75. }
  76. int main()
  77. {
  78. char* cmdLineStr = ::GetCommandLineA();
  79. DWORD flags = CREATE_DEFAULT_ERROR_MODE;
  80. void* envPtr = NULL;
  81. char* useCmdLineStr = cmdLineStr;
  82. if (cmdLineStr[0] != 0)
  83. {
  84. bool nameQuoted = cmdLineStr[0] == '\"';
  85. std::string passedName;
  86. int i;
  87. for (i = (nameQuoted ? 1 : 0); cmdLineStr[i] != 0; i++)
  88. {
  89. wchar_t c = cmdLineStr[i];
  90. if (((nameQuoted) && (c == '"')) ||
  91. ((!nameQuoted) && (c == ' ')))
  92. {
  93. i++;
  94. break;
  95. }
  96. passedName += cmdLineStr[i];
  97. }
  98. useCmdLineStr += i;
  99. while (*useCmdLineStr == L' ')
  100. useCmdLineStr++;
  101. }
  102. std::string cmdLine = useCmdLineStr;
  103. for (int pass = 0; pass < 2; pass++)
  104. {
  105. PROCESS_INFORMATION processInfo;
  106. STARTUPINFOA si;
  107. ZeroMemory(&si, sizeof(si));
  108. si.cb = sizeof(si);
  109. memset(&processInfo, 0, sizeof(processInfo));
  110. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  111. HANDLE stdOut;
  112. CreatePipe(stdOut, si.hStdOutput, false);
  113. HANDLE stdErr;
  114. CreatePipe(stdErr, si.hStdError, false);
  115. si.dwFlags = STARTF_USESTDHANDLES;
  116. DWORD startTick = GetTickCount();
  117. BOOL worked = CreateProcessA(NULL, (char*)cmdLine.c_str(), NULL, NULL, TRUE,
  118. flags, envPtr, NULL, &si, &processInfo);
  119. ::CloseHandle(si.hStdOutput);
  120. ::CloseHandle(si.hStdError);
  121. if (!worked)
  122. return 1;
  123. DWORD threadId;
  124. ProcParams stdOutParams = { stdOut, GetStdHandle(STD_OUTPUT_HANDLE) };
  125. HANDLE stdOutThread = ::CreateThread(NULL, (SIZE_T)128 * 1024, (LPTHREAD_START_ROUTINE)ReadProc, (void*)&stdOutParams, 0, &threadId);
  126. ProcParams stdErrParams = { stdErr, GetStdHandle(STD_ERROR_HANDLE) };
  127. HANDLE stdErrThread = ::CreateThread(NULL, (SIZE_T)128 * 1024, (LPTHREAD_START_ROUTINE)ReadProc, (void*)&stdErrParams, 0, &threadId);
  128. while (true)
  129. {
  130. if (::WaitForSingleObject(processInfo.hProcess, 20) == WAIT_OBJECT_0)
  131. break;
  132. }
  133. ::WaitForSingleObject(stdOutThread, INFINITE);
  134. ::WaitForSingleObject(stdErrThread, INFINITE);
  135. DWORD exitCode = 0;
  136. ::GetExitCodeProcess(processInfo.hProcess, &exitCode);
  137. printf("Exit code: %d\n", exitCode);
  138. if ((exitCode == 0) || (pass == 1))
  139. return exitCode;
  140. printf("FAILED! Starting second attempt.\n");
  141. }
  142. return 0;
  143. }