Python: ctypes and Pointer to Structure - python

I am trying to make a pointer of a struct and then de-reference it. But it's crashing. I have mimic'ed the behvior here with this simple code.
from ctypes import *
import ctypes
class File(Structure):
_fields_ = [("fileSize", c_uint),
("fileName", c_byte * 32)]
f = File()
f.fileSize = 2
print(f.fileSize)
P = ctypes.POINTER(File)
p = P.from_address(addressof(f))
print(p.contents.fileSize)
Can someone point out what's the issue with this code?
Thanks in advance.

this works (I just tried):
p = pointer(f)
no need to instantiate P at all.
To be clearer, given the p and P look quite similar on screen:
from ctypes import *
class File(Structure):
_fields_ = [("fileSize", c_uint),
("fileName", c_byte * 32)]
f = File()
f.fileSize = 2
print(f.fileSize)
p = pointer(f)
print(p.contents.fileSize)

Related

ctypes: c_ulong type magically changed to long

I am writing the deepcopy function using python2.7 for my class. I encountered a weird problem
My code is the following
import copy
from ctypes import *
class Graph (Structure):
_fields_ = [("numVertices", c_ulong),
("numEdges", c_ulong)]
def __init__(self):
self.numVertices = c_ulong(0)
self.numEdges = c_ulong(0)
def __deepcopy__(self,memo={}):
newInstance = Graph()
newInstance.numVertices = c_ulong(self.numVertices.value)
newInstance.numEdges = c_ulong(self.numEdges.value)
return newInstance
graph = Graph()
anotherGraph = copy.deepcopy(graph)
I get the following error:
<ipython-input-46-a0cdaa4ef3f7> in __deepcopy__(self, memo)
9 def __deepcopy__(self,memo={}):
10 newInstance = Graph()
---> 11 newInstance.numVertices = c_ulong(self.numVertices.value)
12 newInstance.numEdges = c_ulong(self.numEdges.value)
13 return newInstance
AttributeError: 'long' object has no attribute 'value'
If you try:
type(graph.numVertices)
The result is long
I declared the numVertices as c_ulong(). Why does it become long?
The type of the fields in the structure is still maintained, but ctypes has some "helpful" conversions when reading the values:
from ctypes import *
class Test(Structure):
_fields_ = [('a',c_ulong),
('b',c_char_p)]
t = Test(1,b'hello')
print(type(t.a),type(t.b))
print(t._fields_)
Output:
<class 'int'> <class 'bytes'>
[('a', <class 'ctypes.c_ulong'>), ('b', <class 'ctypes.c_char_p'>)]
So you can write your code as the following and it will work correctly:
import copy
from ctypes import *
class Graph (Structure):
_fields_ = [("numVertices", c_ulong),
("numEdges", c_ulong)]
def __init__(self):
self.numVertices = 0
self.numEdges = 0
def __deepcopy__(self,memo={}):
newInstance = Graph()
newInstance.numVertices = self.numVertices
newInstance.numEdges = self.numEdges
return newInstance
graph = Graph()
anotherGraph = copy.deepcopy(graph)
You can suppress the conversions by deriving from the classes, but it is usually unnecessary. One use case is when using ctypes to call a function that returns an allocated string. You need to suppress the c_char_p to Python byte string conversion so you can later free the c_char_p.
from ctypes import *
class ulong(c_ulong): pass
class char_p(c_char_p): pass
class Test(Structure):
_fields_ = [('a',ulong),
('b',char_p)]
t = Test(1,b'hello')
print(type(t.a),type(t.b))
print(t.a,t.b)
Output:
<class '__main__.ulong'> <class '__main__.char_p'>
<ulong object at 0x0000000006263748> char_p(b'hello')

Dereference void pointer to numpy array or list

