Windows / ctypes issue with custom build

Eric Frederich eric.frederich at gmail.com
Mon Mar 27 21:59:53 EDT 2017


I built my own Python 2.7.13 for Windows because I'm using bindings to a
3rd party application which were built with Visual Studio 2012.
I started to code up some stuff using the "click" module and found an error
when using click.echo with any kind of unicode input.

    Python 2.7.13 (default, Mar 27 2017, 11:11:01) [MSC v.1700 64 bit
(AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import click
    >>> click.echo('Hello World')
    Hello World
    >>> click.echo(u'Hello World')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Users\eric\my_env\lib\site-packages\click\utils.py", line
259, in echo
        file.write(message)
      File "C:\Users\eric\my_env\lib\site-packages\click\_winconsole.py",
line 180, in write
        return self._text_stream.write(x)
      File "C:\Users\eric\my_env\lib\site-packages\click\_compat.py", line
63, in write
        return io.TextIOWrapper.write(self, x)
      File "C:\Users\eric\my_env\lib\site-packages\click\_winconsole.py",
line 164, in write
        raise OSError(self._get_error_message(GetLastError()))
    OSError: Windows error 6

If I download and install the Python 2.7.13 64-bit installer I don't get
this issue.  It echo's just fine.
I have looked into this a lot and am at a loss right now.
I'm not too familiar with Windows, Visual Studio, or ctypes.

I spent some time looking at the code path to produce the smallest file
(without click) which demonstrates this problem (see below)
It produces the same "Windows error 6"... again, this works fine with the
python installed from the 2.7.13 64 bit MSI installer.
Can someone share the process used to create the Windows installers?  Is
this a manual process or is it automated?
Maybe I'm missing some important switch to msbuild or something.  Any help
or ideas are appreciated.
I cannot use a downloaded copy of Python... it needs to be built with a
specific version, update, patch, etc of Visual Studio.

All I did was
1) clone cpython from github and checkout 2.7.13
2) edit some xp stuff out of tk stuff to get it to compile on Windows
Server 2003
    In `externals\tk-8.5.15.0\win\Makefile.in` remove
`ttkWinXPTheme.$(OBJEXT)` line
    In `externals\tk-8.5.15.0\win\makefile.vc` remove
`$(TMP_DIR)\ttkWinXPTheme.obj` line
    In `externals\tk-8.5.15.0\win\ttkWinMonitor.c` remove 2
`TtkXPTheme_Init` lines
    In `PCbuild\tcltk.props` change VC9 to VC11 at the bottom
3) PCbuild\build.bat -e -p x64 "/p:PlatformToolset=v110"

After that I created an "install" by copying .exe, .pyd, .dll files, ran
get-pip.py, then python -m pip install virtualenv, then virtualenv my_env,
then activated it, then did a pip install click.
But with this stripped down version you don't need pip, virtualenv or
click... just ctypes.
You could probably even build it without the -e switch to build.bat.

    from ctypes import byref, POINTER, py_object, pythonapi, Structure,
windll
    from ctypes import c_char, c_char_p, c_int, c_ssize_t, c_ulong, c_void_p
    c_ssize_p = POINTER(c_ssize_t)

    kernel32 = windll.kernel32
    STDOUT_HANDLE = kernel32.GetStdHandle(-11)

    PyBUF_SIMPLE = 0
    MAX_BYTES_WRITTEN = 32767

    class Py_buffer(Structure):
        _fields_ = [
            ('buf', c_void_p),
            ('obj', py_object),
            ('len', c_ssize_t),
            ('itemsize', c_ssize_t),
            ('readonly', c_int),
            ('ndim', c_int),
            ('format', c_char_p),
            ('shape', c_ssize_p),
            ('strides', c_ssize_p),
            ('suboffsets', c_ssize_p),
            ('internal', c_void_p)
        ]
        _fields_.insert(-1, ('smalltable', c_ssize_t * 2))

    bites = u"Hello World".encode('utf-16-le')
    bytes_to_be_written = len(bites)
    buf = Py_buffer()
    pythonapi.PyObject_GetBuffer(py_object(bites), byref(buf), PyBUF_SIMPLE)
    buffer_type = c_char * buf.len
    buf = buffer_type.from_address(buf.buf)
    code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN)
// 2
    code_units_written = c_ulong()

    kernel32.WriteConsoleW(STDOUT_HANDLE, buf, code_units_to_be_written,
byref(code_units_written), None)
    bytes_written = 2 * code_units_written.value

    if bytes_written == 0 and bytes_to_be_written > 0:
        raise OSError('Windows error %s' % kernel32.GetLastError())


More information about the Python-list mailing list