Mittels ctypes und Structure aus Shared Memory Bereich lesen

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
MacSt0815
User
Beiträge: 4
Registriert: Mittwoch 1. Juni 2016, 15:12

Hallo,

zunächst Mal ein paar Infos zu mir......ich bin ziemlich blutiger Anfänger was Python und ctypes betrifft. Mit C, C++ und Java habe ich Erfahrung.

Ich versuche gerade unter Windows mittels ctypes Daten aus einem Shared Memory Bereich zu lesen, welche von einem anderen Programm (dem Spiel Race07) geschrieben werden. Die Struktur der Daten (Telemetriedaten) ist mir bekannt und ich habe in Python eine entsprechende Struktur definiert:

Code: Alles auswählen

class stru(Structure):
_fields_ = [
("float1", c_float),
("float2", c_float),
("float3", c_float),
("float4", c_float),
("float5", c_float),
("float6", c_float),
("engine_rps", c_float),
("max_engine_rps", c_float),
("fuel_pressure", c_float),
("fuel_left", c_float),
("fuel_capacity", c_float),
("engine_water_temp", c_float),
("engine_oil_temp", c_float),
("engine_oil_pressure", c_float),
("car_speed", c_float),
("number_of_laps", c_int32),
("completed_laps", c_int32),
("lap_time_best_self", c_float),
("lap_time_previous_self", c_float),
("lap_time_current_self", c_float),
("position", c_int32),
("num_cars", c_int32),
("gear", c_int32)
]
Ich habe den relevanten Shared Memory Filehandle schon erfolgreich geöffnet (zumindest wird kein Fehler geprinted ;-))

Code: Alles auswählen

FILE_MAP_ALL_ACCESS = 0xF001F
INVALID_HANDLE_VALUE = 0xFFFFFFFF
SHMEMSIZE = sizeof(stru)
PAGE_READWRITE = 0x04
szName = create_string_buffer(b'$Race$')
szMsg = "Message from Python(ctypes) process"

hMapObject = windll.kernel32.CreateFileMappingA(INVALID_HANDLE_VALUE,
None, PAGE_READWRITE, 0, SHMEMSIZE, szName)
if (hMapObject == 0):
print ("Could not open file mapping object")
raise WinError()

pBuf = windll.kernel32.MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS,
0, 0, SHMEMSIZE)
if (pBuf == 0):
print ("Could not map view of file")
raise WinError()
else:
Und jetzt meine Frage bzgl. des Codes nach dem "else" :
Wie kann ich auf die Daten im Shared Memory mittels meiner Struktur zugreifen? In C würde ich einfach einen Pointer mit Typ meiner Struktur erzeugen und dann auf die Adresse zeigen lassen, die "MapViewOfFile" zurückliefert.
Ich habe allerdings keine Ahnung wie ich ....
1) einen solchen Pointer mittels ctypes erzeuge
2) auf die Felder der Struktur zugreifen kann auf die der Pointer zeigt

Ich hoffe, dass ich mein Anliegen einigermaßen verständlich formuliert habe und würde mich über Tips freuen ;-)

Grüße,
Mac
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was spricht gegen das naheliegende ctypes.pointer() in Verbindung mit ctypes.cast()?
MacSt0815
User
Beiträge: 4
Registriert: Mittwoch 1. Juni 2016, 15:12

Hallo,

