1 /*
2  * support routines for subprocess module
3  *
4  * Currently, this extension module is only required when using the
5  * subprocess module on Windows, but in the future, stubs for other
6  * platforms might be added here as well.
7  *
8  * Copyright (c) 2004 by Fredrik Lundh <fredrik@pythonware.com>
9  * Copyright (c) 2004 by Secret Labs AB, http://www.pythonware.com
10  * Copyright (c) 2004 by Peter Astrand <astrand@lysator.liu.se>
11  *
12  * By obtaining, using, and/or copying this software and/or its
13  * associated documentation, you agree that you have read, understood,
14  * and will comply with the following terms and conditions:
15  *
16  * Permission to use, copy, modify, and distribute this software and
17  * its associated documentation for any purpose and without fee is
18  * hereby granted, provided that the above copyright notice appears in
19  * all copies, and that both that copyright notice and this permission
20  * notice appear in supporting documentation, and that the name of the
21  * authors not be used in advertising or publicity pertaining to
22  * distribution of the software without specific, written prior
23  * permission.
24  *
25  * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
26  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
27  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
28  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
29  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
30  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
31  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32  *
33  */
34 
35 /* Licensed to PSF under a Contributor Agreement. */
36 /* See http://www.python.org/2.4/license for licensing details. */
37 
38 /* TODO: handle unicode command lines? */
39 /* TODO: handle unicode environment? */
40 
41 #include "Python.h"
42 
43 #define WINDOWS_LEAN_AND_MEAN
44 #include "windows.h"
45 
46 /* -------------------------------------------------------------------- */
47 /* handle wrapper.  note that this library uses integers when passing
48    handles to a function, and handle wrappers when returning handles.
49    the wrapper is used to provide Detach and Close methods */
50 
51 typedef struct {
52     PyObject_HEAD
53     HANDLE handle;
54 } sp_handle_object;
55 
56 staticforward PyTypeObject sp_handle_type;
57 
58 static PyObject*
sp_handle_new(HANDLE handle)59 sp_handle_new(HANDLE handle)
60 {
61     sp_handle_object* self;
62 
63     self = PyObject_NEW(sp_handle_object, &sp_handle_type);
64     if (self == NULL)
65         return NULL;
66 
67     self->handle = handle;
68 
69     return (PyObject*) self;
70 }
71 
72 #if defined(MS_WIN32) && !defined(MS_WIN64)
73 #define HANDLE_TO_PYNUM(handle) PyInt_FromLong((long) handle)
74 #define PY_HANDLE_PARAM "l"
75 #else
76 #define HANDLE_TO_PYNUM(handle) PyLong_FromLongLong((long long) handle)
77 #define PY_HANDLE_PARAM "L"
78 #endif
79 
80 static PyObject*
sp_handle_detach(sp_handle_object * self,PyObject * args)81 sp_handle_detach(sp_handle_object* self, PyObject* args)
82 {
83     HANDLE handle;
84 
85     if (! PyArg_ParseTuple(args, ":Detach"))
86         return NULL;
87 
88     handle = self->handle;
89 
90     self->handle = INVALID_HANDLE_VALUE;
91 
92     /* note: return the current handle, as an integer */
93     return HANDLE_TO_PYNUM(handle);
94 }
95 
96 static PyObject*
sp_handle_close(sp_handle_object * self,PyObject * args)97 sp_handle_close(sp_handle_object* self, PyObject* args)
98 {
99     if (! PyArg_ParseTuple(args, ":Close"))
100         return NULL;
101 
102     if (self->handle != INVALID_HANDLE_VALUE) {
103         CloseHandle(self->handle);
104         self->handle = INVALID_HANDLE_VALUE;
105     }
106     Py_INCREF(Py_None);
107     return Py_None;
108 }
109 
110 static void
sp_handle_dealloc(sp_handle_object * self)111 sp_handle_dealloc(sp_handle_object* self)
112 {
113     if (self->handle != INVALID_HANDLE_VALUE)
114         CloseHandle(self->handle);
115     PyObject_FREE(self);
116 }
117 
118 static PyMethodDef sp_handle_methods[] = {
119     {"Detach", (PyCFunction) sp_handle_detach, METH_VARARGS},
120     {"Close",  (PyCFunction) sp_handle_close,  METH_VARARGS},
121     {NULL, NULL}
122 };
123 
124 static PyObject*
sp_handle_getattr(sp_handle_object * self,char * name)125 sp_handle_getattr(sp_handle_object* self, char* name)
126 {
127     return Py_FindMethod(sp_handle_methods, (PyObject*) self, name);
128 }
129 
130 static PyObject*
sp_handle_as_int(sp_handle_object * self)131 sp_handle_as_int(sp_handle_object* self)
132 {
133     return HANDLE_TO_PYNUM(self->handle);
134 }
135 
136 static PyNumberMethods sp_handle_as_number;
137 
138 statichere PyTypeObject sp_handle_type = {
139     PyObject_HEAD_INIT(NULL)
140     0,                                  /*ob_size*/
141     "_subprocess_handle", sizeof(sp_handle_object), 0,
142     (destructor) sp_handle_dealloc, /*tp_dealloc*/
143     0, /*tp_print*/
144     (getattrfunc) sp_handle_getattr,/*tp_getattr*/
145     0,                                  /*tp_setattr*/
146     0,                                  /*tp_compare*/
147     0,                                  /*tp_repr*/
148     &sp_handle_as_number,               /*tp_as_number */
149     0,                                  /*tp_as_sequence */
150     0,                                  /*tp_as_mapping */
151     0                                   /*tp_hash*/
152 };
153 
154 /* -------------------------------------------------------------------- */
155 /* windows API functions */
156 
157 PyDoc_STRVAR(GetStdHandle_doc,
158 "GetStdHandle(handle) -> integer\n\
159 \n\
160 Return a handle to the specified standard device\n\
161 (STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE).\n\
162 The integer associated with the handle object is returned.");
163 
164 static PyObject *
sp_GetStdHandle(PyObject * self,PyObject * args)165 sp_GetStdHandle(PyObject* self, PyObject* args)
166 {
167     HANDLE handle;
168     int std_handle;
169 
170     if (! PyArg_ParseTuple(args, "i:GetStdHandle", &std_handle))
171         return NULL;
172 
173     Py_BEGIN_ALLOW_THREADS
174     handle = GetStdHandle((DWORD) std_handle);
175     Py_END_ALLOW_THREADS
176 
177     if (handle == INVALID_HANDLE_VALUE)
178         return PyErr_SetFromWindowsErr(GetLastError());
179 
180     if (! handle) {
181         Py_INCREF(Py_None);
182         return Py_None;
183     }
184 
185     /* note: returns integer, not handle object */
186     return HANDLE_TO_PYNUM(handle);
187 }
188 
189 PyDoc_STRVAR(GetCurrentProcess_doc,
190 "GetCurrentProcess() -> handle\n\
191 \n\
192 Return a handle object for the current process.");
193 
194 static PyObject *
sp_GetCurrentProcess(PyObject * self,PyObject * args)195 sp_GetCurrentProcess(PyObject* self, PyObject* args)
196 {
197     if (! PyArg_ParseTuple(args, ":GetCurrentProcess"))
198         return NULL;
199 
200     return sp_handle_new(GetCurrentProcess());
201 }
202 
203 PyDoc_STRVAR(DuplicateHandle_doc,
204 "DuplicateHandle(source_proc_handle, source_handle,\n\
205                  target_proc_handle, target_handle, access,\n\
206                  inherit[, options]) -> handle\n\
207 \n\
208 Return a duplicate handle object.\n\
209 \n\
210 The duplicate handle refers to the same object as the original\n\
211 handle. Therefore, any changes to the object are reflected\n\
212 through both handles.");
213 
214 static PyObject *
sp_DuplicateHandle(PyObject * self,PyObject * args)215 sp_DuplicateHandle(PyObject* self, PyObject* args)
216 {
217     HANDLE target_handle;
218     BOOL result;
219 
220     HANDLE source_process_handle;
221     HANDLE source_handle;
222     HANDLE target_process_handle;
223     int desired_access;
224     int inherit_handle;
225     int options = 0;
226 
227     if (! PyArg_ParseTuple(args,
228                            PY_HANDLE_PARAM PY_HANDLE_PARAM PY_HANDLE_PARAM
229                            "ii|i:DuplicateHandle",
230                            &source_process_handle,
231                            &source_handle,
232                            &target_process_handle,
233                            &desired_access,
234                            &inherit_handle,
235                            &options))
236         return NULL;
237 
238     Py_BEGIN_ALLOW_THREADS
239     result = DuplicateHandle(
240         source_process_handle,
241         source_handle,
242         target_process_handle,
243         &target_handle,
244         desired_access,
245         inherit_handle,
246         options
247     );
248     Py_END_ALLOW_THREADS
249 
250     if (! result)
251         return PyErr_SetFromWindowsErr(GetLastError());
252 
253     return sp_handle_new(target_handle);
254 }
255 
256 PyDoc_STRVAR(CreatePipe_doc,
257 "CreatePipe(pipe_attrs, size) -> (read_handle, write_handle)\n\
258 \n\
259 Create an anonymous pipe, and return handles to the read and\n\
260 write ends of the pipe.\n\
261 \n\
262 pipe_attrs is ignored internally and can be None.");
263 
264 static PyObject *
sp_CreatePipe(PyObject * self,PyObject * args)265 sp_CreatePipe(PyObject* self, PyObject* args)
266 {
267     HANDLE read_pipe;
268     HANDLE write_pipe;
269     BOOL result;
270 
271     PyObject* pipe_attributes; /* ignored */
272     int size;
273 
274     if (! PyArg_ParseTuple(args, "Oi:CreatePipe", &pipe_attributes, &size))
275         return NULL;
276 
277     Py_BEGIN_ALLOW_THREADS
278     result = CreatePipe(&read_pipe, &write_pipe, NULL, size);
279     Py_END_ALLOW_THREADS
280 
281     if (! result)
282         return PyErr_SetFromWindowsErr(GetLastError());
283 
284     return Py_BuildValue(
285         "NN", sp_handle_new(read_pipe), sp_handle_new(write_pipe));
286 }
287 
288 /* helpers for createprocess */
289 
290 static int
getint(PyObject * obj,char * name)291 getint(PyObject* obj, char* name)
292 {
293     PyObject* value;
294     int ret;
295 
296     value = PyObject_GetAttrString(obj, name);
297     if (! value) {
298         PyErr_Clear(); /* FIXME: propagate error? */
299         return 0;
300     }
301     ret = (int) PyInt_AsLong(value);
302     Py_DECREF(value);
303     return ret;
304 }
305 
306 static HANDLE
gethandle(PyObject * obj,char * name)307 gethandle(PyObject* obj, char* name)
308 {
309     sp_handle_object* value;
310     HANDLE ret;
311 
312     value = (sp_handle_object*) PyObject_GetAttrString(obj, name);
313     if (! value) {
314         PyErr_Clear(); /* FIXME: propagate error? */
315         return NULL;
316     }
317     if (value->ob_type != &sp_handle_type)
318         ret = NULL;
319     else
320         ret = value->handle;
321     Py_DECREF(value);
322     return ret;
323 }
324 
325 static PyObject*
getenvironment(PyObject * environment)326 getenvironment(PyObject* environment)
327 {
328     int i, envsize;
329     PyObject* out = NULL;
330     PyObject* keys;
331     PyObject* values;
332     char* p;
333 
334     /* convert environment dictionary to windows environment string */
335     if (! PyMapping_Check(environment)) {
336         PyErr_SetString(
337             PyExc_TypeError, "environment must be dictionary or None");
338         return NULL;
339     }
340 
341     envsize = PyMapping_Length(environment);
342 
343     keys = PyMapping_Keys(environment);
344     if (!keys) {
345         return NULL;
346     }
347     values = PyMapping_Values(environment);
348     if (!values) {
349         goto error;
350     }
351 
352     out = PyString_FromStringAndSize(NULL, 2048);
353     if (! out)
354         goto error;
355 
356     p = PyString_AS_STRING(out);
357 
358     for (i = 0; i < envsize; i++) {
359         size_t ksize, vsize, totalsize;
360         PyObject* key = PyList_GET_ITEM(keys, i);
361         PyObject* value = PyList_GET_ITEM(values, i);
362 
363         if (! PyString_Check(key) || ! PyString_Check(value)) {
364             PyErr_SetString(PyExc_TypeError,
365                 "environment can only contain strings");
366             goto error;
367         }
368         ksize = PyString_GET_SIZE(key);
369         vsize = PyString_GET_SIZE(value);
370         if (strlen(PyString_AS_STRING(key)) != ksize ||
371             strlen(PyString_AS_STRING(value)) != vsize)
372         {
373             PyErr_SetString(PyExc_TypeError, "embedded null character");
374             goto error;
375         }
376         /* Search from index 1 because on Windows starting '=' is allowed for
377            defining hidden environment variables. */
378         if (ksize == 0 || strchr(PyString_AS_STRING(key) + 1, '=') != NULL) {
379             PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
380             goto error;
381         }
382         totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 +
383                                                      vsize + 1 + 1;
384         if (totalsize > (size_t)PyString_GET_SIZE(out)) {
385             size_t offset = p - PyString_AS_STRING(out);
386             if (_PyString_Resize(&out, totalsize + 1024))
387                 goto exit;
388             p = PyString_AS_STRING(out) + offset;
389         }
390         memcpy(p, PyString_AS_STRING(key), ksize);
391         p += ksize;
392         *p++ = '=';
393         memcpy(p, PyString_AS_STRING(value), vsize);
394         p += vsize;
395         *p++ = '\0';
396     }
397 
398     /* add trailing null byte */
399     *p++ = '\0';
400     _PyString_Resize(&out, p - PyString_AS_STRING(out));
401 
402     /* PyObject_Print(out, stdout, 0); */
403 exit:
404     Py_XDECREF(keys);
405     Py_XDECREF(values);
406 
407     return out;
408 
409  error:
410     Py_XDECREF(out);
411     Py_XDECREF(keys);
412     Py_XDECREF(values);
413     return NULL;
414 }
415 
416 PyDoc_STRVAR(CreateProcess_doc,
417 "CreateProcess(app_name, cmd_line, proc_attrs, thread_attrs,\n\
418                inherit, flags, env_mapping, curdir,\n\
419                startup_info) -> (proc_handle, thread_handle,\n\
420                                  pid, tid)\n\
421 \n\
422 Create a new process and its primary thread. The return\n\
423 value is a tuple of the process handle, thread handle,\n\
424 process ID, and thread ID.\n\
425 \n\
426 proc_attrs and thread_attrs are ignored internally and can be None.");
427 
428 static PyObject *
sp_CreateProcess(PyObject * self,PyObject * args)429 sp_CreateProcess(PyObject* self, PyObject* args)
430 {
431     BOOL result;
432     PROCESS_INFORMATION pi;
433     STARTUPINFO si;
434     PyObject* environment;
435 
436     char* application_name;
437     char* command_line;
438     PyObject* process_attributes; /* ignored */
439     PyObject* thread_attributes; /* ignored */
440     int inherit_handles;
441     int creation_flags;
442     PyObject* env_mapping;
443     char* current_directory;
444     PyObject* startup_info;
445 
446     if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess",
447                            &application_name,
448                            &command_line,
449                            &process_attributes,
450                            &thread_attributes,
451                            &inherit_handles,
452                            &creation_flags,
453                            &env_mapping,
454                            &current_directory,
455                            &startup_info))
456         return NULL;
457 
458     ZeroMemory(&si, sizeof(si));
459     si.cb = sizeof(si);
460 
461     /* note: we only support a small subset of all SI attributes */
462     si.dwFlags = getint(startup_info, "dwFlags");
463     si.wShowWindow = getint(startup_info, "wShowWindow");
464     si.hStdInput = gethandle(startup_info, "hStdInput");
465     si.hStdOutput = gethandle(startup_info, "hStdOutput");
466     si.hStdError = gethandle(startup_info, "hStdError");
467 
468     if (PyErr_Occurred())
469         return NULL;
470 
471     if (env_mapping == Py_None)
472         environment = NULL;
473     else {
474         environment = getenvironment(env_mapping);
475         if (! environment)
476             return NULL;
477     }
478 
479     Py_BEGIN_ALLOW_THREADS
480     result = CreateProcess(application_name,
481                            command_line,
482                            NULL,
483                            NULL,
484                            inherit_handles,
485                            creation_flags,
486                            environment ? PyString_AS_STRING(environment) : NULL,
487                            current_directory,
488                            &si,
489                            &pi);
490     Py_END_ALLOW_THREADS
491 
492     Py_XDECREF(environment);
493 
494     if (! result)
495         return PyErr_SetFromWindowsErr(GetLastError());
496 
497     return Py_BuildValue("NNii",
498                          sp_handle_new(pi.hProcess),
499                          sp_handle_new(pi.hThread),
500                          pi.dwProcessId,
501                          pi.dwThreadId);
502 }
503 
504 PyDoc_STRVAR(TerminateProcess_doc,
505 "TerminateProcess(handle, exit_code) -> None\n\
506 \n\
507 Terminate the specified process and all of its threads.");
508 
509 static PyObject *
sp_TerminateProcess(PyObject * self,PyObject * args)510 sp_TerminateProcess(PyObject* self, PyObject* args)
511 {
512     BOOL result;
513 
514     HANDLE process;
515     int exit_code;
516     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:TerminateProcess",
517                            &process, &exit_code))
518         return NULL;
519 
520     result = TerminateProcess(process, exit_code);
521 
522     if (! result)
523         return PyErr_SetFromWindowsErr(GetLastError());
524 
525     Py_INCREF(Py_None);
526     return Py_None;
527 }
528 
529 PyDoc_STRVAR(GetExitCodeProcess_doc,
530 "GetExitCodeProcess(handle) -> Exit code\n\
531 \n\
532 Return the termination status of the specified process.");
533 
534 static PyObject *
sp_GetExitCodeProcess(PyObject * self,PyObject * args)535 sp_GetExitCodeProcess(PyObject* self, PyObject* args)
536 {
537     DWORD exit_code;
538     BOOL result;
539 
540     HANDLE process;
541     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetExitCodeProcess", &process))
542         return NULL;
543 
544     result = GetExitCodeProcess(process, &exit_code);
545 
546     if (! result)
547         return PyErr_SetFromWindowsErr(GetLastError());
548 
549     return PyInt_FromLong(exit_code);
550 }
551 
552 PyDoc_STRVAR(WaitForSingleObject_doc,
553 "WaitForSingleObject(handle, timeout) -> result\n\
554 \n\
555 Wait until the specified object is in the signaled state or\n\
556 the time-out interval elapses. The timeout value is specified\n\
557 in milliseconds.");
558 
559 static PyObject *
sp_WaitForSingleObject(PyObject * self,PyObject * args)560 sp_WaitForSingleObject(PyObject* self, PyObject* args)
561 {
562     DWORD result;
563 
564     HANDLE handle;
565     int milliseconds;
566     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:WaitForSingleObject",
567                                  &handle,
568                                  &milliseconds))
569         return NULL;
570 
571     Py_BEGIN_ALLOW_THREADS
572     result = WaitForSingleObject(handle, (DWORD) milliseconds);
573     Py_END_ALLOW_THREADS
574 
575     if (result == WAIT_FAILED)
576         return PyErr_SetFromWindowsErr(GetLastError());
577 
578     return PyInt_FromLong((int) result);
579 }
580 
581 PyDoc_STRVAR(GetVersion_doc,
582 "GetVersion() -> version\n\
583 \n\
584 Return the version number of the current operating system.");
585 
586 static PyObject *
sp_GetVersion(PyObject * self,PyObject * args)587 sp_GetVersion(PyObject* self, PyObject* args)
588 {
589     if (! PyArg_ParseTuple(args, ":GetVersion"))
590         return NULL;
591 
592     return PyInt_FromLong((int) GetVersion());
593 }
594 
595 PyDoc_STRVAR(GetModuleFileName_doc,
596 "GetModuleFileName(module) -> path\n\
597 \n\
598 Return the fully-qualified path for the file that contains\n\
599 the specified module. The module must have been loaded by the\n\
600 current process.\n\
601 \n\
602 The module parameter should be a handle to the loaded module\n\
603 whose path is being requested. If this parameter is 0, \n\
604 GetModuleFileName retrieves the path of the executable file\n\
605 of the current process.");
606 
607 static PyObject *
sp_GetModuleFileName(PyObject * self,PyObject * args)608 sp_GetModuleFileName(PyObject* self, PyObject* args)
609 {
610     BOOL result;
611     HMODULE module;
612     TCHAR filename[MAX_PATH];
613 
614     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetModuleFileName",
615                            &module))
616         return NULL;
617 
618     result = GetModuleFileName(module, filename, MAX_PATH);
619     filename[MAX_PATH-1] = '\0';
620 
621     if (! result)
622         return PyErr_SetFromWindowsErr(GetLastError());
623 
624     return PyString_FromString(filename);
625 }
626 
627 static PyMethodDef sp_functions[] = {
628     {"GetStdHandle", sp_GetStdHandle, METH_VARARGS, GetStdHandle_doc},
629     {"GetCurrentProcess", sp_GetCurrentProcess,         METH_VARARGS,
630                                               GetCurrentProcess_doc},
631     {"DuplicateHandle",         sp_DuplicateHandle,     METH_VARARGS,
632                                             DuplicateHandle_doc},
633     {"CreatePipe", sp_CreatePipe, METH_VARARGS, CreatePipe_doc},
634     {"CreateProcess", sp_CreateProcess, METH_VARARGS, CreateProcess_doc},
635     {"TerminateProcess", sp_TerminateProcess, METH_VARARGS,
636                                              TerminateProcess_doc},
637     {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS,
638                                                GetExitCodeProcess_doc},
639     {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS,
640                                                     WaitForSingleObject_doc},
641     {"GetVersion", sp_GetVersion, METH_VARARGS, GetVersion_doc},
642     {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS,
643                                               GetModuleFileName_doc},
644     {NULL, NULL}
645 };
646 
647 /* -------------------------------------------------------------------- */
648 
649 static void
defint(PyObject * d,const char * name,int value)650 defint(PyObject* d, const char* name, int value)
651 {
652     PyObject* v = PyInt_FromLong((long) value);
653     if (v) {
654         PyDict_SetItemString(d, (char*) name, v);
655         Py_DECREF(v);
656     }
657 }
658 
659 #if PY_VERSION_HEX >= 0x02030000
660 PyMODINIT_FUNC
661 #else
662 DL_EXPORT(void)
663 #endif
init_subprocess()664 init_subprocess()
665 {
666     PyObject *d;
667     PyObject *m;
668 
669     /* patch up object descriptors */
670     sp_handle_type.ob_type = &PyType_Type;
671     sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int;
672 
673     m = Py_InitModule("_subprocess", sp_functions);
674     if (m == NULL)
675         return;
676     d = PyModule_GetDict(m);
677 
678     /* constants */
679     defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE);
680     defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE);
681     defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE);
682     defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS);
683     defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES);
684     defint(d, "STARTF_USESHOWWINDOW", STARTF_USESHOWWINDOW);
685     defint(d, "SW_HIDE", SW_HIDE);
686     defint(d, "INFINITE", INFINITE);
687     defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
688     defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
689     defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
690     defint(d, "STILL_ACTIVE", STILL_ACTIVE);
691 }
692