Anbindung DLL an Python

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
BlackJack

@thomasgull: Den Satz mit „byval” (was ist das?) habe ich nicht verstanden. Ansonsten nimm die Meldung einfach mal so wie sie da steht: irgendwie bekommst Du da einen Zeiger rein der ungültig ist.

Ansonsten sind Beschreibungen wie „hat er beim Ausdruck x.value gemeckert” oder „meldet es invalid String pointer 0xirgendwas” sehr ungenau. Wenn es Ausnahmen gibt, dann wäre der komplette Traceback und der dazugehörige Quelltext in der Regel hilfreicher für helfende.

Kann es sein, dass die Funktion irgendwie auf eine andere Weise anzeigt, dass ein Fehler aufgetreten ist und man deshalb den Zeiger gar nicht erst benutzen darf? Wird da überhaupt ein Zeiger auf einen Zeiger auf ``char`` erwartet? Also ist die C-Signatur an der Stelle wirklich ``char **argument``?
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Ok folgender Code:

Code: Alles auswählen

#mlfb lesen

    mlfbnr=ctypes.c_char_p("xxxxxxxxxxxxxxxxxxxx")
    mlfbnrref=byref(mlfbnr)
    timeout=2000
    egal=0
    print mlfbnr.value

    mlfb= aglink.AGL_ReadMLFBNr(connectNrRet,mlfbnrref,timeout,egal)
    if mlfb ==0:
        print "MLFB erfolgreich"
        x=mlfbnr.value
        print x
        
    else:
        
        error="                                                                                   "                                                   
        errtxt= aglink.AGL_GetErrorMsg(mlfb,error,256)
        print error
Antwort:

Code: Alles auswählen

xxxxxxxxxxxxxxxxxxxx
MLFB erfolgreich

Traceback (most recent call last):
  File "C:\Python26\DLLs\aglink3.py", line 112, in <module>
    x=mlfbnr.value
ValueError: invalid string pointer 0x37534536
>>> 
Danke
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@thomasgull: wenn ich das richtig sehe, erwartet AGL_ReadMLFBNr Zahlen! Was erwartest Du, wenn Du statt dessen einen Pointer auf einen Pointer auf einen String übergibst?
BlackJack

@thomasgull: Ein Pointer dessen Bytewerte alle im ASCII-Bereich liegen und bei etwas das eine ”Nummer” ermittel zwei ASCII-Ziffern enthält ist zumindest verdächtig. Ich bleibe bei meiner Vermutung das hier kein Zeiger auf einen Zeiger auf ``char`` erwartet wird sondern nur ein Zeiger auf ``char``. Und Du möchtest da vielleicht auch besser keinen `c_char_p()`-Wert übergeben der auf eine eigentlich unveränderbare Python-Zeichenkette zeigt. Da können böse Sachen passieren. `c_buffer()` oder `create_string_buffer()` verwendet man dafür.

@Sirius: Da wird ein Zeiger auf ein Byte-Array der Länge 21 erwartet wo ein Nullterminierter String reingeschrieben wird, laut API-Dokumentation. → http://www.sumelco.com/wp-content/uploa ... k_4_HB.pdf (Seite 156):

Code: Alles auswählen

int WINAPI AGL_ReadMLFBNr( int ConnNr, LPMLFB MLFBNr, int Timeout, long UserVal );

typedef struct tagMLFB
{
  BYTE MLFB[21]; // MLFB number as a zero-terminated string
} MLFB, *LPMLFB; 
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Richtig die DLL erwartet eine String mit 21 Zeichen.
BlackJack

@thomasgull: Naja korrekter eher einen Speicherbereich in den man bis zu 21 Bytes ablegen kann. Ob da vor dem Aufruf Daten drinstehen die als C-String durchgehen und bei `strlen()` tatsächlich 20(!) als Ergebnis liefern ist egal. 21 Zeichen kann die Zeichenkette nicht haben, denn dann müsste der Speicherbereich 22 Bytes gross sein.
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Danke mit c_Buffer funktioniert es bestens

Danke
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Nochmals besten Dank
Langsam habe ich die C-Funktionen begriffen
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Nochmals eine Frage

wie würdet ihr das mit Strukturen lösen?

Bei dieser Funktion werden ganze Strukturen übergeben und danach wieder Ausgelesen.