sowas in der Art habe ich in anderen Foren auch schon gesehen und auch versucht es hinzubekommen.
Mir ist allerdings die korrekte Vorgehensweise nicht klar :-(

In C würde es ja z.B. so aussehen:
[codebox=c file=Unbenannt.c]stru *pStru = MapViewOfFile(...);
int iGear = pStru->gear;[/code]

Aber ich verstehe eben nicht wie ich das in python + ctypes übersetzen kann :-(
Zuletzt geändert von Anonymous am Mittwoch 1. Juni 2016, 21:07, insgesamt 1-mal geändert.
Grund: Quelltext in Codebox-Tags gesetzt.
MacSt0815
User
Beiträge: 4
Registriert: Mittwoch 1. Juni 2016, 15:12

Ich vermute mit

Code: Alles auswählen

myPtr = POINTER(stru)
erzeuge ich einen Pointer auf meine Struktur.
Aber wie weise ich dem Pointer dann den die Adresse im Speicher zu?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest Returnwert von MapViewOfFile auf c_void_p setzen (wie das geht steht in der Doku) bevor du es aufrufst.

Dann solltest du mit ctypes cast auf deinen POINTER-typen casten, und mit dann mit contents auf die Werte zugreifen koennen.
BlackJack

@MacSt0815: In C würde das, wenn man pedantisch ist so aussehen, weil `MapViewOfFile()` ja einen void-Zeiger liefert:
[codebox=c file=Unbenannt.c]struct stru *stru = (struct stru*) MapViewOfFile(...);
int gear = stru->gear;[/code]
Und diesen Cast muss man in Python dann auch tatsächlich machen, damit Python weiss welchen Typ man dort vor sich hat. Im Gegensatz zu C ist Python streng typisiert, das heisst es ist nicht alles nur eine Zahl die man nahezu beliebig interpretieren kann, man muss explizit sagen *als was* sie interpretiert werden soll. Und da die Namen keine Typinformation tragen, sondern die Werte, muss man das auch mit Hilfe der Werte regeln.

Womit wir beim nächsten Thema wären: C hat statische Typen zur Übersetzungszeit und Werte zur Laufzeit. Python hat nur Werte zur Laufzeit, das heisst auch die statischen Typen die es in C gibt, werden in Python zur Laufzeit als Werte modelliert. ``POINTER(stru)`` erzeugt keinen Wert der ein Zeiger auf eine `stru`-Struktur ist, sondern der Wert dieses Ausdrucks steht für den C-Daten*typ* „Zeiger auf `stru`“. Mit diesem Typ, der in Python ein Wert ist, kann man dann einen Wert erzeugen, welcher dann tatsächlich auf ein solchen Wert vom Typ `stru` zeigt. Du solltest das also wie einen Typ benennen. Beispielsweise `StruPtr` oder `STRU_PTR`. Den kann man dann beispielsweise mit einer Zahl oder einem `c_void_p`-Exemplar als Argument aufrufen. Ungetestet:

Code: Alles auswählen

# ...
StruPtr = POINTER(Stru) # Bennene bitte Deine Klasse um, sonst wird das echt wirr.
stru_ptr = StruPtr(MapViewOfFile(...))
gear = stru_ptr.contents.gear
# oder
gear = stru_ptr[0].gear
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

@BlackJack: ohne c_void_p als Returnwert wird das mit 64 Bit krachen denke ich, weil ctypes dan int annimmt.
BlackJack

@__deets__: Dem habe ich im Grunde nicht widersprochen. Falls Du damit meinst das ich geschrieben habe, dass man den Pointertyp mit einer ganzen Zahl oder einem `c_void_p`-Exemplar aufrufen kann: Letzteres gibt's nicht. Wenn man `c_void_p` als Rückgabewert einer C-Funktion definiert wird eine Zahl zurückgegeben.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

@BlackJack mir geht es nicht um widersprechen, sondern den Hinweis auf eine unbeabsichtigte truncation (die auch noch je nach Adressmapping sporadisch auftritt). Das ist alles. Wenn der TE deinen Code nimmt und es klappt 10 mal, aber beim 11ten nicht, ist halt oede.

Mehr als ein func.restype = c_void_p (oder gar gleich StruPtr) braucht's ja nicht.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@MacSt0815: vergiss, dass Pointer oder Windows-APIs gibt, Python liefert alles, um sich nicht auf ein so niedriges Niveau begeben zu müssen:

Code: Alles auswählen

import mmap
from ctypes import Structure, sizeof, c_float, c_int32

class stru(Structure):
    _fields_ = [
    ("float1", c_float),
    ("float2", c_float),
    ("float3", c_float),
    ("float4", c_float),
    ("float5", c_float),
    ("float6", c_float),
    ("engine_rps", c_float),
    ("max_engine_rps", c_float),
    ("fuel_pressure", c_float),
    ("fuel_left", c_float),
    ("fuel_capacity", c_float),
    ("engine_water_temp", c_float),
    ("engine_oil_temp", c_float),
    ("engine_oil_pressure", c_float),
    ("car_speed", c_float),
    ("number_of_laps", c_int32),
    ("completed_laps", c_int32),
    ("lap_time_best_self", c_float),
    ("lap_time_previous_self", c_float),
    ("lap_time_current_self", c_float),
    ("position", c_int32),
    ("num_cars", c_int32),
    ("gear", c_int32)
    ]

buf = mmap.mmap(-1, sizeof(stru), "$Race$")
data = stru.from_buffer(buf)
print data.max_engine_rps
Mehr braucht es nicht.
MacSt0815
User
Beiträge: 4
Registriert: Mittwoch 1. Juni 2016, 15:12

Hallo,

danke für die vielen super Hinweise!!!

@Sirius3: Hatte es zunächst mit mmap versucht. da kam dann allerdings immer ein Fehler (file not found odes so ähnlich). Habe es mit deinem Code ausprobiert....funktioniert! Vielen Dank! Ich denke, das ist wohl der einfachste Weg.

Viele Grüße,
Mac
Antworten