为什么不相关的代码有所作为?

时间:2013-08-01 12:16:12

标签: python windows winapi ctypes

我正在考虑在终端中使用python创建一个进度条。首先,我必须得到终端窗口的宽度(列)。在python 2.7中,没有标准库可以在Windows上执行此操作。我知道也许我必须手动调用Windows Console API。

根据MSDN和Python文档,我编写了以下代码:

import ctypes
import ctypes.wintypes

class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
    _fields_ = [
        ('dwSize', ctypes.wintypes._COORD),
        ('dwCursorPosition', ctypes.wintypes._COORD),
        ('wAttributes', ctypes.c_ushort),
        ('srWindow', ctypes.wintypes._SMALL_RECT),
        ('dwMaximumWindowSize', ctypes.wintypes._COORD)
    ]
hstd = ctypes.windll.kernel32.GetStdHandle(ctypes.c_ulong(-11)) # STD_OUTPUT_HANDLE = -11
print hstd
csbi = CONSOLE_SCREEN_BUFFER_INFO()
print ctypes.sizeof(csbi) # <---------------
ret = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(ctypes.c_ulong(hstd), csbi)
print ret
print csbi.dwSize.X

工作正常。我开始在代码中删除一些print。但在那之后,它不起作用! GetLastError返回6(无效句柄)。经过多次尝试,我发现代码的指向位置必须有一些东西,例如print 'hello'import syssys.stdout.flush()。起初,我想也许需要时间做点什么。所以我试着将time.sleep(2)放在那个位置,但它仍然不起作用。

但是,如果我使用struct代替ctypes.Structure,则没有这样的问题。

import ctypes
import struct

hstd = ctypes.windll.kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE = -11
csbi = ctypes.create_string_buffer(22)
res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(hstd, csbi)
width, height, curx, cury, wattr, left, top, right, bottom, maxx, maxy = struct.unpack("hhhhHhhhhhh", csbi.raw)
print bufx

有没有人可以告诉我为什么不相关的代码会产生这样的差异?

1 个答案:

答案 0 :(得分:2)

您需要通过引用传递结构:

ret = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(
    ctypes.c_ulong(hstd), 
    ctypes.byref(csbi)
)

我还建议您为restype声明GetStdHandle。这意味着您的代码已准备好在64位进程下运行。我会这样写:

ctypes.windll.kernel32.GetStdHandle.restype = ctypes.wintypes.HANDLE
hstd = ctypes.windll.kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE = -11
csbi = CONSOLE_SCREEN_BUFFER_INFO()
ret = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(
    hstd, 
    ctypes.byref(csbi)
)

实际上,在我的Python版本中,您编写的代码报告了一个更有用的错误。我明白这一点:

Traceback (most recent call last):
  File "test.py", line 16, in 
    ret = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(ctypes.c_ulong(hstd), csbi)
ValueError: Procedure probably called with too many arguments (20 bytes in 
excess)

这足以清楚地表明Python代码和本机代码之间的接口存在二进制不匹配。

我怀疑如果你得到更新版本的Python,你也会受益于这种堆栈不平衡检查。