Branch data Line data Source code
1 : : #ifndef Py_BUILD_CORE_BUILTIN
2 : : # define Py_BUILD_CORE_MODULE 1
3 : : #endif
4 : : #define NEEDS_PY_IDENTIFIER
5 : :
6 : : #include "Python.h"
7 : : // windows.h must be included before pycore internal headers
8 : : #ifdef MS_WIN32
9 : : # include <windows.h>
10 : : #endif
11 : :
12 : : #include "pycore_call.h" // _PyObject_CallNoArgs()
13 : :
14 : : #include <stdbool.h>
15 : :
16 : : #ifdef MS_WIN32
17 : : # include <malloc.h>
18 : : #endif
19 : :
20 : : #include <ffi.h>
21 : : #include "ctypes.h"
22 : :
23 : : #ifdef HAVE_ALLOCA_H
24 : : /* AIX needs alloca.h for alloca() */
25 : : #include <alloca.h>
26 : : #endif
27 : :
28 : : /**************************************************************/
29 : :
30 : : static void
31 : 151 : CThunkObject_dealloc(PyObject *myself)
32 : : {
33 : 151 : CThunkObject *self = (CThunkObject *)myself;
34 : 151 : PyObject_GC_UnTrack(self);
35 : 151 : Py_XDECREF(self->converters);
36 : 151 : Py_XDECREF(self->callable);
37 : 151 : Py_XDECREF(self->restype);
38 [ + - ]: 151 : if (self->pcl_write)
39 : 151 : Py_ffi_closure_free(self->pcl_write);
40 : 151 : PyObject_GC_Del(self);
41 : 151 : }
42 : :
43 : : static int
44 : 96 : CThunkObject_traverse(PyObject *myself, visitproc visit, void *arg)
45 : : {
46 : 96 : CThunkObject *self = (CThunkObject *)myself;
47 [ + - - + ]: 96 : Py_VISIT(self->converters);
48 [ + - - + ]: 96 : Py_VISIT(self->callable);
49 [ + - - + ]: 96 : Py_VISIT(self->restype);
50 : 96 : return 0;
51 : : }
52 : :
53 : : static int
54 : 1 : CThunkObject_clear(PyObject *myself)
55 : : {
56 : 1 : CThunkObject *self = (CThunkObject *)myself;
57 [ + - ]: 1 : Py_CLEAR(self->converters);
58 [ + - ]: 1 : Py_CLEAR(self->callable);
59 [ + - ]: 1 : Py_CLEAR(self->restype);
60 : 1 : return 0;
61 : : }
62 : :
63 : : PyTypeObject PyCThunk_Type = {
64 : : PyVarObject_HEAD_INIT(NULL, 0)
65 : : "_ctypes.CThunkObject",
66 : : sizeof(CThunkObject), /* tp_basicsize */
67 : : sizeof(ffi_type), /* tp_itemsize */
68 : : CThunkObject_dealloc, /* tp_dealloc */
69 : : 0, /* tp_vectorcall_offset */
70 : : 0, /* tp_getattr */
71 : : 0, /* tp_setattr */
72 : : 0, /* tp_as_async */
73 : : 0, /* tp_repr */
74 : : 0, /* tp_as_number */
75 : : 0, /* tp_as_sequence */
76 : : 0, /* tp_as_mapping */
77 : : 0, /* tp_hash */
78 : : 0, /* tp_call */
79 : : 0, /* tp_str */
80 : : 0, /* tp_getattro */
81 : : 0, /* tp_setattro */
82 : : 0, /* tp_as_buffer */
83 : : Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
84 : : PyDoc_STR("CThunkObject"), /* tp_doc */
85 : : CThunkObject_traverse, /* tp_traverse */
86 : : CThunkObject_clear, /* tp_clear */
87 : : 0, /* tp_richcompare */
88 : : 0, /* tp_weaklistoffset */
89 : : 0, /* tp_iter */
90 : : 0, /* tp_iternext */
91 : : 0, /* tp_methods */
92 : : 0, /* tp_members */
93 : : };
94 : :
95 : : /**************************************************************/
96 : :
97 : : static void
98 : 0 : PrintError(const char *msg, ...)
99 : : {
100 : : char buf[512];
101 : 0 : PyObject *f = PySys_GetObject("stderr");
102 : : va_list marker;
103 : :
104 : 0 : va_start(marker, msg);
105 : 0 : PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
106 : 0 : va_end(marker);
107 [ # # # # ]: 0 : if (f != NULL && f != Py_None)
108 : 0 : PyFile_WriteString(buf, f);
109 : 0 : PyErr_Print();
110 : 0 : }
111 : :
112 : :
113 : : #ifdef MS_WIN32
114 : : /*
115 : : * We must call AddRef() on non-NULL COM pointers we receive as arguments
116 : : * to callback functions - these functions are COM method implementations.
117 : : * The Python instances we create have a __del__ method which calls Release().
118 : : *
119 : : * The presence of a class attribute named '_needs_com_addref_' triggers this
120 : : * behaviour. It would also be possible to call the AddRef() Python method,
121 : : * after checking for PyObject_IsTrue(), but this would probably be somewhat
122 : : * slower.
123 : : */
124 : : static void
125 : : TryAddRef(StgDictObject *dict, CDataObject *obj)
126 : : {
127 : : IUnknown *punk;
128 : : _Py_IDENTIFIER(_needs_com_addref_);
129 : :
130 : : int r = _PyDict_ContainsId((PyObject *)dict, &PyId__needs_com_addref_);
131 : : if (r <= 0) {
132 : : if (r < 0) {
133 : : PrintError("getting _needs_com_addref_");
134 : : }
135 : : return;
136 : : }
137 : :
138 : : punk = *(IUnknown **)obj->b_ptr;
139 : : if (punk)
140 : : punk->lpVtbl->AddRef(punk);
141 : : return;
142 : : }
143 : : #endif
144 : :
145 : : /******************************************************************************
146 : : *
147 : : * Call the python object with all arguments
148 : : *
149 : : */
150 : 462 : static void _CallPythonObject(void *mem,
151 : : ffi_type *restype,
152 : : SETFUNC setfunc,
153 : : PyObject *callable,
154 : : PyObject *converters,
155 : : int flags,
156 : : void **pArgs)
157 : : {
158 : 462 : PyObject *result = NULL;
159 : 462 : Py_ssize_t i = 0, j = 0, nargs = 0;
160 : 462 : PyObject *error_object = NULL;
161 : : int *space;
162 : 462 : PyGILState_STATE state = PyGILState_Ensure();
163 : :
164 : : assert(PyTuple_Check(converters));
165 : 462 : nargs = PyTuple_GET_SIZE(converters);
166 : : assert(nargs <= CTYPES_MAX_ARGCOUNT);
167 : 462 : PyObject **args = alloca(nargs * sizeof(PyObject *));
168 [ - + ]: 462 : PyObject **cnvs = PySequence_Fast_ITEMS(converters);
169 [ + + ]: 2062 : for (i = 0; i < nargs; i++) {
170 : 1600 : PyObject *cnv = cnvs[i]; // borrowed ref
171 : : StgDictObject *dict;
172 : 1600 : dict = PyType_stgdict(cnv);
173 : :
174 [ + - + + : 1600 : if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
+ + ]
175 : 1456 : PyObject *v = dict->getfunc(*pArgs, dict->size);
176 [ - + ]: 1456 : if (!v) {
177 : 0 : PrintError("create argument %zd:\n", i);
178 : 0 : goto Done;
179 : : }
180 : 1456 : args[i] = v;
181 : : /* XXX XXX XX
182 : : We have the problem that c_byte or c_short have dict->size of
183 : : 1 resp. 4, but these parameters are pushed as sizeof(int) bytes.
184 : : BTW, the same problem occurs when they are pushed as parameters
185 : : */
186 [ + - ]: 144 : } else if (dict) {
187 : : /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
188 : 144 : CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
189 [ - + ]: 144 : if (!obj) {
190 : 0 : PrintError("create argument %zd:\n", i);
191 : 0 : goto Done;
192 : : }
193 [ - + ]: 144 : if (!CDataObject_Check(obj)) {
194 : 0 : Py_DECREF(obj);
195 : 0 : PrintError("unexpected result of create argument %zd:\n", i);
196 : 0 : goto Done;
197 : : }
198 : 144 : memcpy(obj->b_ptr, *pArgs, dict->size);
199 : 144 : args[i] = (PyObject *)obj;
200 : : #ifdef MS_WIN32
201 : : TryAddRef(dict, obj);
202 : : #endif
203 : : } else {
204 : 0 : PyErr_SetString(PyExc_TypeError,
205 : : "cannot build parameter");
206 : 0 : PrintError("Parsing argument %zd\n", i);
207 : 0 : goto Done;
208 : : }
209 : : /* XXX error handling! */
210 : 1600 : pArgs++;
211 : : }
212 : :
213 [ - + ]: 462 : if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
214 : 0 : error_object = _ctypes_get_errobj(&space);
215 [ # # ]: 0 : if (error_object == NULL)
216 : 0 : goto Done;
217 [ # # ]: 0 : if (flags & FUNCFLAG_USE_ERRNO) {
218 : 0 : int temp = space[0];
219 : 0 : space[0] = errno;
220 : 0 : errno = temp;
221 : : }
222 : : #ifdef MS_WIN32
223 : : if (flags & FUNCFLAG_USE_LASTERROR) {
224 : : int temp = space[1];
225 : : space[1] = GetLastError();
226 : : SetLastError(temp);
227 : : }
228 : : #endif
229 : : }
230 : :
231 : 462 : result = PyObject_Vectorcall(callable, args, nargs, NULL);
232 [ + + ]: 462 : if (result == NULL) {
233 : 4 : _PyErr_WriteUnraisableMsg("on calling ctypes callback function",
234 : : callable);
235 : : }
236 : :
237 : : #ifdef MS_WIN32
238 : : if (flags & FUNCFLAG_USE_LASTERROR) {
239 : : int temp = space[1];
240 : : space[1] = GetLastError();
241 : : SetLastError(temp);
242 : : }
243 : : #endif
244 [ - + ]: 462 : if (flags & FUNCFLAG_USE_ERRNO) {
245 : 0 : int temp = space[0];
246 : 0 : space[0] = errno;
247 : 0 : errno = temp;
248 : : }
249 : 462 : Py_XDECREF(error_object);
250 : :
251 [ + + + + ]: 462 : if (restype != &ffi_type_void && result) {
252 : : assert(setfunc);
253 : :
254 : : #ifdef WORDS_BIGENDIAN
255 : : /* See the corresponding code in _ctypes_callproc():
256 : : in callproc.c, around line 1219. */
257 : : if (restype->type != FFI_TYPE_FLOAT && restype->size < sizeof(ffi_arg)) {
258 : : mem = (char *)mem + sizeof(ffi_arg) - restype->size;
259 : : }
260 : : #endif
261 : :
262 : : /* keep is an object we have to keep alive so that the result
263 : : stays valid. If there is no such object, the setfunc will
264 : : have returned Py_None.
265 : :
266 : : If there is such an object, we have no choice than to keep
267 : : it alive forever - but a refcount and/or memory leak will
268 : : be the result. EXCEPT when restype is py_object - Python
269 : : itself knows how to manage the refcount of these objects.
270 : : */
271 : 451 : PyObject *keep = setfunc(mem, result, 0);
272 : :
273 [ + + ]: 451 : if (keep == NULL) {
274 : : /* Could not convert callback result. */
275 : 1 : _PyErr_WriteUnraisableMsg("on converting result "
276 : : "of ctypes callback function",
277 : : callable);
278 : : }
279 [ + + ]: 450 : else if (keep == Py_None) {
280 : : /* Nothing to keep */
281 : 438 : Py_DECREF(keep);
282 : : }
283 [ - + ]: 12 : else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) {
284 [ # # ]: 0 : if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning,
285 : : "memory leak in callback function.",
286 : : 1))
287 : : {
288 : 0 : _PyErr_WriteUnraisableMsg("on converting result "
289 : : "of ctypes callback function",
290 : : callable);
291 : : }
292 : : }
293 : : }
294 : :
295 : 462 : Py_XDECREF(result);
296 : :
297 : 462 : Done:
298 [ + + ]: 2062 : for (j = 0; j < i; j++) {
299 : 1600 : Py_DECREF(args[j]);
300 : : }
301 : 462 : PyGILState_Release(state);
302 : 462 : }
303 : :
304 : 462 : static void closure_fcn(ffi_cif *cif,
305 : : void *resp,
306 : : void **args,
307 : : void *userdata)
308 : : {
309 : 462 : CThunkObject *p = (CThunkObject *)userdata;
310 : :
311 : 462 : _CallPythonObject(resp,
312 : : p->ffi_restype,
313 : : p->setfunc,
314 : : p->callable,
315 : : p->converters,
316 : : p->flags,
317 : : args);
318 : 462 : }
319 : :
320 : 151 : static CThunkObject* CThunkObject_new(Py_ssize_t nargs)
321 : : {
322 : : CThunkObject *p;
323 : : Py_ssize_t i;
324 : :
325 : 151 : p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nargs);
326 [ - + ]: 151 : if (p == NULL) {
327 : 0 : return NULL;
328 : : }
329 : :
330 : 151 : p->pcl_write = NULL;
331 : 151 : p->pcl_exec = NULL;
332 : 151 : memset(&p->cif, 0, sizeof(p->cif));
333 : 151 : p->flags = 0;
334 : 151 : p->converters = NULL;
335 : 151 : p->callable = NULL;
336 : 151 : p->restype = NULL;
337 : 151 : p->setfunc = NULL;
338 : 151 : p->ffi_restype = NULL;
339 : :
340 [ + + ]: 1495 : for (i = 0; i < nargs + 1; ++i)
341 : 1344 : p->atypes[i] = NULL;
342 : 151 : PyObject_GC_Track((PyObject *)p);
343 : 151 : return p;
344 : : }
345 : :
346 : 151 : CThunkObject *_ctypes_alloc_callback(PyObject *callable,
347 : : PyObject *converters,
348 : : PyObject *restype,
349 : : int flags)
350 : : {
351 : : int result;
352 : : CThunkObject *p;
353 : : Py_ssize_t nargs, i;
354 : : ffi_abi cc;
355 : :
356 : : assert(PyTuple_Check(converters));
357 : 151 : nargs = PyTuple_GET_SIZE(converters);
358 : 151 : p = CThunkObject_new(nargs);
359 [ - + ]: 151 : if (p == NULL)
360 : 0 : return NULL;
361 : :
362 : : assert(CThunk_CheckExact((PyObject *)p));
363 : :
364 : 151 : p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
365 [ - + ]: 151 : if (p->pcl_write == NULL) {
366 : : PyErr_NoMemory();
367 : 0 : goto error;
368 : : }
369 : :
370 : 151 : p->flags = flags;
371 [ - + ]: 151 : PyObject **cnvs = PySequence_Fast_ITEMS(converters);
372 [ + + ]: 1344 : for (i = 0; i < nargs; ++i) {
373 : 1193 : PyObject *cnv = cnvs[i]; // borrowed ref
374 : 1193 : p->atypes[i] = _ctypes_get_ffi_type(cnv);
375 : : }
376 : 151 : p->atypes[i] = NULL;
377 : :
378 : 151 : Py_INCREF(restype);
379 : 151 : p->restype = restype;
380 [ + + ]: 151 : if (restype == Py_None) {
381 : 38 : p->setfunc = NULL;
382 : 38 : p->ffi_restype = &ffi_type_void;
383 : : } else {
384 : 113 : StgDictObject *dict = PyType_stgdict(restype);
385 [ + + + + ]: 113 : if (dict == NULL || dict->setfunc == NULL) {
386 : 2 : PyErr_SetString(PyExc_TypeError,
387 : : "invalid result type for callback function");
388 : 2 : goto error;
389 : : }
390 : 111 : p->setfunc = dict->setfunc;
391 : 111 : p->ffi_restype = &dict->ffi_type_pointer;
392 : : }
393 : :
394 : 149 : cc = FFI_DEFAULT_ABI;
395 : : #if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64) && !defined(_M_ARM)
396 : : if ((flags & FUNCFLAG_CDECL) == 0)
397 : : cc = FFI_STDCALL;
398 : : #endif
399 : 149 : result = ffi_prep_cif(&p->cif, cc,
400 : : Py_SAFE_DOWNCAST(nargs, Py_ssize_t, int),
401 : : p->ffi_restype,
402 : : &p->atypes[0]);
403 [ - + ]: 149 : if (result != FFI_OK) {
404 : 0 : PyErr_Format(PyExc_RuntimeError,
405 : : "ffi_prep_cif failed with %d", result);
406 : 0 : goto error;
407 : : }
408 : : #if HAVE_FFI_PREP_CLOSURE_LOC
409 : : # ifdef USING_APPLE_OS_LIBFFI
410 : : # define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
411 : : # else
412 : : # define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
413 : : # endif
414 : : if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
415 : 149 : result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
416 : : p,
417 : : p->pcl_exec);
418 : : } else
419 : : #endif
420 : : {
421 : : #if defined(USING_APPLE_OS_LIBFFI) && defined(__arm64__)
422 : : PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
423 : : goto error;
424 : : #else
425 : : #if defined(__clang__) || defined(MACOSX)
426 : : #pragma clang diagnostic push
427 : : #pragma clang diagnostic ignored "-Wdeprecated-declarations"
428 : : #endif
429 : : #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
430 : : #pragma GCC diagnostic push
431 : : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
432 : : #endif
433 : : result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
434 : :
435 : : #if defined(__clang__) || defined(MACOSX)
436 : : #pragma clang diagnostic pop
437 : : #endif
438 : : #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
439 : : #pragma GCC diagnostic pop
440 : : #endif
441 : :
442 : : #endif
443 : : }
444 [ - + ]: 149 : if (result != FFI_OK) {
445 : 0 : PyErr_Format(PyExc_RuntimeError,
446 : : "ffi_prep_closure failed with %d", result);
447 : 0 : goto error;
448 : : }
449 : :
450 : 149 : Py_INCREF(converters);
451 : 149 : p->converters = converters;
452 : 149 : Py_INCREF(callable);
453 : 149 : p->callable = callable;
454 : 149 : return p;
455 : :
456 : 2 : error:
457 : 2 : Py_XDECREF(p);
458 : 2 : return NULL;
459 : : }
460 : :
461 : : #ifdef MS_WIN32
462 : :
463 : : static void LoadPython(void)
464 : : {
465 : : if (!Py_IsInitialized()) {
466 : : Py_Initialize();
467 : : }
468 : : }
469 : :
470 : : /******************************************************************/
471 : :
472 : : long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
473 : : {
474 : : PyObject *func, *result;
475 : : long retval;
476 : : static PyObject *context;
477 : :
478 : : if (context == NULL)
479 : : context = PyUnicode_InternFromString("_ctypes.DllGetClassObject");
480 : :
481 : : func = _PyImport_GetModuleAttrString("ctypes", "DllGetClassObject");
482 : : if (!func) {
483 : : PyErr_WriteUnraisable(context ? context : Py_None);
484 : : /* There has been a warning before about this already */
485 : : return E_FAIL;
486 : : }
487 : :
488 : : {
489 : : PyObject *py_rclsid = PyLong_FromVoidPtr((void *)rclsid);
490 : : PyObject *py_riid = PyLong_FromVoidPtr((void *)riid);
491 : : PyObject *py_ppv = PyLong_FromVoidPtr(ppv);
492 : : if (!py_rclsid || !py_riid || !py_ppv) {
493 : : Py_XDECREF(py_rclsid);
494 : : Py_XDECREF(py_riid);
495 : : Py_XDECREF(py_ppv);
496 : : Py_DECREF(func);
497 : : PyErr_WriteUnraisable(context ? context : Py_None);
498 : : return E_FAIL;
499 : : }
500 : : result = PyObject_CallFunctionObjArgs(func,
501 : : py_rclsid,
502 : : py_riid,
503 : : py_ppv,
504 : : NULL);
505 : : Py_DECREF(py_rclsid);
506 : : Py_DECREF(py_riid);
507 : : Py_DECREF(py_ppv);
508 : : }
509 : : Py_DECREF(func);
510 : : if (!result) {
511 : : PyErr_WriteUnraisable(context ? context : Py_None);
512 : : return E_FAIL;
513 : : }
514 : :
515 : : retval = PyLong_AsLong(result);
516 : : if (PyErr_Occurred()) {
517 : : PyErr_WriteUnraisable(context ? context : Py_None);
518 : : retval = E_FAIL;
519 : : }
520 : : Py_DECREF(result);
521 : : return retval;
522 : : }
523 : :
524 : : STDAPI DllGetClassObject(REFCLSID rclsid,
525 : : REFIID riid,
526 : : LPVOID *ppv)
527 : : {
528 : : long result;
529 : : PyGILState_STATE state;
530 : :
531 : : LoadPython();
532 : : state = PyGILState_Ensure();
533 : : result = Call_GetClassObject(rclsid, riid, ppv);
534 : : PyGILState_Release(state);
535 : : return result;
536 : : }
537 : :
538 : : long Call_CanUnloadNow(void)
539 : : {
540 : : PyObject *mod, *func, *result;
541 : : long retval;
542 : : static PyObject *context;
543 : :
544 : : if (context == NULL)
545 : : context = PyUnicode_InternFromString("_ctypes.DllCanUnloadNow");
546 : :
547 : : mod = PyImport_ImportModule("ctypes");
548 : : if (!mod) {
549 : : /* OutputDebugString("Could not import ctypes"); */
550 : : /* We assume that this error can only occur when shutting
551 : : down, so we silently ignore it */
552 : : PyErr_Clear();
553 : : return E_FAIL;
554 : : }
555 : : /* Other errors cannot be raised, but are printed to stderr */
556 : : func = PyObject_GetAttrString(mod, "DllCanUnloadNow");
557 : : Py_DECREF(mod);
558 : : if (!func) {
559 : : PyErr_WriteUnraisable(context ? context : Py_None);
560 : : return E_FAIL;
561 : : }
562 : :
563 : : result = _PyObject_CallNoArgs(func);
564 : : Py_DECREF(func);
565 : : if (!result) {
566 : : PyErr_WriteUnraisable(context ? context : Py_None);
567 : : return E_FAIL;
568 : : }
569 : :
570 : : retval = PyLong_AsLong(result);
571 : : if (PyErr_Occurred()) {
572 : : PyErr_WriteUnraisable(context ? context : Py_None);
573 : : retval = E_FAIL;
574 : : }
575 : : Py_DECREF(result);
576 : : return retval;
577 : : }
578 : :
579 : : /*
580 : : DllRegisterServer and DllUnregisterServer still missing
581 : : */
582 : :
583 : : STDAPI DllCanUnloadNow(void)
584 : : {
585 : : long result;
586 : : PyGILState_STATE state = PyGILState_Ensure();
587 : : result = Call_CanUnloadNow();
588 : : PyGILState_Release(state);
589 : : return result;
590 : : }
591 : :
592 : : #ifndef Py_NO_ENABLE_SHARED
593 : : BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvRes)
594 : : {
595 : : switch(fdwReason) {
596 : : case DLL_PROCESS_ATTACH:
597 : : DisableThreadLibraryCalls(hinstDLL);
598 : : break;
599 : : }
600 : : return TRUE;
601 : : }
602 : : #endif
603 : :
604 : : #endif
605 : :
606 : : /*
607 : : Local Variables:
608 : : compile-command: "cd .. && python setup.py -q build_ext"
609 : : End:
610 : : */
|