Beschreibung:
Erweiterter Leseauftrag für Antriebe (AGL_Drive_ReadMixEx)
Mit dieser Funktion können Sie die Werte verschiedener Datentypen und Datengrößen in einem Auftrag lesen. Gegenüber der Funktion AGL_Drive_ReadMix darf hier die Strukturvariable DATA_RW40_DRIVE.OpAnz auch größer 1 sein. Dies bedeutet, dass mit einem Strukturelement mehrere aufeinanderfolgende Operandenwerte gelesen werden können.

C/C++-Syntax:
int WINAPI AGL_Drive_ReadMixEx( int ConnNr, LPDATA_RW40_DRIVE Buff, int Num, int Timeout, LONG_PTR UserVal );

Parameter:
ConnNr Verbindungshandle
Buff Zeiger auf Lesestrukturen
Num Anzahl der Strukturen
Timeout Der zu verwendende Timeoutwert
UserVal Wert zur freien Verwendung
Rückgabewert:
>= 0 Auftragsnummer bei asynchronem Aufruf und synchronem Aufruf mit AGL_ReturnJobNr( 1 ) oder AGL40_SUCCESS bei synchronem Aufruf ohne AGL_ReturnJobNr( 1 )
< 0 Eine Fehlernummer (s. AGL_Defines.H, AGL_Defines.BAS oder AGL_Defines.PAS)


Code in C++

Code: Alles auswählen

int doDrive_ReadMixEx(int connnr, int timeout, int userval)
{
    int num = 3;
    LPDATA_RW40 rwfield = (LPDATA_RW40)calloc(num, sizeof(DATA_RW40));
    rwfield[0].Dev = 1;
    rwfield[0].ParaInd = 0;
    rwfield[0].ParaNum = 0;
    rwfield[0].OpAnz = 1;
    rwfield[0].OpType = DTYP_UNKNOWN;
    rwfield[0].Result = 0;
    rwfield[0].Buff = (float*)calloc(rwfield[0].OpAnz, sizeof(float));
    ((float*)rwfield[0].Buff)[0] = 0;
    
    rwfield[1].Dev = 0;
    rwfield[1].ParaInd = 1;
    rwfield[1].ParaNum = 0;
    rwfield[1].OpAnz = 5;
    rwfield[1].OpType = DTYP_UNKNOWN;
    rwfield[1].Result = 0;
    rwfield[1].Buff = (DWORD*)calloc(rwfield[1].OpAnz, sizeof(DWORD));
    ((DWORD*)rwfield[1].Buff)[0] = 1;
    ((DWORD*)rwfield[1].Buff)[1] = 0;
    ((DWORD*)rwfield[1].Buff)[2] = 0;
    ((DWORD*)rwfield[1].Buff)[3] = 0;
    ((DWORD*)rwfield[1].Buff)[4] = 0;
    
    rwfield[2].Dev = 0;
    rwfield[2].ParaInd = 1;
    rwfield[2].ParaNum = 0;
    rwfield[2].OpAnz = 1;
    rwfield[2].OpType = DTYP_UNKNOWN;
    rwfield[2].Result = 0;
    rwfield[2].Buff = (byte*)calloc(rwfield[2].OpAnz, sizeof(byte));
    ((byte*)rwfield[2].Buff)[0] = 1;
    
    // For a successful call, a connection must be built up to the AG.
    int result = AGL_DriveReadMixEx(connnr, rwfield, num, timeout, userval);
    
    if (result != AGL40_SUCCESS)
    {
        // Error happened.
        char errormsg[256];
        AGL_GetErrorMsg(result, errormsg, 256);
    }
    
    // clean up the memory
    free(rwfield[0].Buff);
    free(rwfield[1].Buff);
    free(rwfield[2].Buff);
    
    return result;
}




Viele Grüsse

Thomas
BlackJack

@thomasgull: Was ist denn dabei die konkrete Frage? Wie man Proxy-Objekte für Strukturtypen mit `ctypes` definiert steht in der Python-Dokumentation im `ctypes`-Tutorial.

Und wie man so eine komplette Funktion konkret übersetzt hängt auch ein bisschen davon ab wie man die Python API zu den Funktionen gestaltet. Das wird man ja hoffentlich nicht 1:1 machen sondern eine „pythonische” API entwerfen. Sonst stellt sich nämlich die Frage warum man das überhaupt in Python macht wenn der Code am Ende aussieht wie C-Code in Python-Syntax verkleidet.

In dem gezeigten Quelltext wird der Speicher von `rwfield` nicht wieder freigegeben. Das sieht nach einem Speicherleck aus.
Antworten