I've received a void pointer from a foreign function via ctypes, containing an array of c_double arrays:
[[12.0, 13.0], [14.0, 15.0], …]
I'm accessing it via the restype parameter:
from ctypes import Structure, POINTER, c_void_p, c_size_t, c_double, c_uint32, c_char_p, cast, cdll
class _CoordResult(Structure):
""" Container for returned FFI coordinate data """
_fields_ = [("coords", _FFIArray)]
class _FFIArray(Structure):
"""
Convert sequence of float lists to a C-compatible void array
example: [[1.0, 2.0], [3.0, 4.0]]
"""
_fields_ = [("data", c_void_p),
("len", c_size_t)]
def _void_array_to_nested_list(res, _func, _args):
""" Dereference the FFI result to a list of coordinates """
shape = (res.coords.len, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = string_at(res.coords.data, mem_size)
array = [list(pair) for pair in ((POINTER(c_double * 2).from_buffer_copy(res.coords)[:res.coords.len]))]
drop_array(res.coords)
return array
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _CoordResult
decode_polyline.errcheck = _void_array_to_nested_list
However, this gives me back nonsense values, because the pointer dereference in _void_array_to_nested_list is wrong.
The solution doesn't have to use NumPy, but that seems like the best approach.
I can't test this right now, but this is what I would try:
import numpy as np
result = ...
shape = (10, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = ctypes.string_at(result, mem_size)
array = np.frombuffer(array_str, float, array_size).reshape(shape)
array will be read only, copy it if you need a writable array.
Here is a solution that uses ctypes.cast or numpy.ctypeslib.as_array, and no ctypes.string_at just in case if it makes an extra copy of memory region.
class _FFIArray(Structure):
_fields_ = [("data", c_void_p), ("len", c_size_t)]
class Coordinate(Structure):
_fields_ = [("latitude", c_double), ("longitude", c_double)]
class Coordinates(Structure):
_fields_ = [("data", POINTER(Coordinate)), ("len", c_size_t)]
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _FFIArray
# assuming that the second argument is the length of polyline,
# although it should not be needed for `\0` terminated string
res = decode_polyline(polyline, len(polyline))
nres = Coordinates(cast(res.data, POINTER(Coordinate)), res.len)
for i in range(nres.len):
print(nres.data[i].latitude, nres.data[i].longitude)
# if just a numpy (np) array is needed
xs = np.ctypeslib.as_array((c_double * res.len * 2).from_address(res.data))
# "New view of array with the same data."
xs = xs.view(dtype=[('a', np.float64), ('b', np.float64)], type=np.ndarray)
xs.shape = res.len

which argtypes for NetShareAdd

The win32 function NetShareDel takes three arguments, LPCWSTR LPCWSTR and DWORD.
So I use the following list for argtypes:
import ctypes as C
C.windll.Netapi32.NetShareDel.argtypes = [LPCWSTR, LPCWSTR, c_int]
C.windll.Netapi32.NetShareDel.restype = c_int
C.windll.Netapi32.NetShareDel(server, shareName, 0)
That works fine, but I can't figure out what to use for NetShareAdd, especialle the byte array for NET_SHARE_INFO struct and the last byref(c_int) argument.
Here's the code:
def Share(server, shareName, dir):
info = SHARE_INFO_2()
STYPE_DISKTREE = 0
info.shi2_netname = shareName
info.shi2_path = dir
info.shi2_type = STYPE_DISKTREE
info.shi2_remark = "Shared: " + time.strftime("%Y%m%d-%H:%M")
info.shi2_max_uses = -1
info.shi2_passwd = ""
info.shi2_current_uses = 0
info.shi2_permissions = 0xFFFFFFFF
i = c_int()
bytearray = buffer(info)[:]
windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, ????, ????]
windll.Netapi32.NetShareAdd(server, 2, bytearray, C.byref(i))
What would be the correct argtypes list for NetShareAdd?
Got it working finally
First the line
bytearray = buffer(info)[:]
was changed into byte pointer type
byteptr = C.POINTER(C.wintypes.BYTE)(info)
and then the argtypes and call will become POINTER(BYTE) too of course:
C.windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, C.POINTER(C.wintypes.BYTE), C.POINTER(c_int)]
C.windll.Netapi32.NetShareAdd.restype = c_int
windll.Netapi32.NetShareAdd(server, 2, byteptr, C.byref(i))

OS X and ctypes: how to retrieve raw pixels from CGDataProviderCopyData()?

