Python调用ctypes使用C函数printf

本文介绍如何在Python 3环境下正确调用C语言的printf函数,包括解决Unicode编码问题的方法,并提供在IDLE环境中显示输出结果的技巧。
    在Python程序中导入ctypes模块,载入动态链接库。动态链接库有三种:cdll以及windows下的windll和oledll,cdll载入导出函数使用标准的cdecl调用规范的库,而windll载入导出函数符合stdcall调用规范(Win32 API的原生约定)的库,oledll也使用stdcall调用规范,并假设函数返回Windows的HRESULT错误代码。错误代码用于在出错时自动抛出WindowsError这个Python异常,可以使用COM函数得到具体的错误信息。
    使用cdll.msvcrt即可调用MS标准的C库msvcrt,msvcrt包含了大部分标准C函数。


    下面来看一下简单的printf函数。


from ctypes import *
msvcrt = cdll.msvcrt
str = "Huanhuan!"
msvcrt.printf("Hello %s\n", str)


    这样就可以使用C语言中的printf函数进行输出。
    如果在IDLE里运行的话会发现程序没有任何输出结果,这是因为printf是打印到真实的标准输出,而不是sys.stdout。如果想要看到运行结果,可以在CMD里运行python test.py来查看结果,前提是已经设置好了Python的环境变量。或者有一个曲线方法可以在IDLE中显示输出结果,请曲线阅读到文章最后。


    如果使用的是Py3K,在控制台里会看到只有开头字符H被输出了。因为Py3K使用的是Unicode编码,而printf不支持该编码,所以需要转码。整理出来三种改写方法可以解决这一问题。


# A 转为byte类型 在字符串前面加b
from ctypes import *
msvcrt = cdll.msvcrt
str = b"Huanhuan!"
msvcrt.printf(b"Hello %s\n", str)


# B 使用wprintf宽字符显示
from ctypes import *
msvcrt = cdll.msvcrt
str = "Huanhuan!"
msvcrt.wprintf("Hello %s\n", str)


# C 转码为utf-8
from ctypes import *
msvcrt = cdll.msvcrt
str = "Huanhuan!"
result = "Hello " + str + "\n"
result = result.encode("utf-8")
msvcrt.printf(result)


    最后来搞定在IDLE中曲线显示输出结果的方法。


from ctypes import *
msvcrt = cdll.msvcrt
str = b"Huanhuan!"
s = create_string_buffer(100)   # 必须足够长
msvcrt.sprintf(s, b'Hello %s\n', str)
print(s.value.decode('utf-8'))


    先使用sprintf函数把结果输出到s变量,然后再用Python自带的print方法输出s的value。


    好了,通过以上的各种方法就可以解决Py3K调用C函数printf的问题了。


    什么?你问我为什么费这么大劲非要用printf输出,而不是直接用Python自带的print?
    给我出去站着去!

    我乐意!


From: http://blog.sina.com.cn/s/blog_4a94a0db010136oj.html

