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