I am trying to capture the screen using only the ctypes modules. Unfortunately I cannot retrieve raw pixel from CGDataProviderCopyData. I need to get an access to raw data:
#!/usr/bin/env python
# coding: utf-8
from sys import maxsize
from ctypes import POINTER, Structure, c_double, byref, c_void_p, c_int32, c_uint32, c_float, cdll
from ctypes.util import find_library
CGFloat = c_double if maxsize > 2 ** 32 else c_float
class CGPoint(Structure):
_fields_ = [('x', CGFloat), ('y', CGFloat)]
class CGSize(Structure):
_fields_ = [('width', CGFloat), ('height', CGFloat)]
class CGRect(Structure):
_fields_ = [('origin', CGPoint), ('size', CGSize)]
# Library
cgs = cdll.LoadLibrary(find_library('CoreGraphics'))
# Argtypes
cgs.CGGetActiveDisplayList.argtypes = [c_uint32, POINTER(c_uint32), POINTER(c_uint32)]
cgs.CGDisplayBounds.argtypes = [c_uint32]
cgs.CGRectStandardize.argtypes = [CGRect]
cgs.CGDisplayRotation.argtypes = [c_uint32]
cgs.CGWindowListCreateImage.argtypes = [CGRect, c_uint32, c_uint32, c_uint32]
cgs.CGImageGetWidth.argtypes = [c_void_p]
cgs.CGImageGetHeight.argtypes = [c_void_p]
cgs.CGImageGetDataProvider.argtypes = [c_void_p]
cgs.CGDataProviderCopyData.argtypes = [c_void_p]
cgs.CGDataProviderRelease.argtypes = [c_void_p]
# Restypes
cgs.CGGetActiveDisplayList.restype = c_int32
cgs.CGDisplayBounds.restype = CGRect
cgs.CGRectStandardize.restype = CGRect
cgs.CGDisplayRotation.restype = c_float
cgs.CGWindowListCreateImage.restype = c_void_p
cgs.CGImageGetWidth.restype = c_uint32
cgs.CGImageGetHeight.restype = c_uint32
cgs.CGImageGetDataProvider.restype = c_void_p
cgs.CGDataProviderCopyData.restype = c_void_p
cgs.CGDataProviderRelease.restype = c_void_p
# Monitors
max_displays = 32
display_count = c_uint32(0)
active_displays = (c_uint32 * max_displays)()
cgs.CGGetActiveDisplayList(max_displays, active_displays, byref(display_count))
for idx in range(display_count.value):
display = active_displays[idx]
rect = cgs.CGDisplayBounds(display)
rect = cgs.CGRectStandardize(rect)
image_ref = cgs.CGWindowListCreateImage(rect, 1, 0, 0)
width = int(cgs.CGImageGetWidth(image_ref))
height = int(cgs.CGImageGetHeight(image_ref))
prov = cgs.CGImageGetDataProvider(image_ref)
data = cgs.CGDataProviderCopyData(prov)
# How to get raw pixels from data`?
MacOS X version 10.11.3.
Python versions 2.7.10 and 2.6.9.
I have no experience with the CoreGraphics API, but looking at the documentation it looks like CGDataProviderCopyData returns a CFDataRef object. The documentat for CFDataRef has a section on "Examining a CFData Object" which describes a CFDataGetLength function and a CFDataGetBytePtr function which return the length of the data and a UInt8*.
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGDataProvider/#//apple_ref/c/func/CGDataProviderCopyData
https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFDataRef/index.html
Thanks to Snorfalorpagus, I finally succeed:
#...
data = cgs.CGDataProviderCopyData(prov)
data_ref = cgs.CFDataGetBytePtr(data)
buf_len = cgs.CFDataGetLength()
image_data = cast(data_ref, POINTER(c_ubyte * buf_len))
cgs.CGDataProviderRelease(prov)
# Raw pixels are in image_data.contents

rsvg python memory leak in osx (ctypes?)

I am using the following code to read an svg:
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path, byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
(from Error with Python ctypes and librsvg)
I call img = Handle(path) and this leaks memory. I am pretty sure this is due to incorrectly using ctypes & pointers, but I cannot find a way to fix this.
According to the documentation of rsvg_handle_new, free the handle with g_object_unref. Also, if a failed call allocates a GError, after you get the code and message you have to free the error with g_error_free.
from ctypes import *
from ctypes.util import find_library
_gobj = CDLL(find_library("gobject-2.0"))
_glib = CDLL(find_library("glib-2.0"))
class _GError(Structure):
_fields_ = [("domain", c_uint32),
("code", c_int),
("message", c_char_p)]
_GErrorP = POINTER(_GError)
_glib.g_error_free.restype = None
_glib.g_error_free.argtypes = [_GErrorP]
_gobj.g_object_unref.restype = None
_gobj.g_object_unref.argtypes = [c_void_p]
You can free the handle in the __del__ method of the Handle class:
class Handle(object):
_gobj = _gobj # keep a valid ref for module teardown
# ...
def __del__(self):
if self.handle:
self._gobj.g_object_unref(self.handle)
self.handle = None

Categories

Resources