如何在Python中通过Win32 msvcrt.dll获取FILE*并输出内容
问题:通过ctypes调用msvcrt.dll实现标准输出打印(不使用print/msvcrt/os库)
需要不依赖print、msvcrt及os库,仅用ctypes调用Win32的msvcrt.dll打开标准输出并打印字符串。尝试用_fdopen和_fputs时持续触发OSError: exception: access violation reading 0x0000000036B5FAA8错误,推测是Python无法直接持有FILE*,转成int后无法转回有效指针,也可能存在其他操作问题。
出错的代码
c = __import__('ctypes.util') c_lib = c.CDLL(c.util.find_library('msvcrt')) fptr = c_lib._get_osfhandle( c.cast( 1, c.POINTER(c.c_int), ) ) realf = c_lib._open_osfhandle( fptr, c.cast( 8, c.POINTER(c.c_int), ) ) fd = c_lib._fdopen(realf, c.cast( b'w', c.POINTER(c.c_char), ) ) c_lib.fputs( c.cast( b'Hello msvcrt!', c.POINTER(c.c_char), ), fd ) c_lib._flushall() c_lib._close(realf) c_lib._exit( c.cast( 0, c.POINTER(c.c_int) ) )
可正常运行的对比代码(依赖msvcrt/os库)
import msvcrt, os fptr = msvcrt.get_osfhandle(1) realf = msvcrt.open_osfhandle(fptr, os.O_APPEND) fd = os.fdopen(realf, 'w') fd.write('hello') fd.flush() os.close(realf)
解决方案
核心是修正ctypes的类型声明和参数传递逻辑,具体修正如下:
- 显式声明每个msvcrt函数的参数类型(
argtypes)和返回值类型(restype),避免默认类型转换错误 - 直接传递整数参数,无需将int转为指针类型
- 用
c_void_p表示FILE*指针,确保指针有效性 - 使用正确的文件打开标志(
O_APPEND对应数值0x400) - 避免双重释放资源,
_fdopen接管文件描述符后无需手动调用_close
修正后的代码
import ctypes from ctypes.util import find_library msvcrt = ctypes.CDLL(find_library('msvcrt')) # 声明函数类型 msvcrt._get_osfhandle.argtypes = [ctypes.c_int] msvcrt._get_osfhandle.restype = ctypes.c_void_p msvcrt._open_osfhandle.argtypes = [ctypes.c_void_p, ctypes.c_int] msvcrt._open_osfhandle.restype = ctypes.c_int msvcrt._fdopen.argtypes = [ctypes.c_int, ctypes.c_char_p] msvcrt._fdopen.restype = ctypes.c_void_p msvcrt.fputs.argtypes = [ctypes.c_char_p, ctypes.c_void_p] msvcrt.fputs.restype = ctypes.c_int msvcrt._flushall.argtypes = [] msvcrt._flushall.restype = ctypes.c_int msvcrt._exit.argtypes = [ctypes.c_int] msvcrt._exit.restype = None # 标准输出的文件描述符是1 stdout_fd = 1 # 获取OS文件句柄 os_handle = msvcrt._get_osfhandle(stdout_fd) # 打开句柄,O_APPEND对应0x400 new_fd = msvcrt._open_osfhandle(os_handle, 0x400) # 转为FILE*指针 file_ptr = msvcrt._fdopen(new_fd, b'w') # 打印字符串 msvcrt.fputs(b'Hello msvcrt!', file_ptr) # 刷新缓冲区 msvcrt._flushall() # 退出程序 msvcrt._exit(0)
内容的提问来源于stack exchange,提问作者sawbez
相关产品推荐
相关产品推荐

