2 Stimmen

Problem bei der Ausführung von Funktionen aus einer DLL-Datei mit ctypes in objektorientiertem Python

Ich hoffe sehr, dass dies keine bereits beantwortete oder eine dumme Frage sein wird. In letzter Zeit habe ich mit mehreren Geräten programmiert. Ich versuche, zwischen ihnen zu kommunizieren, um ein Testprogramm zu erstellen. Allerdings habe ich einige Probleme mit einem bestimmten Instrument, wenn ich versuche, Funktionen aufzurufen, die ich aus der DLL-Datei des Instruments "maskiert" habe. Wenn ich die interaktive Python-Shell verwende, funktioniert es perfekt (obwohl es eine Menge Wortklaubereien gibt). Aber wenn ich die Funktionen objektorientiert implementiere, schlägt das Programm fehl, nun ja, eigentlich schlägt es nicht fehl, es tut nur nichts. Dies ist die erste Methode, die aufgerufen wird: (ctypes und ctypes.util sind importiert)

    def init_hardware(self):
    """ Inits the instrument """
    self.write_log("Initialising the automatic tuner")
    version_string = create_string_buffer(80)
    self.error_string = create_string_buffer(80)
    self.name = "Maury MT982EU"
    self.write_log("Tuner DLL path: %s", find_library('MLibTuners'))
    self.maury = WinDLL('MlibTuners')
    self.maury.get_tuner_driver_version(version_string)
    if (version_string.value == ""):
        self.write_log("IMPORTANT: Error obtaining the driver version")
    else:
        self.write_log("Version number of the DLL: %s" % version_string.value)
    self.ThreeTypeLong = c_long * 3

Das funktioniert jetzt prima, alles ist perfekt und ich bekomme perfekte Log-Einträge. Aber wenn ich versuche, eine Methode weiter in das Programm genannt laufen:

def add_tuner_and_controller(self, name, serial_number, tuner_number=0):
    """ Adds the tuner to the driver object, controller is inside the tuner """
    self.write_log("Adding tuner %d and the built-in controller" % tuner_number)
    TempType = self.ThreeTypeLong()
    self.maury.add_controller(c_short(tuner_number), c_char_p(self.file_path), c_char_p(name), c_int(0), c_int(0), 
                              c_long(0), c_short(serial_number), self.error_string)
    self.maury.add_tuner(c_short(tuner_number), c_char_p(name), c_short(serial_number), c_short(0),
                            c_short(1), pointer(c_double()), TempType, pointer(c_double()), pointer(c_double()),
                            pointer(c_double()), self.error_string)

Das Programm hört plötzlich auf zu arbeiten/läuft weiter, nichts passiert, wenn die "self.maury"-Zeile aufgerufen wird. Wenn ich alles in der init_hardware-Methode platziere, funktioniert es perfekt, also vermute ich, dass es einen leichten Speicher-"Fehler" oder etwas mit der zielorientierten Struktur gibt. Ich möchte wirklich, dass es so bleibt. Gibt es eine Möglichkeit, die Funktionen auf diese Weise zu isolieren, oder muss ich mich auf einen großen Teil des Codes beschränken?


EDIT:
Informationen zur Dokumentation :
[Legende: Die Sterne stehen für Zeiger und die Klammern für Arrays.]

Die Funktion add_tuner fügt dem Tuner-Treiberobjekt einen Tuner hinzu oder aktualisiert ihn.

short add_tuner(short tuner_number, char model[ ], short serial_number, short ctlr_num, short ctlr_port, short *no_of_motors, long max_range[ ], double *fmin, double *fmax, double *fcrossover, char error_string[ ])

Sortie : no_motors, max_range (array of three numbers), fmin, fmax, fcrossover,error_string (80+ characters long), function-return->Error flag

Die Funktion add_controller fügt dem Tuner-Treiberobjekt einen Controller hinzu oder aktualisiert ihn

short add_controller(short controller_number, char driver[ ], char model[ ], int timeout, int address, long delay_ms, char error_string[ ])

