ctypes_hyper-competitive

ctypes_hyper-competitivedll:动态链接库 系统生成时的调用约定不同 cdll、windll和oledll 有些时候 dll 导出的函数不是Python中的有效值,比如 "??2@YAPAXI@Z". 在这种情况下你必须使用函数 getattr() 来获取函数: 在windows中,一些dll的导出函数

ctypes_hyper-competitive"h4 { font-size: 14px; font-weight: bold; margin: 10px 0; background-color: rgba(152, 213, 229, 1); border-radius: 6px; height: 35px; line-height: 35px; border: 1px solid rgba(57, 154, 178, 1); padding-left: 6px } h5 { font-size: 14px; font-weight: bold; margin: 10px 0; background-color: rgba(152, 213, 229, 1); border-radius: 6px; height: 35px; line-height: 35px; border: 1px solid rgba(57, 154, 178, 1); padding-left: 6px; width: 50% }

ctypes 是一个Python 标准库中的一个库.为了实现调用 DLL,或者共享库等C数据类型而设计.它可以把这些C库包装后在纯Python环境下调用.
加载动态链接库

dll:动态链接库 系统生成时的调用约定不同 cdll、windll和oledll 

CDLL:代码方式 cdecl 。

WINDLL:代码方式win32 stdcall 。

oledll使用win32调用代码方式 且返回值是windows里返回的hresult值,双字节的值说明函数执行结果,其最高bit位为0则执行成功,1则为执行失败。
                          详细见http:
//www.blogjava.net/JAVA-HE/archive/2010/01/04/308134.html。 cdecl和stdcall异同,参数入栈顺序均是从右向左,不同的是栈的清除工作,cdecl是由调用者负责清除,stdcall由被调用者清除。

 

注意 linux 平台上使用 cdll 的,而不是 windll。
from ctypes import windll # 首先导入 ctypes 模块的 windll 子模块
somelibc = windll.LoadLibrary(some.dll) # 使用 windll 模块的 LoadLibrary 导入动态链接库

from ctypes import cdll      
# 首先导入 ctypes 模块的 cdll 子模块,注意 linux 平台上使用 cdll 的,而不是 windll。
somelibc = cdll.LoadLibrary(“./some.so”)
 # 使用 cdll 模块的 LoadLibrary 导入动态链接库

 

stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的
加载的时候要根据你将要调用的函数是符合什么调用约定的
 
cdecl调用约定:也有两种加载方式 
    Objdll = ctypes.cdll.LoadLibrary("dllpath")  
    Objdll = ctypes.CDLL("dllpath")  
    /*其实windll和cdll分别是WinDLL类和CDll类的对象。*/ 
from ctypes import *

print(windll.kernel32)  # 使用windll约定方式导出 kernel32.dll 中的功能和信息
print(cdll.msvcrt)  # 使用cdll约定方式导出 msvcrt.dll 中的功能和信息

print(cdll.LoadLibrary('libc.so.6'))    
print(CDLL('libc.so.6'))    
# Linux动态链接库的编译与windows不同,后缀也不同,通常为 .so 文件,放在 usr/lib 文件夹下
# windows的dll大多放在Windows\System32文件夹下.

 

有些时候 dll 导出的函数不是Python中的有效值,比如 "??2@YAPAXI@Z". 在这种情况下你必须使用函数 getattr() 来获取函数:

getattr(cdll.msvcrt, "??2@YAPAXI@Z")   # 因为 cdll.msvcrt.??2@YAPAXI@Z 不合法,变量名(属性)不可以这样定义

 

获取函数

 

在加载dll的时候会返回一个DLL对象(假设名字叫Objdll),利用该对象就可以调用dll中的方法。 
e.g.
如果dll中有个方法名字叫Add(注意如果经过stdcall声明的方法,如果不是用def文件声明的导出函数或者extern “C” 声明的话,
                    编译器会对函数名进行修改,这个要注意,我想你们懂的。) 调用:nRet
= Objdll.Add(12, 15) 即完成一次调用。

有些时候 dll 导出的函数不是Python中的有效值,比如 “??2@YAPAXI@Z”. 在这种情况下你必须使用函数 getattr() 来获取函数:

getattr(cdll.msvcrt, "??2@YAPAXI@Z") # 因为 cdll.msvcrt.??2@YAPAXI@Z 不合法,变量名(属性)不可以这样定义

在windows中,一些dll的导出函数并不是按名字来的,而是下标数字. 这些函数可以用下标序号来获取,比如:

>>> cdll.kernel32[1]               # 通过下标获取函数信息
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]               # 可见并不是按顺序排列的...
Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "ctypes.py", line 310, in __getitem__
        func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>

 

 基本数据类型

 

ctypes type            C type                  Python type
c_bool               _Bool                   bool(1)
c_char                       char                        1-character bytes object
c_wchar                     wchar_t 1-charactor string      ①
c_byte                        char                               int
c_ubyte                    unsigned char                         int
c_short                       short                              int
c_ushort                   unsigned short                        int
c_int                          int int           ②
c_uint                      unsigned int                         int
c_long                        long                               int
c_ulong                     unsigned long                        int
c_longlong              __int64 or long long                     int
c_ulonglong       unsigned __int64 or unsigned long long         int
c_size_t                       size_t                            int
c_ssize_t                ssize_t or Py_ssize_t                   int
c_float                       float                              float
c_double                      double                             float
c_longdouble                  long double                        float
# 指针类型 c_char_p char * (NUL terminated) bytes object or None    ③ c_wchar_p wchar_t * (NUL terminated) string or None c_void_p void * int or None
                   Null             None            ④
