_cffi_errors.h 3.8 KB

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