Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r1412:8a16eff7850c Date: 2013-11-12 13:48 +0100 http://bitbucket.org/cffi/cffi/changeset/8a16eff7850c/
Log: Add ffi.getwinerror(). diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -5227,6 +5227,9 @@ {"set_errno", b_set_errno, METH_VARARGS}, {"newp_handle", b_newp_handle, METH_VARARGS}, {"from_handle", b_from_handle, METH_O}, +#ifdef MS_WIN32 + {"getwinerror", b_getwinerror, METH_VARARGS}, +#endif {"_get_types", b__get_types, METH_NOARGS}, {"_testfunc", b__testfunc, METH_VARARGS}, {NULL, NULL} /* Sentinel */ diff --git a/c/misc_win32.h b/c/misc_win32.h --- a/c/misc_win32.h +++ b/c/misc_win32.h @@ -80,6 +80,54 @@ /* else: cannot report the error */ } +static PyObject *b_getwinerror(PyObject *self, PyObject *args) +{ + int err = -1; + int len; + char *s; + char *s_buf = NULL; /* Free via LocalFree */ + char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */ + PyObject *v; + + if (!PyArg_ParseTuple(args, "|i", &err)) + return NULL; + + if (err == -1) { + struct cffi_errno_s *p; + p = _geterrno_object(); + if (p == NULL) + return PyErr_NoMemory(); + err = p->saved_lasterror; + } + + len = FormatMessage( + /* Error API error */ + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, /* no message source */ + err, + MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &s_buf, + 0, /* size not used */ + NULL); /* no args */ + if (len==0) { + /* Only seen this in out of mem situations */ + sprintf(s_small_buf, "Windows Error 0x%X", err); + s = s_small_buf; + s_buf = NULL; + } else { + s = s_buf; + /* remove trailing cr/lf and dots */ + while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) + s[--len] = '\0'; + } + v = Py_BuildValue("(is)", err, s); + LocalFree(s_buf); + return v; +} + /************************************************************/ /* Emulate dlopen()&co. from the Windows API */ diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -2698,6 +2698,16 @@ # res = GetLastError() assert res == 42 + # + SetLastError(2) + code, message = getwinerror() + assert code == 2 + assert message == "The system cannot find the file specified" + # + code, message = getwinerror(1155) + assert code == 1155 + assert message == ("No application is associated with the " + "specified file for this operation") def test_nonstandard_integer_types(): for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -347,6 +347,9 @@ errno = property(_get_errno, _set_errno, None, "the value of 'errno' from/to the C calls") + def getwinerror(self, code=-1): + return self._backend.getwinerror(code) + def _pointer_to(self, ctype): from . import model with self._lock: diff --git a/doc/source/index.rst b/doc/source/index.rst --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1122,9 +1122,18 @@ ``ffi.errno``: the value of ``errno`` received from the most recent C call in this thread, and passed to the following C call, is available via -reads and writes of the property ``ffi.errno``. On Windows we also save -and restore the ``GetLastError()`` value, but to access it you need to -declare and call the ``GetLastError()`` function as usual. +reads and writes of the property ``ffi.errno``. + +``ffi.getwinerror(code=-1)``: on Windows, in addition to ``errno`` we +also save and restore the ``GetLastError()`` value across function +calls. This function returns this error code as a tuple ``(code, +message)``, adding a readable message like Python does when raising +WindowsError. If the argument ``code`` is given, format that code into +a message instead of using ``GetLastError()``. *New in version 0.8.* +(Note that it is also possible to declare and call the ``GetLastError()`` +function as usual.) + +.. "versionadded:: 0.8" --- inlined in the previous paragraph ``ffi.string(cdata, [maxlen])``: return a Python string (or unicode string) from the 'cdata'. *New in version 0.3.* diff --git a/testing/test_ffi_backend.py b/testing/test_ffi_backend.py --- a/testing/test_ffi_backend.py +++ b/testing/test_ffi_backend.py @@ -194,3 +194,20 @@ assert p.a[0] == 200 assert p.a[1] == 300 assert p.a[2] == 400 + + @pytest.mark.skipif("sys.platform != 'win32'") + def test_getwinerror(self): + ffi = FFI() + code, message = ffi.getwinerror(1155) + assert code == 1155 + assert message == ("No application is associated with the " + "specified file for this operation") + ffi.cdef("void SetLastError(int);") + lib = ffi.dlopen("Kernel32.dll") + lib.SetLastError(2) + code, message = ffi.getwinerror() + assert code == 2 + assert message == "The system cannot find the file specified" + code, message = ffi.getwinerror(-1) + assert code == 2 + assert message == "The system cannot find the file specified" _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit