PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/Samples/Extra/RedirectChildHandleThreaded/RedirectChildHandleThreaded.d

http://github.com/AndrejMitrovic/DWinProgramming
D | 264 lines | 175 code | 45 blank | 44 comment | 12 complexity | 1276d5feac05d15523023ce93c6aec3d MD5 | raw file
  1. module RedirectChildHandleThreaded;
  2. pragma(lib, "gdi32.lib");
  3. import core.memory;
  4. import core.runtime;
  5. import core.thread;
  6. import core.stdc.string;
  7. import std.concurrency;
  8. import std.parallelism;
  9. import std.conv;
  10. import std.exception;
  11. import std.file;
  12. import std.math;
  13. import std.range;
  14. import std.string;
  15. import std.utf;
  16. import std.process;
  17. import core.sys.windows.windef;
  18. import core.sys.windows.winuser;
  19. import core.sys.windows.wingdi;
  20. import core.sys.windows.winbase;
  21. import std.algorithm;
  22. import std.array;
  23. import std.stdio;
  24. import std.conv;
  25. import std.typetuple;
  26. import std.typecons;
  27. import std.traits;
  28. enum BUFSIZE = 4096;
  29. HANDLE hInputFile;
  30. wstring fromUTF16z(const wchar* s)
  31. {
  32. if (s is null) return null;
  33. wchar* ptr;
  34. for (ptr = cast(wchar*)s; *ptr; ++ptr) {}
  35. return to!wstring(s[0..ptr-s]);
  36. }
  37. auto toUTF16z(S)(S s)
  38. {
  39. return toUTFz!(const(wchar)*)(s);
  40. }
  41. struct ProcessInfo
  42. {
  43. string procName;
  44. HANDLE childStdinRead;
  45. HANDLE childStdinWrite;
  46. HANDLE childStdoutRead;
  47. HANDLE childStdoutWrite;
  48. }
  49. void createProcessPipes(ref ProcessInfo procInfo)
  50. {
  51. SECURITY_ATTRIBUTES saAttr;
  52. // Set the bInheritHandle flag so pipe handles are inherited.
  53. saAttr.nLength = SECURITY_ATTRIBUTES.sizeof;
  54. saAttr.bInheritHandle = true;
  55. with (procInfo)
  56. {
  57. // Create a pipe for the child process's STDOUT.
  58. if (!CreatePipe(/* out */ &childStdoutRead, /* out */ &childStdoutWrite, &saAttr, 0) )
  59. ErrorExit(("StdoutRd CreatePipe"));
  60. // Ensure the read handle to the pipe for STDOUT is not inherited (sets to 0)
  61. if (!SetHandleInformation(childStdoutRead, HANDLE_FLAG_INHERIT, 0) )
  62. ErrorExit(("Stdout SetHandleInformation"));
  63. // Create a pipe for the child process's STDIN.
  64. if (!CreatePipe(&childStdinRead, &childStdinWrite, &saAttr, 0))
  65. ErrorExit(("Stdin CreatePipe"));
  66. // Ensure the write handle to the pipe for STDIN is not inherited. (sets to 0)
  67. if (!SetHandleInformation(childStdinWrite, HANDLE_FLAG_INHERIT, 0) )
  68. ErrorExit(("Stdin SetHandleInformation"));
  69. }
  70. }
  71. void makeProcess(size_t index, string procName)
  72. {
  73. makeProcess(procName, processInfos[index]);
  74. }
  75. void makeProcess(string procName, ProcessInfo procInfo)
  76. {
  77. // Create a child process that uses the previously created pipes for STDIN and STDOUT.
  78. auto szCmdline = toUTFz!(wchar*)(procName);
  79. PROCESS_INFORMATION piProcInfo;
  80. STARTUPINFO siStartInfo;
  81. BOOL bSuccess = false;
  82. // Set up members of the STARTUPINFO structure.
  83. // This structure specifies the STDIN and STDOUT handles for redirection.
  84. siStartInfo.cb = STARTUPINFO.sizeof;
  85. siStartInfo.hStdError = procInfo.childStdoutWrite; // we should replace this
  86. siStartInfo.hStdOutput = procInfo.childStdoutWrite;
  87. siStartInfo.hStdInput = procInfo.childStdinRead;
  88. siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
  89. // Create the child process.
  90. bSuccess = CreateProcess(NULL,
  91. szCmdline, // command line
  92. NULL, // process security attributes
  93. NULL, // primary thread security attributes
  94. true, // handles are inherited
  95. 0, // creation flags
  96. NULL, // use parent's environment
  97. NULL, // use parent's current directory
  98. &siStartInfo, // STARTUPINFO pointer
  99. &piProcInfo); // receives PROCESS_INFORMATION
  100. // If an error occurs, exit the application.
  101. if (!bSuccess)
  102. ErrorExit(("CreateProcess"));
  103. else
  104. {
  105. // Close handles to the child process and its primary thread.
  106. // Some applications might keep these handles to monitor the status
  107. // of the child process, for example.
  108. CloseHandle(piProcInfo.hProcess);
  109. CloseHandle(piProcInfo.hThread);
  110. }
  111. }
  112. /*
  113. * Note: If you want to print out in parallel you'll have to wrap the entire
  114. * writefln section in a synchronized block. E.g.:
  115. * synchronized { writefln(); while (1) { writefln("..."); } writeln(); }
  116. */
  117. void readProcessPipe(size_t index, ProcessInfo procInfo)
  118. {
  119. // Read output from the child process's pipe for STDOUT
  120. // and write to the parent process's pipe for STDOUT.
  121. // Stop when there is no more data.
  122. DWORD dwRead, dwWritten;
  123. CHAR[BUFSIZE] chBuf;
  124. BOOL bSuccess = false;
  125. HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  126. // Close the write end of the pipe before reading from the
  127. // read end of the pipe, to control child process execution.
  128. // The pipe is assumed to have enough buffer space to hold the
  129. // data the child process has already written to it.
  130. if (!CloseHandle(procInfo.childStdoutWrite))
  131. ErrorExit(("StdOutWr CloseHandle"));
  132. writefln("Process #%s:", index);
  133. while (1)
  134. {
  135. bSuccess = ReadFile(procInfo.childStdoutRead, chBuf.ptr, BUFSIZE, &dwRead, NULL);
  136. if (!bSuccess || dwRead == 0)
  137. break;
  138. // note: don't call writeln as you'll have text broken on new lines,
  139. // but make sure to either flush via stdout.flush() or call writeln();
  140. // write/writef don't flush
  141. write(chBuf[0..dwRead]);
  142. }
  143. writeln();
  144. }
  145. __gshared ProcessInfo[50] processInfos;
  146. /*
  147. * Pick between spawn or taskPool.parallel. To make them
  148. * both work I've had to make processInfos global and add
  149. * forwarding functions for spawn(), which load a process
  150. * info by index.
  151. */
  152. version = StdConcurrency;
  153. //~ version = StdParallelism;
  154. void main(string[] args)
  155. {
  156. // workaround: build.d tries to build stub.d if it's present
  157. system(`echo module stub; void main() { } > stub.d`);
  158. scope(exit) { std.file.remove("stub.d"); }
  159. foreach (ref procInfo; processInfos)
  160. {
  161. createProcessPipes(procInfo);
  162. }
  163. writeln("\n->Start of parent execution.\n");
  164. version (StdParallelism)
  165. {
  166. foreach (procInfo; taskPool.parallel(processInfos[], 1))
  167. {
  168. makeProcess(r"dmd stub.d", procInfo);
  169. }
  170. }
  171. else
  172. version (StdConcurrency)
  173. {
  174. foreach (index; 0 .. processInfos.length)
  175. {
  176. spawn(&makeProcess, index, r"dmd stub.d");
  177. }
  178. }
  179. else
  180. static assert("Set version to StdParallelism or StdConcurrency");
  181. thread_joinAll();
  182. // read out sequentally, if you want to do it in parallel you have to make
  183. // sure you don't interleave your writeln calls (see readProcessPipe)
  184. foreach (index, procInfo; processInfos)
  185. {
  186. readProcessPipe(index, procInfo);
  187. }
  188. writeln("\n->End of parent execution.\n");
  189. // The remaining open handles are cleaned up when this process terminates.
  190. // To avoid resource leaks in a larger application, close handles explicitly.
  191. }
  192. void ErrorExit(string lpszFunction)
  193. {
  194. // Format a readable error message, display a message box,
  195. // and exit from the application.
  196. LPVOID lpMsgBuf;
  197. LPVOID lpDisplayBuf;
  198. DWORD dw = GetLastError();
  199. FormatMessage(
  200. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  201. FORMAT_MESSAGE_FROM_SYSTEM |
  202. FORMAT_MESSAGE_IGNORE_INSERTS,
  203. NULL,
  204. dw,
  205. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  206. cast(LPTSTR)&lpMsgBuf,
  207. 0, NULL);
  208. lpDisplayBuf = cast(LPVOID)LocalAlloc(LMEM_ZEROINIT,
  209. (lstrlen(cast(LPCTSTR)lpMsgBuf) + lstrlen(cast(LPCTSTR)lpszFunction) + 40) * (TCHAR.sizeof));
  210. auto str = format("%s failed with error %s: %s",
  211. lpszFunction,
  212. dw,
  213. fromUTF16z(cast(wchar*)lpMsgBuf)
  214. );
  215. writeln(str);
  216. // protip: never use exit/exitProcess, your scope() statements won't run
  217. // and you'll end up with garbage on the drive (temporary files), etc.
  218. enforce(0);
  219. }