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