只有移上四种两者间可以直接使用,其余都要经由ctypes包装 
Bottles                                  
自定义数据类型
class
Bottles: ... def __init__(self, number): ... self._as_parameter_ = number    # ,它必须是Python支持的四种类型之一

          

 

ctypes所有类型都是一个对象包装
c_int() # ctypes 中 c_int只是 c_long的别名而已
你应该小心,不要把这些指针传给试图改变内存的函数. 如果你确实需要改变内存数据而非替换指针地址, ctypes提供了create_string_buffer()函数.
from ctypes import *

s = "Hello, World"
c_s = c_wchar_p(s)
print(c_s)  # c_wchar_p(4765728)
c_wchar_p('Hello, World')
c_s.value = "Hi, there"
print(c_s)  # c_wchar_p(45431616)
c_wchar_p('Hi, there')
print(s)  # Hello, World            # first object is unchanged

 

from ctypes import *
p = create_string_buffer(3)            # create a 3 byte buffer, initialized to NUL bytes
print(sizeof(p), str(p.raw))
# 3 b'\x00\x00\x00'
p = create_string_buffer(b"Hello")     # create a buffer containing a NUL terminated string
print(sizeof(p), str(p.raw))
# 6 b'Hello\x00'
print(repr(p.value))
# b'Hello'
p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
print(sizeof(p), str(p.raw))
# 10 b'Hello\x00\x00\x00\x00\x00'
p.value = b"Hi"                       # 这里注意 p.value = b'HI'并不是把value替换成常量b'HI'的指针,而是直接修改了buffer
print(sizeof(p), repr(p.raw))
# 10 b'Hi\x00lo\x00\x00\x00\x00\x00'        # 从这里看得到,确实是 buffer 被修改了,上一次的值Hello 中的lo还在内存之中.

create_string_buffer() 函数代替了以前的 c_buffer() 函数(现在依旧可用,作为别名). 为了创建可修改的unicode wchar_t类型内存块, 请使用 create_unicode_buffer() 函数.

 

返回值类型
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d")) 
8059983            # 这里ctypes并不知道返回的是什么,所以默认直接就把指针地址打印了出来(int型)
>>> strchr.restype = c_char_p   # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'             # 这里设置过了,ctypes知道返回的是c_char_p类型,所以打印该指针指向的字符串数据
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
 参数类型
printf.argtypes = [c_char_p, c_char_p, c_int, c_double]  # 指定4个参数,按顺序
>>> dll.addf.argtypes = (c_float, c_float) # addf 有两个形参,都是 float 类型
或者是下面这样,但是,你知道的,查找元组的效率略高:)
>>> dll.addf.argtypes = [c_float, c_float] # addf 有两个形参,都是 float 类型
结构体

 

typedef struct _SimpleStruct  
{  
    int    nNo;  
    float  fVirus;  
    char   szBuffer[512];  
} SimpleStruct, *PSimpleStruct;  
typedef const SimpleStruct*  PCSimpleStruct;  
  
extern "C"int  __declspec(dllexport) PrintStruct(PSimpleStruct simp);  
int PrintStruct(PSimpleStruct simp)  
{  
    printf ("nMaxNum=%f, szContent=%s", simp->fVirus, simp->szBuffer);  
return simp->nNo;  
}  
from ctypes import *  
class SimpStruct(Structure):  
    _fields_ = [ ("nNo", c_int),  
              ("fVirus", c_float),  
              ("szBuffer", c_char * 512)]  
  
dll = CDLL("AddDll.dll")  
simple = SimpStruct();  
simple.nNo = 16  
simple.fVirus = 3.1415926  
simple.szBuffer = "magicTong/0"  
print dll.PrintStruct(byref(simple))  

 

指针

ctypes内和指针有关的三个参数

函数 说明
byref(x [, offset]) 返回 x 的地址,x 必须为 ctypes 类型的一个实例。相当于 c 的 &x 。 offset 表示偏移量。
pointer(x) 创建并返回一个指向 x 的指针实例, x 是一个实例对象。
POINTER(type) 返回一个类型,这个类型是指向 type 类型的指针类型, type 是 ctypes 的一个类型。

 

 

 

 

 

 

byref 很好理解,传递参数的时候就用这个,用 pointer 创建一个指针变量也行,不过 byref 更快。
而 pointer 和 POINTER 的区别是,pointer 返回一个实例,POINTER 返回一个类型。甚至你可以用 POINTER 来做 pointer 的工作:
>>> a = c_int(66)         # 创建一个 c_int 实例
>>> b = pointer(a)        # 创建指针
>>> c = POINTER(c_int)(a) # 创建指针
>>> b
<__main__.LP_c_long object at 0x00E12AD0>
>>> c
<__main__.LP_c_long object at 0x00E12B20>
>>> b.contents            # 输出 a 的值
c_long(66)
>>> c.contents            # 输出 a 的值
c_long(66)
pointer 创建的指针貌似没方法修改指向的 ctypes 类型值。
该说的都说了,接下来就要调用 print_point 函数了:
>>> dll.print_point.argtypes = (POINTER(Point),)   # 指明函数的参数类型
>>> dll.print_point.restype = None                 # 指明函数的返回类型
>>>
>>> p = Point(32.4, -92.1)      # 实例化一个 Point
>>> dll.print_point(byref(p))   # 调用函数
position x 32.400002 y -92.099998>>>
>>> dll.print_point(pointer(p))  # 调用函数
position x 32.400002 y -92.099998>>>

 

今天的文章ctypes_hyper-competitive分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/47102.html

(0)
编程小号编程小号
上一篇 2023-09-05 08:17
下一篇 2023-09-05 08:46

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注