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

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 ... orial.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:

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

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

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

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

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

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

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

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

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

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

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

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

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
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

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.
BlackJack

antimicro hat geschrieben:

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)
Ich weiss nicht, ob das so funktioniert. Eigentlich sollte es, weil keine Typprüfung vorgenommen wird, aber das was Du von `buffer_info()` zurückbekommst ist ein Integer und die Funktion sollte einen Zeiger bekommen. Ich habe mal hiermit experimentiert:

Code: Alles auswählen

void reverse(char *start, unsigned int length)
{
    char tmp;
    char *end = start + (length - 1);
    
    while (start < end) {
        tmp = *start;
        *start++ = *end;
        *end-- = tmp;
    }
}
Das als Shared Library übersetzt und dann damit benutzt:

Code: Alles auswählen

import array
import ctypes

test_so = ctypes.cdll.LoadLibrary('./test.so')
reverse = test_so.reverse
reverse.argtypes = (ctypes.c_char_p, ctypes.c_uint)
reverse.restype = None

# 
# The trailing zero byte is just there to be able to print the pointer object.
# 
data = array.array('c', '123456\x00')
print '        data:', data

data_pointer = ctypes.c_char_p(data.buffer_info()[0])
print 'data_pointer:', data_pointer

length = 4
print '\nreversing first %d chars...\n' % length
reverse(data_pointer, length)
print '        data:', data
print 'data_pointer:', data_pointer
Funktioniert prima. `argtypes` und `restype` muss man nicht zwingend setzen, würde ich aber empfehlen wenn man die importierte Funktion öfter benutzt.
baesi
User
Beiträge: 5
Registriert: Mittwoch 7. November 2007, 21:29

Hallo

Ich bin noch Anfaenger mit Python und sollte in ein bestehendes Programm eine zusätzliche Funktion implementieren welche mir ziemliches Kopfzerbrechen bereitet. Ev. kann mir ja jemand weiterhelfen :?:

Ich habe eine Applikation welche mit Python3.2 via API angesteuert wird. Dies funktioniert auch ziemlich gut. Es besteht nun die Möglichkeit via einer dll eine weiter Verbindung mit der Applikation herzustellen um einen erweiterten Funktionsumfang zu erhalten.

Um die dll anzusteuern habe ich ctypes runtergeladen und installiert.
Damit kann ich die dll laden.

Für die Initialisierung der dll soll eine Funktion mit einem Übergabeparameter aufgerufen werden. und genau dort liegt das Problem:

Die Funktion sieht so aus:

Bool Call_Conv Funktion( LPDISPATCH Parameter)

Den Parameter welchen ich zu Verfügung habe ist ein Python <COMObject>. Er soll als Pointer auf ein Dispatch Objekt(wie erwähnt ComObjekt) übergeben werden.

Ich weiss nun einfach nicht wie ich mein Com Objekt in eine Dispatch Form bringen kann. Leider habe ich mit c überhaupt keine Erfahrung. :(

Ich hoffe Ihr wisst eine Lösung.

Besten Dank im voraus.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Klingt nach einer C++ Dll (ComObjekt als Parameter), mit ctypes gehen aber nur C Dlls.

Gruss
baesi
User
Beiträge: 5
Registriert: Mittwoch 7. November 2007, 21:29

hallo rayo

besten dank für deine infos. was gäbe es denn für möglichkeiten um mein problem zu lösen. ich brauche von der dll neun von unzähligen funktionen um meine applikation zu verfollständigen.

ich weiss nicht ob es möglich wäre einen c wrapper für die c++ dll zu schreiben. oder wäre ein anderer ansatz besser?

für euere hilfe danke ich im voraus
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Du kannst gleich ein in C++ geschriebenes Modul machen, das auf die DLL zugreift und sich als Python-Modul verwenden lässt. Oder du lässt dir von SIP helfen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten