如何对64位数据进行位操作并输出到dll中的C函数

时间:2015-08-12 08:49:52

标签: python string bit-manipulation

我想将表示寄存器的压缩数组传递给接受char *的C函数(在DLL中)。这是我到目前为止的代码:

from ctypes import * 
class BitField():
    def __init__(self,position,size):
        self.position = position
        self.size = size

class Reg1(BitField):
    self.dll = "win32.dll"
    self.B6 = BitField(self,58,6)
    self.B5 = BitField(self,54,4)
    self.B4 = BitField(self,48,6)
    self.B3 = BitField(self,36,12)
    self.B2 = BitField(self,24,12)
    self.B1 = BitField(self,16,8)
    self.B0 = BitField(self,0,16)

pack_register(Reg1,(expects a tuple)):
   pass    # created a stub , need help on this   
#I want to pack the to 64 bit data, by padding all other zero's, 
#if none of bit #fields specified by user.

obj = Reg1()
obj.B5 = 4
obj.B4 = 12
obj.B0 = 70
charBuffer = create_string_buffer(64)
packed_value =pack_register(Reg1,(B5,B4,B0)) 
charBuffer  = packed_value
obj.dll.createBuff(charBuffer) # accepting a character pointer

这是win32.dll函数(在C中)

int createBuff (char * charBuffer){
   print charBuffer
}

从代码中的注释可以看出,我不知道如何将寄存器打包成64位。我也有32位和128位寄存器。

如何打包寄存器并以合适的格式输出以传递给我需要char *的C函数?

2 个答案:

答案 0 :(得分:5)

此代码创建一个类,让您阅读&使用字段名称写入位域。它没有记在ctypes,但你仍然觉得它很有用。

#!/usr/bin/env python

""" A bit field class

    See http://stackoverflow.com/q/31960327/4014959

    Written by PM 2Ring 2015.08.12
"""

class BitFields(object):
    """ A bit field class

        fieldwidth is a tuple or list containing the
        bit width of each field, from least significant
        to most significant.
    """
    def __init__(self, totalwidth, fieldwidths):
        if sum(fieldwidths) != totalwidth:
            raise ValueError, "Field width error"

        self.fieldwidths = fieldwidths
        self.num_fields = len(fieldwidths)

        #Calculate field offsets
        self.offsets = []
        pos = 0
        for w in fieldwidths:
            self.offsets.append(pos)
            pos += w

        #Set up bitfield attribute names
        self.field_names = ['b' + str(i) for i in range(self.num_fields)]
        self.clear()

    #Set all fields to zero
    def clear(self):
        for f in self.field_names:
            setattr(self, f, 0)

    #A generator expression of all the field values
    def _all_fields(self):
        return (getattr(self, f) for f in self.field_names)

    def __str__(self):
        return ', '.join(['%s: 0x%x' % (f, v) 
            for f, v in zip(self.field_names, self._all_fields())])

    #Get the register value as an int
    @property
    def value(self):
       return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))

    #Set field values 
    def regset(self, **kwargs):
        for f, v in kwargs.items():
            setattr(self, f, v)

#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)

#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3

#Print the register using its __str__ method
print reg

#Print a single field
print reg.b1

#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)

#Reset all fields to zero
reg.clear()
print reg

#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg

#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg

<强>输出

b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0

这是一个简单的Python 2 to_bytes()函数。

def to_bytes(n, width):
    b = bytearray(width)
    for i in range(width-1, -1, -1):
        b[i] = n & 0xff
        n >>= 8
        if n == 0:
            break
    return bytes(b)

n = 0x8182838485868788
print repr(to_bytes(n, 8))

<强>输出

'\x81\x82\x83\x84\x85\x86\x87\x88'

这是一个稍微修改过的版本,带有一个新方法.setvalue(),它允许您从整数设置寄存器的值。在构造函数中调用此方法,因此您现在可以传递一个可选的整数来初始化寄存器。如果没有初始值传递给构造函数,则寄存器初始化为零,如前所述。

