Seite 1 von 1

Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 15:46
von MacSt0815
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

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 16:55
von snafu
Was spricht gegen das naheliegende ctypes.pointer() in Verbindung mit ctypes.cast()?

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 18:05
von MacSt0815
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 :-(

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 19:45
von MacSt0815
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?

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 20:52
von __deets__
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.

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 21:27
von 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

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 21:31
von __deets__
@BlackJack: ohne c_void_p als Returnwert wird das mit 64 Bit krachen denke ich, weil ctypes dan int annimmt.

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 21:41
von 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.

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Mittwoch 1. Juni 2016, 22:18
von __deets__
@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.

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Donnerstag 2. Juni 2016, 06:22
von Sirius3
@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.

Re: Mittels ctypes und Structure aus Shared Memory Bereich lesen

Verfasst: Freitag 3. Juni 2016, 10:48
von MacSt0815
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