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 " def flush(self):\n"
58 " pass\n"
59 "fl = FileLike()\n"
60 "fl.buf = ''\n"
61 "of = sys.stderr\n"
62 "sys.stderr = fl\n"
63 "def done():\n"
64 " sys.stderr = of\n"
65 " return fl.buf\n", /* make sure the returned value stays alive */
66 Py_file_input,
67 result, result);
68 Py_XDECREF(x);
69
70 error:
71 if (PyErr_Occurred())
72 {
73 PyErr_WriteUnraisable(Py_None);
74 PyErr_Clear();
75 }
76 return result;
77 }
78
79 #pragma comment(lib, "user32.lib")
80
_cffi_bootstrap_dialog(LPVOID ignored)81 static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored)
82 {
83 Sleep(666); /* may be interrupted if the whole process is closing */
84 #if PY_MAJOR_VERSION >= 3
85 MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text,
86 L"Python-CFFI error",
87 MB_OK | MB_ICONERROR);
88 #else
89 MessageBoxA(NULL, (char *)_cffi_bootstrap_text,
90 "Python-CFFI error",
91 MB_OK | MB_ICONERROR);
92 #endif
93 _cffi_bootstrap_text = NULL;
94 return 0;
95 }
96
_cffi_stop_error_capture(PyObject * ecap)97 static void _cffi_stop_error_capture(PyObject *ecap)
98 {
99 PyObject *s;
100 void *text;
101
102 if (ecap == (PyObject *)1)
103 return;
104
105 if (ecap == NULL)
106 goto error;
107
108 s = PyRun_String("done()", Py_eval_input, ecap, ecap);
109 if (s == NULL)
110 goto error;
111
112 /* Show a dialog box, but in a background thread, and
113 never show multiple dialog boxes at once. */
114 #if PY_MAJOR_VERSION >= 3
115 text = PyUnicode_AsWideCharString(s, NULL);
116 #else
117 text = PyString_AsString(s);
118 #endif
119
120 _cffi_bootstrap_text = text;
121
122 if (text != NULL)
123 {
124 HANDLE h;
125 h = CreateThread(NULL, 0, _cffi_bootstrap_dialog,
126 NULL, 0, NULL);
127 if (h != NULL)
128 CloseHandle(h);
129 }
130 /* decref the string, but it should stay alive as 'fl.buf'
131 in the small module above. It will really be freed only if
132 we later get another similar error. So it's a leak of at
133 most one copy of the small module. That's fine for this
134 situation which is usually a "fatal error" anyway. */
135 Py_DECREF(s);
136 PyErr_Clear();
137 return;
138
139 error:
140 _cffi_bootstrap_text = NULL;
141 PyErr_Clear();
142 }
143
144 #else
145
_cffi_start_error_capture(void)146 static PyObject *_cffi_start_error_capture(void) { return NULL; }
_cffi_stop_error_capture(PyObject * ecap)147 static void _cffi_stop_error_capture(PyObject *ecap) { }
148
149 #endif
150