<think>我们正在讨论通过Pythonctypes库来调用Cmd的方法。请注意,ctypes主要用于调用C语言编写的动态链接库(DLL中的函数,而Cmd是Windows命令提示符程序,并不是一个可以直接通过C函数调用的库。但是,我们可以通过调用Windows API函数(如system或CreateProcess)来执行命令行命令。 根据引用[1]和[2],我们知道ctypes可以调用Windows API,这些API位于Windows系统的动态链接库中,比如msvcrt.dll(用于C标准库函数)或kernel32.dll(用于Windows系统函数)。 在C语言中,我们可以使用system函数(在stdlib.h中声明)来执行一个命令行字符串。同样,在Python中,我们可以使用ctypes调用这个system函数。 另外,引用[3]提到,在IDLE中直接调用printf可能看不到输出,因为printf输出到真实的标准输出,而IDLE重定向了标准输出。同样的道理,使用system函数执行命令,输出也会在真实控制台中显示。因此,如果在IDLE中运行,可能需要通过命令行(CMD)运行Python脚本才能看到输出。 下面我将介绍两种方法: 1. 使用msvcrt.dll中的system函数(简单命令执行) 2. 使用kernel32.dll中的CreateProcess函数(更复杂的进程创建) ### 方法1:使用`system`函数 `system`函数是C标准库中的函数,在Windows中通常由msvcrt.dll提供(注意:不同版本的Windows可能使用不同的运行库,例如ucrtbase.dll)。我们可以通过ctypes调用它。 ```python import ctypes # 加载msvcrt.dll # 注意:在较新的Windows版本中,可能需要使用ucrtbase.dll,但msvcrt.dll通常也可用 libc = ctypes.CDLL('msvcrt.dll') # 或者使用ctypes.cdll.msvcrt # 声明system函数 system = libc.system system.argtypes = [ctypes.c_char_p] # 参数是一个字符串 system.restype = ctypes.c_int # 返回值是整型 # 执行命令 command = b"echo Hello, World!" # 注意:这里必须是bytes类型 return_code = system(command) print(f"命令执行返回码: {return_code}") ``` 注意:`system`函数会启动一个命令解释器(通常是cmd.exe)来执行命令字符串。它会在当前控制台窗口中运行命令,并等待命令执行完成。 ### 方法2:使用`CreateProcess`函数 如果需要对进程创建进行更精细的控制(例如,隐藏窗口、获取输出等),可以使用Windows API `CreateProcess`。 以下是一个简单的示例,使用`CreateProcess`来执行命令: ```python import ctypes # 定义必要的Windows类型和函数 kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # 定义结构体和常量 STARTUPINFO = ctypes.c_void_p PROCESS_INFORMATION = ctypes.c_void_p # 设置CreateProcess的参数类型 kernel32.CreateProcessA.argtypes = [ ctypes.c_char_p, # lpApplicationName ctypes.c_char_p, # lpCommandLine ctypes.c_void_p, # lpProcessAttributes ctypes.c_void_p, # lpThreadAttributes ctypes.c_bool, # bInheritHandles ctypes.c_uint32, # dwCreationFlags ctypes.c_void_p, # lpEnvironment ctypes.c_char_p, # lpCurrentDirectory ctypes.POINTER(STARTUPINFO), # lpStartupInfo ctypes.POINTER(PROCESS_INFORMATION) # lpProcessInformation ] kernel32.CreateProcessA.restype = ctypes.c_bool # 创建STARTUPINFO和PROCESS_INFORMATION结构体实例 class STARTUPINFO(ctypes.Structure): _fields_ = [ ("cb", ctypes.c_uint32), ("lpReserved", ctypes.c_char_p), ("lpDesktop", ctypes.c_char_p), ("lpTitle", ctypes.c_char_p), ("dwX", ctypes.c_uint32), ("dwY", ctypes.c_uint32), ("dwXSize", ctypes.c_uint32), ("dwYSize", ctypes.c_uint32), ("dwXCountChars", ctypes.c_uint32), ("dwYCountChars", ctypes.c_uint32), ("dwFillAttribute", ctypes.c_uint32), ("dwFlags", ctypes.c_uint32), ("wShowWindow", ctypes.c_uint16), ("cbReserved2", ctypes.c_uint16), ("lpReserved2", ctypes.c_void_p), ("hStdInput", ctypes.c_void_p), ("hStdOutput", ctypes.c_void_p), ("hStdError", ctypes.c_void_p), ] class PROCESS_INFORMATION(ctypes.Structure): _fields_ = [ ("hProcess", ctypes.c_void_p), ("hThread", ctypes.c_void_p), ("dwProcessId", ctypes.c_uint32), ("dwThreadId", ctypes.c_uint32), ] # 初始化结构体 si = STARTUPINFO() si.cb = ctypes.sizeof(STARTUPINFO) pi = PROCESS_INFORMATION() # 执行命令 command_line = b"cmd /c echo Hello, World!" # /c表示执行后关闭 if kernel32.CreateProcessA( None, # 使用命令行指定程序 command_line, None, None, False, 0, None, None, ctypes.byref(si), ctypes.byref(pi) ): print("进程创建成功") # 等待进程结束 kernel32.WaitForSingleObject(pi.hProcess, -1) # -1表示无限等待 kernel32.CloseHandle(pi.hProcess) kernel32.CloseHandle(pi.hThread) else: print("进程创建失败") error_code = ctypes.get_last_error() print(f"错误代码: {error_code}") ``` 注意:`CreateProcess`的使用较为复杂,上述代码仅为示例。在实际使用中,可能还需要考虑错误处理、标准输入输出重定向等。 ### 总结 - 对于简单的命令执行,使用`system`函数是最简单的。 - 如果需要更复杂的控制(如隐藏窗口、获取输出、异步执行等),则需要使用`CreateProcess`或`CreateProcessW`(宽字符版本)以及其他的进程控制函数。 另外,引用[3]提到,如果是在IDLE中运行,可能看不到输出,因为输出在另一个控制台窗口中。因此,建议在命令行中运行上述代码。 §§相关问题§§ 1. 使用ctypes调用system函数执行命令,如何捕获命令的输出? 2. 在Windows上,如何通过ctypes隐藏cmd窗口? 3. 使用ctypes调用CreateProcess,如何传递环境变量? </think> ### Python使用ctypes调用Cmd的方法 通过Pythonctypes调用命令行(Cmd)主要有两种方式,核心思路是调用Windows API中的命令行执行函数。以下是具体实现方法: #### 方法1:使用`system()`函数(简单命令执行) ```python import ctypes # 加载C标准库(Windows使用msvcrt.dll) libc = ctypes.CDLL('msvcrt.dll') # 或使用ctypes.cdll.msvcrt # 定义函数原型 system = libc.system system.argtypes = [ctypes.c_char_p] system.restype = ctypes.c_int # 执行CMD命令(注意:命令需转为bytes类型) cmd = b"echo Hello, CMD!" # 示例命令 exit_code = system(cmd) print(f"命令退出代码: {exit_code}") ``` - **说明**: - 直接调用C标准库的`system()`函数执行命令字符串 - 输出内容会显示在调用Python的控制台窗口中 - 返回值是命令的退出状态码(0表示成功)[^1][^3] #### 方法2:使用`CreateProcess()`(高级控制) ```python import ctypes # 加载kernel32.dll kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # 定义结构体和常量 class STARTUPINFO(ctypes.Structure): _fields_ = [ ("cb", ctypes.c_uint32), ("lpReserved", ctypes.c_char_p), ("lpDesktop", ctypes.c_char_p), ("lpTitle", ctypes.c_char_p), # ...(其他字段省略,实际使用需完整定义) ] class PROCESS_INFORMATION(ctypes.Structure): _fields_ = [ ("hProcess", ctypes.c_void_p), ("hThread", ctypes.c_void_p), ("dwProcessId", ctypes.c_uint32), ("dwThreadId", ctypes.c_uint32) ] # 创建进程执行CMD def run_cmd(command): si = STARTUPINFO() si.cb = ctypes.sizeof(STARTUPINFO) pi = PROCESS_INFORMATION() success = kernel32.CreateProcessW( None, # 应用程序名(None表示从命令行解析) ctypes.c_wchar_p("cmd.exe /c " + command), # 完整命令 None, None, False, 0, None, None, ctypes.byref(si), ctypes.byref(pi) ) if success: # 等待进程结束 kernel32.WaitForSingleObject(pi.hProcess, -1) # 关闭句柄 kernel32.CloseHandle(pi.hProcess) kernel32.CloseHandle(pi.hThread) return 0 else: raise ctypes.WinError(ctypes.get_last_error()) # 使用示例 run_cmd("dir C:\\") # 列出C盘根目录 ``` - **优势**: - 可控制进程属性(如窗口可见性、输入输出重定向) - 支持异步执行和进程监控 - 更细粒度的错误处理[^2][^4] #### 关键注意事项: 1. **字符串编码**: - `system()`需`bytes`类型(如`b"command"`) - `CreateProcessW()`需Unicode字符串(`c_wchar_p`) 2. **输出重定向**: - 若需捕获输出,需使用管道重定向(通过`CreateProcess`的`hStdOutput`) 3. **管理员权限**: - 需要提权执行的命令,需先提升Python进程权限 4. **错误处理**: - 检查API返回值,使用`ctypes.get_last_error()`获取错误码 > **注意**:在IDLE中运行,命令输出可能不可见,建议在CMD终端直接执行Python脚本[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值