class BitFields(object):
    """ A bit field class

        fieldwidth is a tuple or list containing the
        bit width of each field, from least significant
        to most significant.
    """
    def __init__(self, totalwidth, fieldwidths, value=0):
        if sum(fieldwidths) != totalwidth:
            raise ValueError, "Field width error"

        self.fieldwidths = fieldwidths
        self.num_fields = len(fieldwidths)

        #Calculate field offsets
        self.offsets = []
        pos = 0
        for w in fieldwidths:
            self.offsets.append(pos)
            pos += w

        #Set up bitfield attribute names
        self.field_names = ['b' + str(i) for i in range(self.num_fields)]

        #Initialize
        self.setvalue(value)

    #Set all fields to zero
    def clear(self):
        for f in self.field_names:
            setattr(self, f, 0)

    #A generator expression of all the field values
    def _all_fields(self):
        return (getattr(self, f) for f in self.field_names)

    def __str__(self):
        return ', '.join(['%s: 0x%x' % (f, v) 
            for f, v in zip(self.field_names, self._all_fields())])

    #Get the register value as an int
    @property
    def value(self):
       return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))

    #Set field values 
    def regset(self, **kwargs):
        for f, v in kwargs.items():
            setattr(self, f, v)

    #Set the register from an int 
    def setvalue(self, value):
        for f, w in zip(self.field_names, self.fieldwidths):
            #print f, w
            mask = (1<<w) - 1
            v = value & mask
            value >>= w
            setattr(self, f, v)


#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)

#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3

#Print the register using its __str__ method
print reg

#Print a single field
print reg.b1

#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)

#Reset all fields to zero
reg.clear()
print reg

#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg

#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg

#Set the register from an int or long
n = 0x111133337777ffff
reg = BitFields(64, fields, n)
print reg

v = reg.value
print v, hex(v), n == v

n = 0x123456789abcdef0
reg.setvalue(n)
print reg

v = reg.value
print v, hex(v), n == v

import random

print 'Testing .setvalue()...'
for _ in xrange(50000):
    n = random.randint(0, (1<<64) - 1)
    reg.setvalue(n)
    v = reg.value
    assert v == n, (n, v)
print 'OK'

<强>输出

b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0
b0: 0xffff, b1: 0x77, b2: 0x377, b3: 0x333, b4: 0x11, b5: 0x4, b6: 0x4
1229820469389557759 0x111133337777ffffL True
b0: 0xdef0, b1: 0xbc, b2: 0x89a, b3: 0x567, b4: 0x34, b5: 0x8, b6: 0x4
1311768467463790320 0x123456789abcdef0L True
Testing .setvalue()...
OK

答案 1 :(得分:4)

您可以创建一个能够将多个字段转换为单个int的寄存器(然后可以使用int.to_bytes将其转换为字节字符串)。例如:

class Register:
    def __init__ (self, *sizes):
        self.sizes = sizes

    def pack (self, *fields):
        offset = 0
        result = 0
        for value, size in zip(fields, self.sizes):
            result |= self.trimSize(value, size) << offset
            offset += size
        return result

    def unpack (self, value):
        offset = 0
        fields = []
        for size in self.sizes:
            fields.append(self.trimSize(value, size))
            value >>= size
        return fields

    def trimSize (self, value, size):
        return value & ((1 << size) - 1)

你可以像这样使用它:

>>> r = Register(16, 8, 12, 12, 6, 4, 6)
>>> r.pack(70, 0, 0, 12, 4, 0, 0)
1126724540563526
>>> _.to_bytes(8, 'big')
b'\x00\x04\x00\xc0\x00\x00\x00F'

您还可以再次解压缩值:

>>> r.unpack(1126724540563526)
[70, 0, 0, 12, 4, 0, 0]

您现在可以创建一个新的类型,它具有您的注册字段的多个属性,并在内部使用Register

class Reg1:
    def __init__ (self):
        self.r = Register(16, 8, 12, 12, 6, 4, 6)
        # default values
        self.B0 = self.B1 = self.B2 = self.B3 = self.B4 = self.B5 = self.B6 = 0
    def pack (self):
        return self.r.pack(self.B0, self.B1, self.B2, self.B3, self.B4, self.B5, self.B6)
    def unpack (self, value):
        self.B0, self.B1, self.B2, self.B3, self.B4, self.B5, self.B6 = self.r.unpack(value)

像这样使用:

>>> obj = Reg1()
>>> obj.B5 = 4
>>> obj.B4 = 12
>>> obj.B0 = 70
>>> obj.pack()
75435293758455878