fork download
  1. #define STRICT
  2. #define WIN32_LEAN_AND_MEAN
  3. #include <windows.h>
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <io.h>
  7. #include <fcntl.h>
  8. #include <stdlib.h>
  9.  
  10. #include "spawn.h"
  11.  
  12. static void system_error(char const *name) {
  13. // A function to retrieve, format, and print out a message from the
  14. // last error. The `name' that's passed should be in the form of a
  15. // present tense noun (phrase) such as "opening file".
  16. //
  17. char *ptr = NULL;
  18. FormatMessage(
  19. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  20. FORMAT_MESSAGE_FROM_SYSTEM,
  21. 0,
  22. GetLastError(),
  23. 0,
  24. (char *)&ptr,
  25. 1024,
  26. NULL);
  27.  
  28. fprintf(stderr, "%s\n", ptr);
  29. LocalFree(ptr);
  30. }
  31.  
  32. static void InitializeInheritableSA(SECURITY_ATTRIBUTES *sa) {
  33.  
  34. sa->nLength = sizeof *sa;
  35. sa->bInheritHandle = TRUE;
  36. sa->lpSecurityDescriptor = NULL;
  37. }
  38.  
  39.  
  40. static HANDLE OpenInheritableFile(char const *name) {
  41. SECURITY_ATTRIBUTES sa;
  42. HANDLE retval;
  43.  
  44. InitializeInheritableSA(&sa);
  45.  
  46. retval = CreateFile(
  47. name,
  48. GENERIC_READ,
  49. FILE_SHARE_READ | FILE_SHARE_WRITE,
  50. &sa,
  51. OPEN_EXISTING,
  52. FILE_ATTRIBUTE_NORMAL,
  53. 0);
  54.  
  55.  
  56. if (INVALID_HANDLE_VALUE == retval) {
  57. char buffer[100];
  58.  
  59. sprintf(buffer, "opening file %s", name);
  60.  
  61. system_error(buffer);
  62. return retval;
  63. }
  64. }
  65.  
  66. static HANDLE CreateInheritableFile(char const *name, int mode) {
  67. SECURITY_ATTRIBUTES sa;
  68. HANDLE retval;
  69.  
  70. DWORD FSmode = mode ? OPEN_ALWAYS : CREATE_NEW;
  71.  
  72. InitializeInheritableSA(&sa);
  73.  
  74. retval = CreateFile(
  75. name,
  76. GENERIC_WRITE,
  77. FILE_SHARE_READ,
  78. &sa,
  79. FSmode,
  80. FILE_ATTRIBUTE_NORMAL,
  81. 0);
  82.  
  83. if (INVALID_HANDLE_VALUE == retval) {
  84. char buffer[100];
  85.  
  86. sprintf(buffer, "creating file %s", name);
  87.  
  88. system_error(buffer);
  89. return retval;
  90. }
  91.  
  92. if ( mode == APPEND )
  93. SetFilePointer(retval, 0, 0, FILE_END);
  94. }
  95.  
  96. enum inheritance { inherit_read = 1, inherit_write = 2 };
  97.  
  98. static BOOL CreateInheritablePipe(HANDLE *read, HANDLE *write, int inheritance) {
  99.  
  100. SECURITY_ATTRIBUTES sa;
  101.  
  102. InitializeInheritableSA(&sa);
  103.  
  104. if ( !CreatePipe(read, write, &sa, 0)) {
  105. system_error("Creating pipe");
  106. return FALSE;
  107. }
  108.  
  109. if (!inheritance & inherit_read)
  110. DuplicateHandle(
  111. GetCurrentProcess(),
  112. *read,
  113. GetCurrentProcess(),
  114. NULL,
  115. 0,
  116. FALSE,
  117. DUPLICATE_SAME_ACCESS);
  118.  
  119. if (!inheritance & inherit_write)
  120. DuplicateHandle(
  121. GetCurrentProcess(),
  122. *write,
  123. GetCurrentProcess(),
  124. NULL,
  125. 0,
  126. FALSE,
  127. DUPLICATE_SAME_ACCESS);
  128.  
  129. return TRUE;
  130. }
  131.  
  132. static BOOL find_image(char const *name, char *buffer) {
  133. // Try to find an image file named by the user.
  134. // First search for the exact file name in the current
  135. // directory. If that's found, look for same base name
  136. // with ".com", ".exe" and ".bat" appended, in that order.
  137. // If we can't find it in the current directory, repeat
  138. // the entire process on directories specified in the
  139. // PATH environment variable.
  140. //
  141. #define elements(array) (sizeof(array)/sizeof(array[0]))
  142.  
  143. static char *extensions[] = {".com", ".exe", ".bat", ".cmd"};
  144. int i;
  145. char temp[FILENAME_MAX];
  146.  
  147. if (-1 != access(name, 0)) {
  148. strcpy(buffer, name);
  149. return TRUE;
  150. }
  151.  
  152. for (i=0; i<elements(extensions); i++) {
  153. strcpy(temp, name);
  154. strcat(temp, extensions[i]);
  155. if ( -1 != access(temp, 0)) {
  156. strcpy(buffer, temp);
  157. return TRUE;
  158. }
  159. }
  160.  
  161. _searchenv(name, "PATH", buffer);
  162. if ( buffer[0] != '\0')
  163. return TRUE;
  164.  
  165. for ( i=0; i<elements(extensions); i++) {
  166. strcpy(temp, name);
  167. strcat(temp, extensions[i]);
  168. _searchenv(temp, "PATH", buffer);
  169. if ( buffer[0] != '\0')
  170. return TRUE;
  171. }
  172.  
  173. return FALSE;
  174. }
  175.  
  176.  
  177. static HANDLE DetachProcess(char const *name, HANDLE const *streams) {
  178. STARTUPINFO s;
  179. PROCESS_INFORMATION p;
  180. char buffer[FILENAME_MAX];
  181.  
  182. memset(&s, 0, sizeof s);
  183. s.cb = sizeof(s);
  184. s.dwFlags = STARTF_USESTDHANDLES;
  185. s.hStdInput = streams[0];
  186. s.hStdOutput = streams[1];
  187. s.hStdError = streams[2];
  188.  
  189. if ( !find_image(name, buffer)) {
  190. system_error("Finding Image file");
  191. return INVALID_HANDLE_VALUE;
  192. }
  193.  
  194. // Since we've redirected the standard input, output and error handles
  195. // of the child process, we create it without a console of its own.
  196. // (That's the `DETACHED_PROCESS' part of the call.) Other
  197. // possibilities include passing 0 so the child inherits our console,
  198. // or passing CREATE_NEW_CONSOLE so the child gets a console of its
  199. // own.
  200. //
  201. if (!CreateProcess(
  202. NULL,
  203. buffer, NULL, NULL,
  204. TRUE,
  205. DETACHED_PROCESS,
  206. NULL, NULL,
  207. &s,
  208. &p))
  209. {
  210. system_error("Spawning program");
  211. return INVALID_HANDLE_VALUE;
  212. }
  213.  
  214. // Since we don't need the handle to the child's thread, close it to
  215. // save some resources.
  216. CloseHandle(p.hThread);
  217.  
  218. return p.hProcess;
  219. }
  220.  
  221. static HANDLE StartStreamHandler(ThrdProc proc, HANDLE stream) {
  222.  
  223. DWORD ignore;
  224.  
  225. return CreateThread(
  226. NULL,
  227. 0,
  228. proc,
  229. (void *)stream,
  230. 0,
  231. &ignore);
  232. }
  233.  
  234. HANDLE CreateDetachedProcess(char const *name, stream_info *streams) {
  235. // This Creates a detached process.
  236. // First parameter: name of process to start.
  237. // Second parameter: names of files to redirect the standard input, output and error
  238. // streams of the child to (in that order.) Any file name that is NULL will be
  239. // redirected to an anonymous pipe connected to the parent.
  240. // Third Parameter: handles of the anonymous pipe(s) for the standard input, output
  241. // and/or error streams of the new child process.
  242. //
  243. // Return value: a handle to the newly created process.
  244. //
  245.  
  246. HANDLE child_handles[3];
  247. HANDLE process;
  248.  
  249. int i;
  250.  
  251. // First handle the child's standard input. This is separate from the
  252. // standard output and standard error because it's going the opposite
  253. // direction. Basically, we create either a handle to a file the child
  254. // will use, or else a pipe so the child can communicate with us.
  255. //
  256. if ( streams[0].filename != NULL ) {
  257. streams[0].handle = NULL;
  258. child_handles[0] = OpenInheritableFile(streams[0].filename);
  259. }
  260. else
  261. CreateInheritablePipe(child_handles, &(streams[0].handle), inherit_read);
  262.  
  263. // Now handle the child's standard output and standard error streams. These
  264. // are separate from the code above simply because they go in the opposite
  265. // direction.
  266. //
  267. for ( i=1; i<3; i++)
  268. if ( streams[i].filename != NULL) {
  269. streams[i].handle = NULL;
  270. child_handles[i] = CreateInheritableFile(streams[i].filename, APPEND);
  271. }
  272. else
  273. CreateInheritablePipe(&(streams[i].handle), child_handles+i, inherit_write);
  274.  
  275. // Now that we've set up the pipes and/or files the child's going to use,
  276. // we're ready to actually start up the child process:
  277. process = DetachProcess(name, child_handles);
  278. if (INVALID_HANDLE_VALUE == process)
  279. return process;
  280.  
  281. // Now that we've started the child, we close our handles to its ends of the pipes.
  282. // If one or more of these happens to a handle to a file instead, it doesn't really
  283. // need to be closed, but it doesn't hurt either. However, with the child's standard
  284. // output and standard error streams, it's CRUCIAL to close our handles if either is a
  285. // handle to a pipe. The system detects the end of data on a pipe when ALL handles to
  286. // the write end of the pipe are closed -- if we still have an open handle to the
  287. // write end of one of these pipes, we won't be able to detect when the child is done
  288. // writing to the pipe.
  289. //
  290. for ( i=0; i<3; i++) {
  291. CloseHandle(child_handles[i]);
  292. if ( streams[i].handler )
  293. streams[i].handle =
  294. StartStreamHandler(streams[i].handler, streams[i].handle);
  295. }
  296. return process;
  297. }
  298.  
  299. #ifdef TEST
  300.  
  301. #define buf_size 256
  302.  
  303. unsigned long __stdcall handle_error(void *pipe) {
  304. // The control (and only) function for a thread handling the standard
  305. // error from the child process. We'll handle it by displaying a
  306. // message box each time we receive data on the standard error stream.
  307. //
  308. char buffer[buf_size];
  309. HANDLE child_error_rd = (HANDLE)pipe;
  310. unsigned bytes;
  311.  
  312. while (ERROR_BROKEN_PIPE != GetLastError() &&
  313. ReadFile(child_error_rd, buffer, 256, &bytes, NULL))
  314. {
  315. buffer[bytes+1] = '\0';
  316. MessageBox(NULL, buffer, "Error", MB_OK);
  317. }
  318. return 0;
  319. }
  320.  
  321. unsigned long __stdcall handle_output(void *pipe) {
  322. // A similar thread function to handle standard output from the child
  323. // process. Nothing special is done with the output - it's simply
  324. // displayed in our console. However, just for fun it opens a C high-
  325. // level FILE * for the handle, and uses fgets to read it. As
  326. // expected, fgets detects the broken pipe as the end of the file.
  327. //
  328. char buffer[buf_size];
  329. int handle;
  330. FILE *file;
  331.  
  332. handle = _open_osfhandle((long)pipe, _O_RDONLY | _O_BINARY);
  333. file = _fdopen(handle, "r");
  334.  
  335. if ( NULL == file )
  336. return 1;
  337.  
  338. while ( fgets(buffer, buf_size, file))
  339. printf("%s", buffer);
  340.  
  341. return 0;
  342. }
  343.  
  344. int main(int argc, char **argv) {
  345.  
  346. stream_info streams[3];
  347. HANDLE handles[3];
  348. int i;
  349.  
  350. if ( argc < 3 ) {
  351. fputs("Usage: spawn prog datafile"
  352. "\nwhich will spawn `prog' with its standard input set to"
  353. "\nread from `datafile'. Then `prog's standard output"
  354. "\nwill be captured and printed. If `prog' writes to its"
  355. "\nstandard error, that output will be displayed in a"
  356. "\nMessageBox.\n",
  357. stderr);
  358. return 1;
  359. }
  360.  
  361. memset(streams, 0, sizeof(streams));
  362.  
  363. streams[0].filename = argv[2];
  364. streams[1].handler = handle_output;
  365. streams[2].handler = handle_error;
  366.  
  367. handles[0] = CreateDetachedProcess(argv[1], streams);
  368. handles[1] = streams[1].handle;
  369. handles[2] = streams[2].handle;
  370.  
  371. WaitForMultipleObjects(3, handles, TRUE, INFINITE);
  372.  
  373. for ( i=0; i<3; i++)
  374. CloseHandle(handles[i]);
  375.  
  376. return 0;
  377. }
  378.  
  379. #endif
  380.  
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cpp:3:21: error: windows.h: No such file or directory
prog.cpp:6:16: error: io.h: No such file or directory
prog.cpp: In function ‘void system_error(const char*)’:
prog.cpp:19: error: ‘FORMAT_MESSAGE_ALLOCATE_BUFFER’ was not declared in this scope
prog.cpp:20: error: ‘FORMAT_MESSAGE_FROM_SYSTEM’ was not declared in this scope
prog.cpp:22: error: ‘GetLastError’ was not declared in this scope
prog.cpp:26: error: ‘FormatMessage’ was not declared in this scope
prog.cpp:29: error: ‘LocalFree’ was not declared in this scope
prog.cpp: At global scope:
prog.cpp:32: error: variable or field ‘InitializeInheritableSA’ declared void
prog.cpp:32: error: ‘SECURITY_ATTRIBUTES’ was not declared in this scope
prog.cpp:32: error: ‘sa’ was not declared in this scope
prog.cpp:40: error: ‘HANDLE’ does not name a type
prog.cpp:66: error: ‘HANDLE’ does not name a type
prog.cpp:98: error: ‘BOOL’ does not name a type
prog.cpp:132: error: ‘BOOL’ does not name a type
prog.cpp:177: error: ‘HANDLE’ does not name a type
prog.cpp:221: error: ‘HANDLE’ does not name a type
prog.cpp:234: error: ‘HANDLE’ does not name a type
prog.cpp:12: warning: ‘void system_error(const char*)’ defined but not used
stdout
Standard output is empty