有人向我反应,在代码里同时用我的python模块uiautomation和其它另一个模块后,脚本运行时会报错,但单独使用任意一个模块时都是正常的,没有错误。issue链接

我用一个例子来演示下这个问题是如何出现的。

假设我需要写一个module,这个module需要提供获取当前鼠标光标下窗口句柄的功能,这需要调用系统C API来实现。

实现如下:

module1.py


#!python3
# -*- coding: utf-8 -*-
import ctypes
import ctypes.wintypes class POINT(ctypes.Structure):
_fields_ = [("x", ctypes.wintypes.LONG),
("y", ctypes.wintypes.LONG)] ctypes.windll.user32.WindowFromPoint.argtypes = (POINT, )
ctypes.windll.user32.WindowFromPoint.restype = ctypes.c_void_p ctypes.windll.user32.GetCursorPos.argtypes = (ctypes.POINTER(POINT), ) def WindowFromPoint(x, y):
return ctypes.windll.user32.WindowFromPoint(POINT(x, y)) def GetCursorPos():
point = POINT(0, 0)
ctypes.windll.user32.GetCursorPos(ctypes.byref(point))
return point.x, point.y def WindowFromCursor():
x, y = GetCursorPos()
return WindowFromPoint(x, y)

WindowFromPoint和GetCursorPos是系统user32.dll里的函数。

WindowFromPoint的C/C++函数原型是 HWND WindowFromPoint( POINT Point ); POINT 是C/C++里的struct类型,HWND是void*类型。

WindowFromPoint.argtypes = (POINT, ) 设置函数的参数类型,如果在Python里调用WindowFromPoint传入的参数不是POINT类型就会提示参数类型不匹配。如果不设置argtypes也是可以调用的,但要保证传入的参数是合法的类型。

WindowFromPoint.restype = ctypes.c_void_p 设置函数的返回类型,这个是必要的,如果用的是64位版本,C函数返回是64位指针值,如果不设置,返回值会被截断成32位。

调用的代码如下

test.py

#!python3
# -*- coding:utf-8 -*-
import module1 def main():
print('the handle under cursor is', module1.WindowFromCursor()) if __name__ == '__main__':
main()

运行结果如下

the handle under cursor is 1839250

这时复制一份module1.py,重命名为module2.py,他们的代码是完全一样的

在test.py同时调用这两个module,代码如下

#!python3
# -*- coding:utf-8 -*-
import module1
import module2 def main():
print('the handle under cursor is', module1.WindowFromCursor())
print('the handle under cursor is', module2.WindowFromCursor()) if __name__ == '__main__':
main()

运行就会报错了

ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_POINT instance instead of pointer to POINT

但分开单独调用任一个模块就是正常的,不会出错。

这是因为,module1,module2调用的同一个C函数,在设置argtypes的时候,后面的修改会覆盖前面的设置。

执行

import module1
import module2

后,C函数中的POINT参数必须是module2.POINT才是合法的。

在用module1调用时,传入的参数类型是module1.POINT,运行时就会报错了。

这种错误应该只有在参数中有结构体或结构体指针时才会出现。

假设module1, module2分别是两个人写,而这两个module都会调用同一个C函数,C函数参数里有你自定义的ctypes.Structure类型,你又要同时用这两个module,只要有一个module设置了argtypes,运行时可能就会出错。

解决方法是,在module1, module2中注释两行代码

#ctypes.windll.user32.WindowFromPoint.argtypes = (POINT, )
ctypes.windll.user32.WindowFromPoint.restype = ctypes.c_void_p #ctypes.windll.user32.GetCursorPos.argtypes = (ctypes.POINTER(POINT), )

不要修改argtypes,也是可以调用的,再运行test.py就不会报错了。

使用ctypes调用系统C API函数需要注意的问题,函数参数中有指针或结构体的情况下最好不要修改argtypes的更多相关文章

  1. C语言--- 高级指针2(结构体指针,数组作为函数参数)

    一.结构体指针 1. 什么是结构体指针?指向结构体变量的指针     结构体:     typedef  struct stu{                          char name[ ...

  2. C++ 利用指针和数组以及指针和结构体实现一个函数返回多个值

    C++ 利用指针和数组实现一个函数返回多个值demo1 #include <iostream> using namespace std; int* test(int,int,int); i ...

  3. (60) 结构体指针、结构体变量嵌套、结构体指针嵌套、函数指针、数组指针、指针数组、typedef 综合运用

    #include<stdio.h> #include<iostream> #include<malloc.h> /* author : 吴永聪 program: 结 ...

  4. C语言基础知识点整理(函数/变量/常量/指针/数组/结构体)

    函数 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ...

  5. Delphi调用WINAPI时到底应该是指针还是结构体(注意是Delphi变量本身就是指针)

    看MSDN,GetWindowRect的说明如下: BOOL WINAPI GetWindowRect( _In_  HWND   hWnd, _Out_ LPRECT lpRect // 注意,没* ...

  6. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  7. 调用系统相机拍照,保存照片,调用系统裁剪API对照片处理,显示裁剪之后的照片

    package com.pingyijinren.test; import android.annotation.TargetApi; import android.app.Notification; ...

  8. Python3基础 函数 参数 在设定缺省值的情况下指明参数类型

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  9. 调用系统api修改系统时间

    一:截图 二:代码 using System; using System.Collections.Generic; using System.ComponentModel; using System. ...

随机推荐

  1. mac上运行shell脚本遇到回车字符错误

    今天运行一段其他人给的shell脚本,遇到如下问题,这个脚本的内容如下: dname=\((dirname "\)PWD") mkdir ${dname}"/rom_pu ...

  2. PorterDuffXfermode之Mode.SRC_IN

    package com.loaderman.customviewdemo.view; import android.content.Context; import android.graphics.B ...

  3. (三)java虚拟机内存管理和线程独占区和线程共享区

    一.内存管理 二.线程独占区之程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节 ...

  4. C#登录SSH执行命令,下载文件

    前言 批量登录SSH执行命令 ,把应急响应中的日志文件下载回来. 代码实现 Renci.SshNet编译出DLL,引用. using System; using System.Collections. ...

  5. iOS-NSBundle、NSArray、NSDictionay

    NSBundle.NSArray.NSDictionay 读取plist文件 NSDictionary  *dict= [NSDictionary dictionaryWithContentsOfFi ...

  6. iOS-UINavigationController多控制器管理

    UINavigationController 7.8.1 添加子控制器进栈 UINavigationController *nav = [[UINavigationController alloc]  ...

  7. sersync参数说明

    -v, --verbose 详细模式输出-q, --quiet 精简输出模式-c, --checksum 打开校验开关,强制对文件传输进行校验-a, --archive 归档模式,表示以递归方式传输文 ...

  8. 解决X-Scan安装后“无法启动此程序,因为计算机丢失NPPTools.dll”

    最近在一本书中看到X-Scan这个扫描器,虽说X-Scan相比现在的扫描器已经有点过时了,但也想下载来试一试,谁知道在VM中Win7安装时出现这种问题 可以在脚本之家找到缺失的这个文件:https:/ ...

  9. 二十四 java 多线程一些知识点

    1:blocked线程和waiting的线程的区别? 如何唤醒? java线程中含有waiting与blocked两种状态: 线程的 blocked状态往往是无法进入同步方法/代码块来完成的(BLOC ...

  10. 【C/C++开发】运算符重载

    c++的一大特性就是重载(overload),通过重载可以把功能相似的几个函数合为一个,使得程序更加简洁.高效.在c++中不止函数可以重载,运算符也可以重载.由于一般数据类型间的运算符没有重载的必要, ...