Sortie : error_string, function-return->Error flag

1voto

Mark Rushakoff Punkte 236626

Ich bin mir nicht sicher, was genau Ihr Problem ist, aber hier sind ein paar allgemeine Tipps:

Für die Funktionen, die Sie außerhalb des Konstruktors aufrufen, würde ich dringend empfehlen, ihre argtypes auch im Konstruktor. Sobald Sie die Deklaration der argtypes sollten Sie nicht alle Argumente als c_short , c_double , usw. Wenn Sie außerdem versehentlich ein falsches Argument an eine C-Funktion übergeben, wird Python einen Laufzeitfehler auslösen, anstatt innerhalb der DLL abzustürzen.

Ein weiteres kleines Detail, aber Sie sollten die x = 0; byref(x) oder vielleicht POINTER(c_double)() anstelle von pointer(c_double()) im Tuner und Controller.

Ich habe in letzter Zeit auch einige ctypes-Klassen in Python 2.6 geschrieben, und ich habe keine Probleme wie das, was Sie beschreiben, gesehen. Da es anscheinend auch keine Python-Fehlerberichte dazu gibt, glaube ich fest daran, dass es nur ein winziges Detail gibt, das wir beide in Ihrer Methode übersehen, das ein Problem verursacht.

0voto

Gibt einer der Parameter in add-controller oder add-tuner tatsächlich Werte zurück?

Es wird dringend empfohlen, Prototypen für Ihre Funktionen zu definieren, anstatt sie direkt mit Casts aller Parameter aufzurufen.

Ich bin sicher, Sie haben diese Seite bereits gelesen aber der Abschnitt, den Sie sich ansehen sollten, ist Funktionsprototypen. Macht den Code viel sauberer und einfacher zu verfolgen/debuggen.

Auch - wie Mark Rushakoff erwähnt auch - mit Zeiger (c_double()) und wie in Ihrem Aufruf ist ziemlich icky. Ich habe viel mehr Glück mit POINTER() und empfehle erneut, dass Sie den Wert als Variable vordeklarieren und die Variable in Ihrem Funktionsaufruf übergeben. Dann können Sie zumindest den Wert hinterher auf seltsames Verhalten untersuchen.

EDIT: Ihr Prototyp und Ihr Aufruf werden also in etwa so aussehen:

prototype = WINFUNCTYPE(
    c_int,              # Return value (correct? Guess)
    c_short,            # tuner_number
    c_char_p,           # file_path
    c_char_p,           # name
    c_int,              # 0?
    c_int,              # 0?
    c_long,             # 0?
    c_short,            # serial_number
    c_char_p,           # error_string
)
# 1 input, 2 output, 4 input default to zero (I think; check doc page)
paramflags = (1, 'TunerNumber' ), (1, 'FilePath' ), (1, 'Name' ), ...
AddController = prototype(('add_controller', WinDLL.MlibTuners), paramflags)

Dann ist Ihr Anruf viel sauberer:

arg1 = 0
arg2 = 0
arg3 = 0
AddController(tuner_number, self.file_path, name, arg1, arg2, arg3, 
    serial_number, self.error_string)

0voto

Ich fand heraus, dass die einzige Möglichkeit, die Funktionen in der exportierten DLL aufzurufen, darin bestand, die DLL über einen Parameter in jeder Methode zu verwenden. (das Programm exportiert die DLL und in jeder aufgerufenen Methode wird sie als Parameter übergeben). Das sieht ziemlich hässlich aus, aber das ist der einzige Weg, den ich gefunden habe, der bei mir funktioniert. Ich habe sogar versucht, die DLL als Klassenattribut zu exportieren. Das System, mit dem ich arbeite, ist ziemlich umfangreich, so dass ich vermute, dass es irgendwo einen Boboo-Code gibt, der es scheitern lässt. Vielen Dank für alle Rückmeldungen und Tipps!

/Mazdak

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X