1 #ifndef CFFI_MESSAGEBOX
2 # ifdef _MSC_VER
3 # define CFFI_MESSAGEBOX 1
4 # else
5 # define CFFI_MESSAGEBOX 0
6 # endif
7 #endif
8
9
10 #if CFFI_MESSAGEBOX
11 /* Windows only: logic to take the Python-CFFI embedding logic
12 initialization errors and display them in a background thread
13 with MessageBox. The idea is that if the whole program closes
14 as a result of this problem, then likely it is already a console
15 program and you can read the stderr output in the console too.
16 If it is not a console program, then it will likely show its own
17 dialog to complain, or generally not abruptly close, and for this
18 case the background thread should stay alive.
19 */
20 static void *volatile _cffi_bootstrap_text;
21
_cffi_start_error_capture(void)22 static PyObject *_cffi_start_error_capture(void)
23 {
24 PyObject *result = NULL;
25 PyObject *x, *m, *bi;
26
27 if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text,
28 (void *)1, NULL) != NULL)
29 return (PyObject *)1;
30
31 m = PyImport_AddModule("_cffi_error_capture");
32 if (m == NULL)
33 goto error;
34
35 result = PyModule_GetDict(m);
36 if (result == NULL)
37 goto error;
38
39 #if PY_MAJOR_VERSION >= 3
40 bi = PyImport_ImportModule("builtins");
41 #else
42 bi = PyImport_ImportModule("__builtin__");
43 #endif
44 if (bi == NULL)
45 goto error;
46 PyDict_SetItemString(result, "__builtins__", bi);
47 Py_DECREF(bi);
48
49 x = PyRun_String(
50 "import sys\n"
51 "class FileLike:\n"
52 " def write(self, x):\n"
53 " try:\n"
54 " of.write(x)\n"
55 " except: pass\n"
56 " self.buf += x\n"
57 "fl = FileLike()\n"
58 "fl.buf = ''\n"
59 "of = sys.stderr\n"
60 "sys.stderr = fl\n"
61 "def done():\n"
62 " sys.stderr = of\n"
63 " return fl.buf\n", /* make sure the returned value stays alive */
64 Py_file_input,
65 result, result);
66 Py_XDECREF(x);
67
68 error:
69 if (PyErr_Occurred())
70 {
71 PyErr_WriteUnraisable(Py_None);
72 PyErr_Clear();
73 }
74 return result;
75 }
76
77 #pragma comment(lib, "user32.lib")
78
_cffi_bootstrap_dialog(LPVOID ignored)79 static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored)
80 {
81 Sleep(666); /* may be interrupted if the whole process is closing */
82 #if PY_MAJOR_VERSION >= 3
83 MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text,
84 L"Python-CFFI error",
85 MB_OK | MB_ICONERROR);
86 #else
87 MessageBoxA(NULL, (char *)_cffi_bootstrap_text,
88 "Python-CFFI error",
89 MB_OK | MB_ICONERROR);
90 #endif
91 _cffi_bootstrap_text = NULL;
92 return 0;
93 }
94
_cffi_stop_error_capture(PyObject * ecap)95 static void _cffi_stop_error_capture(PyObject *ecap)
96 {
97 PyObject *s;
98 void *text;
99
100 if (ecap == (PyObject *)1)
101 return;
102
103 if (ecap == NULL)
104 goto error;
105
106 s = PyRun_String("done()", Py_eval_input, ecap, ecap);
107 if (s == NULL)
108 goto error;
109
110 /* Show a dialog box, but in a background thread, and
111 never show multiple dialog boxes at once. */
112 #if PY_MAJOR_VERSION >= 3
113 text = PyUnicode_AsWideCharString(s, NULL);
114 #else
115 text = PyString_AsString(s);
116 #endif
117
118 _cffi_bootstrap_text = text;
119
120 if (text != NULL)
121 {
122 HANDLE h;
123 h = CreateThread(NULL, 0, _cffi_bootstrap_dialog,
124 NULL, 0, NULL);
125 if (h != NULL)
126 CloseHandle(h);
127 }
128 /* decref the string, but it should stay alive as 'fl.buf'
129 in the small module above. It will really be freed only if
130 we later get another similar error. So it's a leak of at
131 most one copy of the small module. That's fine for this
132 situation which is usually a "fatal error" anyway. */
133 Py_DECREF(s);
134 PyErr_Clear();
135 return;
136
137 error:
138 _cffi_bootstrap_text = NULL;
139 PyErr_Clear();
140 }
141
142 #else
143
_cffi_start_error_capture(void)144 static PyObject *_cffi_start_error_capture(void) { return NULL; }
_cffi_stop_error_capture(PyObject * ecap)145 static void _cffi_stop_error_capture(PyObject *ecap) { }
146
147 #endif
148