Home | History | Annotate | Download | only in PC
      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 (at) pythonware.com>
      9  * Copyright (c) 2004 by Secret Labs AB, http://www.pythonware.com
     10  * Copyright (c) 2004 by Peter Astrand <astrand (at) 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*
     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*
     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*
     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
    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*
    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*
    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 *
    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 *
    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 *
    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 *
    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
    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
    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*
    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     values = PyMapping_Values(environment);
    345     if (!keys || !values)
    346         goto error;
    347 
    348     out = PyString_FromStringAndSize(NULL, 2048);
    349     if (! out)
    350         goto error;
    351 
    352     p = PyString_AS_STRING(out);
    353 
    354     for (i = 0; i < envsize; i++) {
    355         int ksize, vsize, totalsize;
    356         PyObject* key = PyList_GET_ITEM(keys, i);
    357         PyObject* value = PyList_GET_ITEM(values, i);
    358 
    359         if (! PyString_Check(key) || ! PyString_Check(value)) {
    360             PyErr_SetString(PyExc_TypeError,
    361                 "environment can only contain strings");
    362             goto error;
    363         }
    364         ksize = PyString_GET_SIZE(key);
    365         vsize = PyString_GET_SIZE(value);
    366         totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 +
    367                                                      vsize + 1 + 1;
    368         if (totalsize > PyString_GET_SIZE(out)) {
    369             int offset = p - PyString_AS_STRING(out);
    370             if (_PyString_Resize(&out, totalsize + 1024))
    371                 goto exit;
    372             p = PyString_AS_STRING(out) + offset;
    373         }
    374         memcpy(p, PyString_AS_STRING(key), ksize);
    375         p += ksize;
    376         *p++ = '=';
    377         memcpy(p, PyString_AS_STRING(value), vsize);
    378         p += vsize;
    379         *p++ = '\0';
    380     }
    381 
    382     /* add trailing null byte */
    383     *p++ = '\0';
    384     _PyString_Resize(&out, p - PyString_AS_STRING(out));
    385 
    386     /* PyObject_Print(out, stdout, 0); */
    387 exit:
    388     Py_XDECREF(keys);
    389     Py_XDECREF(values);
    390 
    391     return out;
    392 
    393  error:
    394     Py_XDECREF(out);
    395     Py_XDECREF(keys);
    396     Py_XDECREF(values);
    397     return NULL;
    398 }
    399 
    400 PyDoc_STRVAR(CreateProcess_doc,
    401 "CreateProcess(app_name, cmd_line, proc_attrs, thread_attrs,\n\
    402                inherit, flags, env_mapping, curdir,\n\
    403                startup_info) -> (proc_handle, thread_handle,\n\
    404                                  pid, tid)\n\
    405 \n\
    406 Create a new process and its primary thread. The return\n\
    407 value is a tuple of the process handle, thread handle,\n\
    408 process ID, and thread ID.\n\
    409 \n\
    410 proc_attrs and thread_attrs are ignored internally and can be None.");
    411 
    412 static PyObject *
    413 sp_CreateProcess(PyObject* self, PyObject* args)
    414 {
    415     BOOL result;
    416     PROCESS_INFORMATION pi;
    417     STARTUPINFO si;
    418     PyObject* environment;
    419 
    420     char* application_name;
    421     char* command_line;
    422     PyObject* process_attributes; /* ignored */
    423     PyObject* thread_attributes; /* ignored */
    424     int inherit_handles;
    425     int creation_flags;
    426     PyObject* env_mapping;
    427     char* current_directory;
    428     PyObject* startup_info;
    429 
    430     if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess",
    431                            &application_name,
    432                            &command_line,
    433                            &process_attributes,
    434                            &thread_attributes,
    435                            &inherit_handles,
    436                            &creation_flags,
    437                            &env_mapping,
    438                            &current_directory,
    439                            &startup_info))
    440         return NULL;
    441 
    442     ZeroMemory(&si, sizeof(si));
    443     si.cb = sizeof(si);
    444 
    445     /* note: we only support a small subset of all SI attributes */
    446     si.dwFlags = getint(startup_info, "dwFlags");
    447     si.wShowWindow = getint(startup_info, "wShowWindow");
    448     si.hStdInput = gethandle(startup_info, "hStdInput");
    449     si.hStdOutput = gethandle(startup_info, "hStdOutput");
    450     si.hStdError = gethandle(startup_info, "hStdError");
    451 
    452     if (PyErr_Occurred())
    453         return NULL;
    454 
    455     if (env_mapping == Py_None)
    456         environment = NULL;
    457     else {
    458         environment = getenvironment(env_mapping);
    459         if (! environment)
    460             return NULL;
    461     }
    462 
    463     Py_BEGIN_ALLOW_THREADS
    464     result = CreateProcess(application_name,
    465                            command_line,
    466                            NULL,
    467                            NULL,
    468                            inherit_handles,
    469                            creation_flags,
    470                            environment ? PyString_AS_STRING(environment) : NULL,
    471                            current_directory,
    472                            &si,
    473                            &pi);
    474     Py_END_ALLOW_THREADS
    475 
    476     Py_XDECREF(environment);
    477 
    478     if (! result)
    479         return PyErr_SetFromWindowsErr(GetLastError());
    480 
    481     return Py_BuildValue("NNii",
    482                          sp_handle_new(pi.hProcess),
    483                          sp_handle_new(pi.hThread),
    484                          pi.dwProcessId,
    485                          pi.dwThreadId);
    486 }
    487 
    488 PyDoc_STRVAR(TerminateProcess_doc,
    489 "TerminateProcess(handle, exit_code) -> None\n\
    490 \n\
    491 Terminate the specified process and all of its threads.");
    492 
    493 static PyObject *
    494 sp_TerminateProcess(PyObject* self, PyObject* args)
    495 {
    496     BOOL result;
    497 
    498     HANDLE process;
    499     int exit_code;
    500     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:TerminateProcess",
    501                            &process, &exit_code))
    502         return NULL;
    503 
    504     result = TerminateProcess(process, exit_code);
    505 
    506     if (! result)
    507         return PyErr_SetFromWindowsErr(GetLastError());
    508 
    509     Py_INCREF(Py_None);
    510     return Py_None;
    511 }
    512 
    513 PyDoc_STRVAR(GetExitCodeProcess_doc,
    514 "GetExitCodeProcess(handle) -> Exit code\n\
    515 \n\
    516 Return the termination status of the specified process.");
    517 
    518 static PyObject *
    519 sp_GetExitCodeProcess(PyObject* self, PyObject* args)
    520 {
    521     DWORD exit_code;
    522     BOOL result;
    523 
    524     HANDLE process;
    525     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetExitCodeProcess", &process))
    526         return NULL;
    527 
    528     result = GetExitCodeProcess(process, &exit_code);
    529 
    530     if (! result)
    531         return PyErr_SetFromWindowsErr(GetLastError());
    532 
    533     return PyInt_FromLong(exit_code);
    534 }
    535 
    536 PyDoc_STRVAR(WaitForSingleObject_doc,
    537 "WaitForSingleObject(handle, timeout) -> result\n\
    538 \n\
    539 Wait until the specified object is in the signaled state or\n\
    540 the time-out interval elapses. The timeout value is specified\n\
    541 in milliseconds.");
    542 
    543 static PyObject *
    544 sp_WaitForSingleObject(PyObject* self, PyObject* args)
    545 {
    546     DWORD result;
    547 
    548     HANDLE handle;
    549     int milliseconds;
    550     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:WaitForSingleObject",
    551                                  &handle,
    552                                  &milliseconds))
    553         return NULL;
    554 
    555     Py_BEGIN_ALLOW_THREADS
    556     result = WaitForSingleObject(handle, (DWORD) milliseconds);
    557     Py_END_ALLOW_THREADS
    558 
    559     if (result == WAIT_FAILED)
    560         return PyErr_SetFromWindowsErr(GetLastError());
    561 
    562     return PyInt_FromLong((int) result);
    563 }
    564 
    565 PyDoc_STRVAR(GetVersion_doc,
    566 "GetVersion() -> version\n\
    567 \n\
    568 Return the version number of the current operating system.");
    569 
    570 static PyObject *
    571 sp_GetVersion(PyObject* self, PyObject* args)
    572 {
    573     if (! PyArg_ParseTuple(args, ":GetVersion"))
    574         return NULL;
    575 
    576     return PyInt_FromLong((int) GetVersion());
    577 }
    578 
    579 PyDoc_STRVAR(GetModuleFileName_doc,
    580 "GetModuleFileName(module) -> path\n\
    581 \n\
    582 Return the fully-qualified path for the file that contains\n\
    583 the specified module. The module must have been loaded by the\n\
    584 current process.\n\
    585 \n\
    586 The module parameter should be a handle to the loaded module\n\
    587 whose path is being requested. If this parameter is 0, \n\
    588 GetModuleFileName retrieves the path of the executable file\n\
    589 of the current process.");
    590 
    591 static PyObject *
    592 sp_GetModuleFileName(PyObject* self, PyObject* args)
    593 {
    594     BOOL result;
    595     HMODULE module;
    596     TCHAR filename[MAX_PATH];
    597 
    598     if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetModuleFileName",
    599                            &module))
    600         return NULL;
    601 
    602     result = GetModuleFileName(module, filename, MAX_PATH);
    603     filename[MAX_PATH-1] = '\0';
    604 
    605     if (! result)
    606         return PyErr_SetFromWindowsErr(GetLastError());
    607 
    608     return PyString_FromString(filename);
    609 }
    610 
    611 static PyMethodDef sp_functions[] = {
    612     {"GetStdHandle", sp_GetStdHandle, METH_VARARGS, GetStdHandle_doc},
    613     {"GetCurrentProcess", sp_GetCurrentProcess,         METH_VARARGS,
    614                                               GetCurrentProcess_doc},
    615     {"DuplicateHandle",         sp_DuplicateHandle,     METH_VARARGS,
    616                                             DuplicateHandle_doc},
    617     {"CreatePipe", sp_CreatePipe, METH_VARARGS, CreatePipe_doc},
    618     {"CreateProcess", sp_CreateProcess, METH_VARARGS, CreateProcess_doc},
    619     {"TerminateProcess", sp_TerminateProcess, METH_VARARGS,
    620                                              TerminateProcess_doc},
    621     {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS,
    622                                                GetExitCodeProcess_doc},
    623     {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS,
    624                                                     WaitForSingleObject_doc},
    625     {"GetVersion", sp_GetVersion, METH_VARARGS, GetVersion_doc},
    626     {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS,
    627                                               GetModuleFileName_doc},
    628     {NULL, NULL}
    629 };
    630 
    631 /* -------------------------------------------------------------------- */
    632 
    633 static void
    634 defint(PyObject* d, const char* name, int value)
    635 {
    636     PyObject* v = PyInt_FromLong((long) value);
    637     if (v) {
    638         PyDict_SetItemString(d, (char*) name, v);
    639         Py_DECREF(v);
    640     }
    641 }
    642 
    643 #if PY_VERSION_HEX >= 0x02030000
    644 PyMODINIT_FUNC
    645 #else
    646 DL_EXPORT(void)
    647 #endif
    648 init_subprocess()
    649 {
    650     PyObject *d;
    651     PyObject *m;
    652 
    653     /* patch up object descriptors */
    654     sp_handle_type.ob_type = &PyType_Type;
    655     sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int;
    656 
    657     m = Py_InitModule("_subprocess", sp_functions);
    658     if (m == NULL)
    659         return;
    660     d = PyModule_GetDict(m);
    661 
    662     /* constants */
    663     defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE);
    664     defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE);
    665     defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE);
    666     defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS);
    667     defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES);
    668     defint(d, "STARTF_USESHOWWINDOW", STARTF_USESHOWWINDOW);
    669     defint(d, "SW_HIDE", SW_HIDE);
    670     defint(d, "INFINITE", INFINITE);
    671     defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0);
    672     defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE);
    673     defint(d, "CREATE_NEW_PROCESS_GROUP", CREATE_NEW_PROCESS_GROUP);
    674     defint(d, "STILL_ACTIVE", STILL_ACTIVE);
    675 }
    676