• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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