Argument Übergabe mit ctypes

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
dj4ee

Argument Übergabe mit ctypes

Beitragvon dj4ee » Freitag 8. Juli 2005, 11:18

Moin @ all,


ich habe da mal ein kleines Problem mit ctypes.
Und zwar möchte ich aus einer vorhandenen C-DLL eine Funktion mittels ctypes aufrufen, die aber einen Zeiger als Argument verlangt (also ungefähr so:)

Code: Alles auswählen

 int  GetNumber_i(unsigned long* AnzahlGefundeneGeräte)


Jetzt habe ich mich also erstmal aufs ctypes Tutorial (http://starship.python.net/crew/theller/ctypes/tutorial.html) gestürzt und die Funktion "byref()" gefunden.

Ich habe also einmal folgendes probiert:


Code: Alles auswählen

libc = cdll.LoadLibrary("d:\martin\STIF.dll")
number = c_ulong()
libc.GetNumber_i(byref(number))


Bekomme aber immer die Meldung "Access Violation"....

Sinn des ganzen soll sein, daß nach dem Aufruf der Funktion in der variablen "number" die Anzahl der angeschlossenen Geräte drinstehen sollen.

Kann mir da jemand weiterhelfen ?

Vielen Dank schon mal im Vorraus,

Martin
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Beitragvon ProgChild » Freitag 8. Juli 2005, 11:24

Ich kenne mich nicht mit den Win32 erweiterungen von Python aus, aber zu der Zeit, in der ich noch die WinAPI programmiert habe, da musst man, bevor man eine Funktion benutzen konnt, erst die DLL laden und dann auch noch die Addresse der Funktion. Das tust du unten nicht.
dj4ee

Beitragvon dj4ee » Freitag 8. Juli 2005, 11:58

Hmm, ich habe aber schon einige andere Funktionen auf diesem Wege erfolgreich nutzen können.

Allerdings habe die als Argument lediglich einen integer, einen String oder gar nichts übergeben bekommen... Aber die haben auf diesem Wege funktioniert...

Gruß,
martin
dj4ee

Beitragvon dj4ee » Freitag 8. Juli 2005, 13:43

Hallo , nochmal....

Bin selbst auf die Lösung gekommen (Lesen bildet, vor allem dann, wenn man etwas tiefer ins Tutorial schaut :wink: )..

Code: Alles auswählen

libc = cdll.LoadLibrary("d:\martin\STIF.dll")
i = c_ulong()
print libc.GetNumber_i(pointer(i))
print i.value


und schon funzts....

Gruß,
MK
dj4ee

Beitragvon dj4ee » Montag 18. Juli 2005, 13:13

Moin nochmals @ all,


habe aber genau zum Thema passend noch ne weitere Frage:

Ich habe eine C - Funktion, die von mir einen Pointer auf ein Array als Argument verlangt (also ungefähr so:)

Code: Alles auswählen

int FOO(unsigned short* array_pus)


Die Funktion greift sich jetzt diesen Zeiger und fängt an, lustig Werte ins Array zu schreiben, indem sie den Pointer immer um einen unsigned long im Speicher verschiebt.
Jetzt scheint es aber so zu sein, daß Python ein Array anders anlegt (also nicht alle Werte brav hintereinander), so daß bei meinen kläglichen Versuchen mittels

Code: Alles auswählen

a =  ArrayOfUnsignedInt(128) # Eigene Funnktion, die mir ein Array mit      #unsigned shorts anlegt
lib.FOO(array[0])

nur Müll herauskommt...

Kann mmir da jemand weiterhelfen ?

Vielen Dank schon einmal im Vorraus.

Gruß,

MK
dj4ee

Beitragvon dj4ee » Montag 18. Juli 2005, 16:54

Ohhgott.. heute ist einfach viel zu heiß!

Also, bevor hier noch böse Antworten eintreffen, daß ich sämtliche Datentypen durcheinanderwerfe:

Meine C-DLL verlangt als Argument einen Zeiger auf ein Array aus unsigned shorts:

Code: Alles auswählen

 int foo(unsigned short* array_pus)


Mein erster Versuch war eine Funktion, die mir ein Array aus unsigned Shorts anlegt:

Code: Alles auswählen

def ArrayOfUnsignedShorts(number):
      a = range(number)
      for i in range(number):
           a[i] = c_ushort()
      return a


Die Funktion habe ich dann verwendet, um einen kläglichen Versuch mittels:

Code: Alles auswählen

from ctypes import *
lib = cdll.LoadLibrary("d:\meine.dll")
array = ArrayOfUnsignedShorts(10)
print lib.FOO(pointer(array[0])

zu starten.
Erstaunlicherweise funktioniert das halt insoweit, als daß mir die C Funktion den ersten Wert brav ins erste Feld einträgt, nur dann.... weiß der Geier, schreibt er die Daten sonst wo hin.

Meine Frage also: Gibt es entweder eine Möglichkeit in Python., einen Zeiger auf ein ganzes Array zu übergeben, oder weiß jemand eine andere Möglichkeit, um das Problem zu lösen ?

Vielen Dank schon mal,

Gruß,
MK

Edit (Leonidas): Code in Python-Tags gesetzt.
BlackJack

Beitragvon BlackJack » Montag 18. Juli 2005, 21:47

dj4ee hat geschrieben:Mein erster Versuch war eine Funktion, die mir ein Array aus unsigned Shorts anlegt:

Code: Alles auswählen

def ArrayOfUnsignedShorts(number):
      a = range(number)
      for i in range(number):
           a[i] = c_ushort()
      return a


Du legst hier kein Array an, sondern eine Python-Liste. Das ist ein ziemlich komplexes Objekt und nicht einfach nur ein Speicherbereich in dem die Zahlen direkt abgelegt werden, wie das in C der Fall ist und was der Aufruf der C Funktion erwartet.

Schau Dir mal das `array` Modul an. Die Adresse von den Daten bekommst Du mit der Methode `array.buffer_info()`.
dj4ee

Beitragvon dj4ee » Dienstag 19. Juli 2005, 10:30

joouu! Das wars !

Schon wieder was gelernt, Tausend Dank !
Ich bin echt begeistert, wie leistungsfähig Python ist und dabei echt nicht schwer zu erlernen!

Gut, hier zur Vollständigkeit mal der vollständige Code, so wie's bei mir hinterher funkationuckelt hat:

Code: Alles auswählen

from ctypes import *
from array import array

# Funktion ArrayOfUnsignedShort
# Erstellt ein Array mit unsigned shorts

def ArrayOfUnsignedShort(len):
   for i in range(len):
      a = array('H').append(0)
   return a

libc = cdll.LoadLibrary("d:\meine.dll")
Feld = ArrayOfUnsignedShort(20)
print libc.Foo(Feld.buffer_info()[0])
print Feld.tolist()


Aufpassen muss man nur, daß das Array groß genug vordimensioniert wird,
sonst funktionierts nicht (zumindest nicht bei mir).

Gruß,
MK

Edit (Leonidas): Code in Python-Tags gesetzt.
BlackJack

Beitragvon BlackJack » Dienstag 19. Juli 2005, 21:32

dj4ee hat geschrieben:

Code: Alles auswählen

# Funktion ArrayOfUnsignedShort
# Erstellt ein Array mit unsigned shorts

def ArrayOfUnsignedShort(len):
   for i in range(len):
      a = array('H').append(0)
   return a


Aus dem Kommentar vor der Funktion kann man einen Docstring machen, das heisst den Kommentar einfach als mehrzeilige Zeichenkette als erstes in die Funktion schreiben. Dann kommt man an die Information auch mit `help()` im Interpreter oder mit den üblichen Dokumentationswerkzeugen heran.

Dann sollte man keine Namen von eingebauten Funktionen wiederverwenden. Am Syntaxhighlighting erkennt man, das `len()` eine eingebaute Funktion ist.

Und zu guter letzt enthält die Funktion einen gravierenden Fehler: Du erzeugst in der Schleife dauernd neue Arrays mit der Länge 1 und gibst davon das letzte zurück. Wenn der C Code da mehr als eine Zeil reinschreibt, dann ist das pures Glück, wenn das Programm funktioniert.

So sollte es korrekt funktionieren:

Code: Alles auswählen

def ArrayOfUnsignedShort(length):
    """Erstellt ein Array mit unsigned shorts."""
    return array('H', [0] * length)
dj4ee

Beitragvon dj4ee » Mittwoch 20. Juli 2005, 08:23

Aus dem Kommentar vor der Funktion kann man einen Docstring machen, das heisst den Kommentar einfach als mehrzeilige Zeichenkette als erstes in die Funktion schreiben. Dann kommt man an die Information auch mit `help()` im Interpreter oder mit den üblichen Dokumentationswerkzeugen heran.


Ahh, vielen Dank für den Tip! Das ist ja mal echt nützlich.
Zum Beispiel, wenn man die Rückgabeparameter dokumentieren will, zum Beispiel :-)

Und zu guter letzt enthält die Funktion einen gravierenden Fehler: Du erzeugst in der Schleife dauernd neue Arrays mit der Länge 1 und gibst davon das letzte zurück. Wenn der C Code da mehr als eine Zeil reinschreibt, dann ist das pures Glück, wenn das Programm funktioniert.


Jou, da hast Du wohl völlig recht, ist mir hinterher auch aufgefallen :-/
Eigentlich wollte ich das auch so machen:

Code: Alles auswählen

def ArrayOfUnsignedShort(laenge):
      a = array('H')
      for i in range(laenge):
            a.append(0)
      return a

aber Deine Lösung ist ja mal wesentlich eleganter!

Danke nochmal,

Gruß,
MK
antimicro
User
Beiträge: 151
Registriert: Sonntag 29. Februar 2004, 16:24

Beitragvon antimicro » Donnerstag 25. August 2005, 13:59

Kann die Funktion aus der CLib die Variable über den Zeiger verändern? Ich habe auch mal ein Array mit dem Code von dj4ee und BlackJack gebastelt.
Die Funktion nimmt den Zeiger von dem Array auch ohne Probleme an. Nur leider ändert sich nichts in dem Array :?:

Ganz blöd gefragt:
Sind Python Variablen wie in C nur Orte im Speicher?

Und warum werden Rückgaben in C nicht auch über return erledigt?
greetings
sebi
BlackJack

Beitragvon BlackJack » Donnerstag 25. August 2005, 23:08

antimicro hat geschrieben:Kann die Funktion aus der CLib die Variable über den Zeiger verändern? Ich habe auch mal ein Array mit dem Code von dj4ee und BlackJack gebastelt.
Die Funktion nimmt den Zeiger von dem Array auch ohne Probleme an. Nur leider ändert sich nichts in dem Array :?:


Eigentlich sollte das funktionieren.

Ganz blöd gefragt:
Sind Python Variablen wie in C nur Orte im Speicher?


Eher nicht. In Python gibt's keine "Variablen" im C-Sinn, sondern Namen und Objekte. So ein Name bezeichnet nicht wie in C einen festen Ort im Speicher, sondern immer ein Objekt. Das liegt zwar auch irgendwo im Speicher, aber wenn man dem Namen ein anderes Objekt zuweist, dann liegt das an einem anderen Speicherort. Aus C-Sicht kann man sagen, das die Namen in Python alle als Pointer auf Objekte realisiert sind.

Die Brücke zu C kann man mit `Array` schlagen, weil man sich von dem Array-Objekt die Speicheradresse des Datenteils geben lassen kann. Damit sollte eine C-Funktion auch diese Daten verändern können.

Und warum werden Rückgaben in C nicht auch über return erledigt?


Können sie auch. Allerdings gibt's keine Tupel bei Rückgaben. Wenn man also einen Fehlercode und noch etwas anderes zurückgeben will, dann kann man eines davon per ``return`` zurückgeben und das andere per Zeiger den man beim Aufruf übergibt und der auf einen entsprechenden Speicherbereich zeigt.

Ausserdem möchte man ja nicht immer etwas zurückgeben, sondern oft auch eine Datenstruktur, die (als Zeiger) übergeben wird "in-place" verändern.
antimicro
User
Beiträge: 151
Registriert: Sonntag 29. Februar 2004, 16:24

Beitragvon antimicro » Freitag 26. August 2005, 07:47

BlackJack hat geschrieben:
antimicro hat geschrieben:Kann die Funktion aus der CLib die Variable über den Zeiger verändern? Ich habe auch mal ein Array mit dem Code von dj4ee und BlackJack gebastelt.
Die Funktion nimmt den Zeiger von dem Array auch ohne Probleme an. Nur leider ändert sich nichts in dem Array :?:


Eigentlich sollte das funktionieren.


Ich zeig am besten mal was ich angestellt habe:

Der Code von BlackJack (nur leicht angepasst):

Code: Alles auswählen

def Array(length,typecode='L',item=[0]):
     """Erstellt ein Array dem typecode entsprechend."""
     return array.array(typecode, item * length)

DriverIDArray = Array(10)


Damit bekomme ich mein Array das ich dann der Funktion übergebe:

Code: Alles auswählen

DriverNum = yasdi.yasdiGetDriver(DriverIDArray.buffer_info()[0],maxDriverIDs)


Das Array bleibt allerdings leer. Die Rückgabe DriverNum gibt allerdings an das die Funktion mindestens einen Eintrag in DriverIDArray hinzugefügt haben sollte :(

Für jeden Tip bin ich sehr dankbar.
greetings

sebi
antimicro
User
Beiträge: 151
Registriert: Sonntag 29. Februar 2004, 16:24

Beitragvon antimicro » Freitag 26. August 2005, 09:57

Ich hab's. Das Array hab ich mit 0 Vormaskiert. Die Funktion hat für jeden Driver eine 0 in das Array hinzugefügt.

Deswegen war der neue Eintrag 'unsichtbar'. :roll:
greetings

sebi
Benutzeravatar
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Beitragvon Joghurt » Freitag 26. August 2005, 14:28

IdR musst du übrigens die Backslashes escapen, damit sie nicht als Escapesequenz betrachtet werden, oder raw-Strings verwenden:

Code: Alles auswählen

"C:\\meine.dll" #oder
r"C:\meine.dll"
Sonst bekommst du Probleme, sobald irgendein Name mit "n", "r" oder "t" beginnt, da "C:\nein.dll" als "C:<Newline>ein.dll" geparst wird.

Wer ist online?

Mitglieder in diesem Forum: cedsoft