自定义版本

时间:2017-03-28 02:33:28

标签: python windows visual-studio visual-studio-2012 ctypes

编辑(开始)

在与@eryksun共度时光之后,我们深入挖掘并发现了潜在的问题。这两个示例fail1.pyfail2.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

fail1.py

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)

fail2.py

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的特定版本,更新,补丁等构建。

我所做的只是

  • 克隆cpython来自github和checkout 2.7.13
  • 编辑tk中的一些xp东西,让它在Windows Server 2003上编译
    • 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
  • PCbuild \ build.bat -e -p x64“/ p:PlatformToolset = v110”

之后我通过复制.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())

0 个答案:

没有答案