2013-08-01 68 views
5

Tôi đang nghĩ để tạo một thanh tiến trình với python trong terminal. Đầu tiên, tôi phải lấy chiều rộng (cột) của cửa sổ đầu cuối. Trong python 2.7, không có thư viện chuẩn nào có thể thực hiện điều này trên Windows. Tôi biết có lẽ tôi phải gọi Windows API API theo cách thủ công.Tại sao mã không liên quan tạo nên sự khác biệt?

Theo MSDN và Python Tài liệu, tôi đã viết đoạn mã sau:

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 

Nó hoạt động tốt. Tôi đã đặt xóa một số mã print. Nhưng sau đó, nó không hoạt động! GetLastError trả lại 6 (Xử lý không hợp lệ). Sau nhiều lần cố gắng, tôi thấy rằng phải có SOMETHING ở vị trí được chỉ định của mã như print 'hello', import sys hoặc sys.stdout.flush(). Lúc đầu, tôi đoán rằng có lẽ nó cần thời gian để làm điều gì đó. Vì vậy, tôi đã cố gắng để đặt time.sleep(2) tại vị trí đó, nhưng nó vẫn không hoạt động.

Nhưng, nếu tôi sử dụng struct thay vì ctypes.Structure, không có vấn đề như vậy.

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 

Có ai có thể cho tôi biết lý do mã không liên quan tạo sự khác biệt như vậy không?

+0

Để sử dụng 'GetLastError' đáng tin cậy từ Python, hãy sử dụng' kernel32 = ctypes.WinDLL ('kernel32', use_last_error = True) 'và sau đó gọi' ctypes.get_last_error'. – eryksun

Trả lời

1

Bạn cần phải vượt qua các cấu trúc bằng cách tham khảo:

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

Tôi cũng sẽ khuyên bạn nên khai báo restype cho GetStdHandle. Điều đó có nghĩa là mã của bạn đã sẵn sàng để chạy theo quy trình 64 bit. Tôi viết như sau:

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) 
) 

Thực ra, trong phiên bản Python của tôi, bạn mã báo cáo lỗi hữu ích hơn nhiều. Tôi thấy điều này:

 
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) 

Điều này là đủ để làm rõ rằng có sự không tương thích nhị phân ở giao diện giữa mã Python và mã gốc.

Tôi nghi ngờ rằng nếu bạn nhận được phiên bản Python mới hơn, bạn cũng sẽ được hưởng lợi từ kiểm tra mất cân đối ngăn xếp này.

+0

Tôi đã trid đó. Nhưng nếu tôi sử dụng 'ctypes.byref' (như những gì tôi đã làm lúc đầu), tôi không thể có được giá trị đúng cho dù tôi giữ đường thẳng. – SEIAROTg

+0

Mã trong câu trả lời của tôi là chính xác và hoạt động. –

+0

@eryksun Tôi không chắc chắn tôi sẽ làm theo bình luận thứ hai của bạn. Nếu tôi gán 'HANDLE' cho' GetStdHandle.restype' thì 'hstd' sẽ thuộc kiểu' HANDLE'. Sau đó, khi tôi chuyển 'hstd' sang' GetConsoleScreenBufferInfo' không có nghĩa là ctypes sẽ sử dụng kiểu 'hstd' để sắp xếp chính xác một đối tượng có kích thước con trỏ ngay cả khi không có đặc tả' argtypes' rõ ràng. –