PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Samples/Extra/RedirectChildHandle/RedirectChildHandle.d

http://github.com/AndrejMitrovic/DWinProgramming
D | 235 lines | 148 code | 49 blank | 38 comment | 18 complexity | 4a77436b11512a90794cd643707698ba MD5 | raw file
  1. module RedirectChildHandle;
  2. // Note: See the Pipe sample instead, which is a minimal pipe example
  3. // with an execute() API same as the upcoming std.process module.
  4. pragma(lib, "gdi32.lib");
  5. import core.memory;
  6. import core.runtime;
  7. import core.thread;
  8. import core.stdc.string;
  9. import std.conv;
  10. import std.math;
  11. import std.range;
  12. import std.string;
  13. import std.utf;
  14. import core.sys.windows.windef;
  15. import core.sys.windows.winuser;
  16. import core.sys.windows.wingdi;
  17. import core.sys.windows.winbase;
  18. import std.algorithm;
  19. import std.array;
  20. import std.stdio;
  21. import std.conv;
  22. import std.typetuple;
  23. import std.typecons;
  24. import std.traits;
  25. enum BUFSIZE = 4096;
  26. // Todo: Comment from NG:
  27. // in RedirectChildHandle.d, CreateChildProcess, you should be closing childStdoutWrite and childStdinRead after CreateProcess. If you don't you get 2 copies of them, one in the child and one in the parent. If the child closes its copy the parent will not notice the pipe close (when reading from the other end of the pipe - for example). If you close it, it will mean that you will actually drop out of a blocking read in the parent immediately (when the child terminates), rather than timing out and then discovering the child has terminated (using the thread or process handle).
  28. HANDLE childStdinRead;
  29. HANDLE childStdinWrite;
  30. HANDLE childStdoutRead;
  31. HANDLE childStdoutWrite;
  32. HANDLE hInputFile;
  33. wstring fromUTF16z(const wchar* s)
  34. {
  35. if (s is null) return null;
  36. wchar* ptr;
  37. for (ptr = cast(wchar*)s; *ptr; ++ptr) {}
  38. return to!wstring(s[0..ptr-s]);
  39. }
  40. auto toUTF16z(S)(S s)
  41. {
  42. return toUTFz!(const(wchar)*)(s);
  43. }
  44. // console
  45. void main(string[] args)
  46. {
  47. string[] argv = ["RedirectStdChildProcess.exe", "empty.txt"]; // simulate args
  48. SECURITY_ATTRIBUTES saAttr;
  49. writeln("\n->Start of parent execution.\n");
  50. // Set the bInheritHandle flag so pipe handles are inherited.
  51. saAttr.nLength = SECURITY_ATTRIBUTES.sizeof;
  52. saAttr.bInheritHandle = true;
  53. // Create a pipe for the child process's STDOUT.
  54. if (!CreatePipe(/* out */ &childStdoutRead, /* out */ &childStdoutWrite, &saAttr, 0) )
  55. ErrorExit(("StdoutRd CreatePipe"));
  56. // Ensure the read handle to the pipe for STDOUT is not inherited (sets to 0)
  57. if (!SetHandleInformation(childStdoutRead, HANDLE_FLAG_INHERIT, 0) )
  58. ErrorExit(("Stdout SetHandleInformation"));
  59. // Create a pipe for the child process's STDIN.
  60. if (!CreatePipe(&childStdinRead, &childStdinWrite, &saAttr, 0))
  61. ErrorExit(("Stdin CreatePipe"));
  62. // Ensure the write handle to the pipe for STDIN is not inherited. (sets to 0)
  63. if (!SetHandleInformation(childStdinWrite, HANDLE_FLAG_INHERIT, 0) )
  64. ErrorExit(("Stdin SetHandleInformation"));
  65. // Create the child process.
  66. CreateChildProcess();
  67. // Read from pipe that is the standard output for child process.
  68. writeln("\n->Contents of child process STDOUT:\n\n", argv[1]);
  69. ReadFromPipe();
  70. writeln("\n->End of parent execution.\n");
  71. // The remaining open handles are cleaned up when this process terminates.
  72. // To avoid resource leaks in a larger application, close handles explicitly.
  73. }
  74. void CreateChildProcess()
  75. {
  76. // Create a child process that uses the previously created pipes for STDIN and STDOUT.
  77. auto szCmdline = toUTFz!(wchar*)("dmd");
  78. PROCESS_INFORMATION piProcInfo;
  79. STARTUPINFO siStartInfo;
  80. BOOL bSuccess = false;
  81. // Set up members of the STARTUPINFO structure.
  82. // This structure specifies the STDIN and STDOUT handles for redirection.
  83. siStartInfo.cb = STARTUPINFO.sizeof;
  84. siStartInfo.hStdError = childStdoutWrite; // we should replace this
  85. siStartInfo.hStdOutput = childStdoutWrite;
  86. siStartInfo.hStdInput = childStdinRead;
  87. siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
  88. // Create the child process.
  89. bSuccess = CreateProcess(NULL,
  90. szCmdline, // command line
  91. NULL, // process security attributes
  92. NULL, // primary thread security attributes
  93. true, // handles are inherited
  94. 0, // creation flags
  95. NULL, // use parent's environment
  96. NULL, // use parent's current directory
  97. &siStartInfo, // STARTUPINFO pointer
  98. &piProcInfo); // receives PROCESS_INFORMATION
  99. // If an error occurs, exit the application.
  100. if (!bSuccess)
  101. ErrorExit(("CreateProcess"));
  102. else
  103. {
  104. // Close handles to the child process and its primary thread.
  105. // Some applications might keep these handles to monitor the status
  106. // of the child process, for example.
  107. // close
  108. CloseHandle(childStdoutWrite);
  109. CloseHandle(childStdinRead);
  110. CloseHandle(piProcInfo.hProcess);
  111. CloseHandle(piProcInfo.hThread);
  112. }
  113. }
  114. void WriteToPipe()
  115. {
  116. // Read from a file and write its contents to the pipe for the child's STDIN.
  117. // Stop when there is no more data.
  118. DWORD dwRead, dwWritten;
  119. CHAR[BUFSIZE] chBuf;
  120. BOOL bSuccess = false;
  121. while (1)
  122. {
  123. bSuccess = ReadFile(hInputFile, chBuf.ptr, BUFSIZE, &dwRead, NULL);
  124. if (!bSuccess || dwRead == 0)
  125. break;
  126. bSuccess = WriteFile(childStdinWrite, chBuf.ptr, dwRead, &dwWritten, NULL);
  127. if (!bSuccess)
  128. break;
  129. }
  130. // Close the pipe handle so the child process stops reading.
  131. if (!CloseHandle(childStdinWrite) )
  132. ErrorExit(("StdInWr CloseHandle"));
  133. }
  134. void ReadFromPipe()
  135. {
  136. // Read output from the child process's pipe for STDOUT
  137. // and write to the parent process's pipe for STDOUT.
  138. // Stop when there is no more data.
  139. DWORD dwRead, dwWritten;
  140. CHAR[BUFSIZE] chBuf;
  141. BOOL bSuccess = false;
  142. HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  143. // Close the write end of the pipe before reading from the
  144. // read end of the pipe, to control child process execution.
  145. // The pipe is assumed to have enough buffer space to hold the
  146. // data the child process has already written to it.
  147. CloseHandle(childStdoutWrite);
  148. //~ if (!CloseHandle(childStdoutWrite))
  149. //~ ErrorExit(("StdOutWr CloseHandle"));
  150. while (1)
  151. {
  152. bSuccess = ReadFile(childStdoutRead, chBuf.ptr, BUFSIZE, &dwRead, NULL);
  153. if (!bSuccess || dwRead == 0)
  154. break;
  155. // here we would synchronously write
  156. bSuccess = WriteFile(hParentStdOut, chBuf.ptr, dwRead, &dwWritten, NULL);
  157. if (!bSuccess)
  158. break;
  159. }
  160. }
  161. void ErrorExit(string lpszFunction)
  162. {
  163. // Format a readable error message, display a message box,
  164. // and exit from the application.
  165. LPVOID lpMsgBuf;
  166. LPVOID lpDisplayBuf;
  167. DWORD dw = GetLastError();
  168. FormatMessage(
  169. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  170. FORMAT_MESSAGE_FROM_SYSTEM |
  171. FORMAT_MESSAGE_IGNORE_INSERTS,
  172. NULL,
  173. dw,
  174. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  175. cast(LPTSTR)&lpMsgBuf,
  176. 0, NULL);
  177. lpDisplayBuf = cast(LPVOID)LocalAlloc(LMEM_ZEROINIT,
  178. (lstrlen(cast(LPCTSTR)lpMsgBuf) + lstrlen(cast(LPCTSTR)lpszFunction) + 40) * (TCHAR.sizeof));
  179. auto str = format("%s failed with error %s: %s",
  180. lpszFunction,
  181. dw,
  182. fromUTF16z(cast(wchar*)lpMsgBuf)
  183. );
  184. writeln(str);
  185. ExitProcess(1);
  186. }