在与@eryksun共度时光之后,我们深入挖掘并发现了潜在的问题。这两个示例fail1.py
和fail2.py
与我发布的原始较长示例具有相同的效果。
事实证明,这个问题与将4字节C ints复制到8字节堆栈位置有关,而不会先将内存置零,可能会在高4字节中留下垃圾。这是使用调试器确认的(再次支持@eryksun)。
这是一个奇怪的错误(heisenbug),您可以在其中添加一些printf语句,然后该错误不再存在。
在argp = stack;之前的行的顶部ffi_prep_args中,添加一个调用 memset(stack,0,ecif-> cif-> bytes);.这将使整个过程归零 叠加。
这是要修复的位置: https://github.com/python/cpython/blob/v2.7.13/Modules/_ctypes/libffi_msvc/ffi.c#L47
import ctypes
kernel32 = ctypes.WinDLL('kernel32')
hStdout = kernel32.GetStdHandle(-11)
written = ctypes.c_ulong()
kernel32.WriteFile(hStdout, b'spam\n', 5, ctypes.byref(written), None)
import ctypes
import msvcrt
import sys
kernel32 = ctypes.WinDLL('kernel32')
hStdout = msvcrt.get_osfhandle(sys.stdout.fileno())
written = ctypes.c_ulong()
kernel32.WriteFile(hStdout, b'spam\n', 5, ctypes.byref(written), None)
我为Windows构建了自己的Python 2.7.13,因为我正在使用我为第三方应用程序创建的绑定,该应用程序必须使用特定版本的Visual Studio 2012构建。 我开始使用“click”模块编写一些内容,并在使用click.echo时遇到任何类型的unicode echo时发现错误。
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
如果我下载并安装Python 2.7.13 64位安装程序,我不会遇到此问题。回声很好。 我对此进行了很多调查,现在感到很茫然。 我对Windows,Visual Studio或ctypes不太熟悉。
我花了一些时间查看代码路径来生成最小的文件(没有点击),这表明了这个问题(见下文) 它产生相同的“Windows错误6”...再次,这适用于从2.7.13 64位MSI安装程序安装的python。 有人可以共享用于创建Windows安装程序的进程吗?这是手动过程还是自动化? 也许我错过了一些重要的切换到msbuild或其他东西。任何帮助或想法都表示赞赏。 我不能使用下载的Python副本......它需要使用Visual Studio的特定版本,更新,补丁等构建。
我所做的只是
externals\tk-8.5.15.0\win\Makefile.in
删除ttkWinXPTheme.$(OBJEXT)
行externals\tk-8.5.15.0\win\makefile.vc
删除$(TMP_DIR)\ttkWinXPTheme.obj
行externals\tk-8.5.15.0\win\ttkWinMonitor.c
删除2 TtkXPTheme_Init
行
PCbuild\tcltk.props
中将VC9更改为底部的VC11 之后我通过复制.exe,.pyd,.dll文件,运行get-pip.py,然后python -m pip install virtualenv,然后virtualenv my_env创建了一个“安装”,然后激活它,然后做了一个点安装点击。 但是对于这个精简版本,你不需要pip,virtualenv或点击......只需要ctypes。 你甚至可以在不使用-e切换到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())