- LiFePO4 Speicher Test         
Ergebnis 1 bis 10 von 10

Thema: Sehr komisches Verhalten bei void*...

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2008
    Ort
    Korntal-Münchingen
    Alter
    42
    Beiträge
    15

    Sehr komisches Verhalten bei Call bei Reference und bei void*...

    Anzeige

    LiFePo4 Akku selber bauen - Video
    Guten Morgen zusammen,

    erstmal sorry für die blöde Überschrift, mir ist keine vernünftige eingefallen...

    Zum Problem:
    Ich schreibe gerade an einer DLL, die die Verbindung zwischen einer Diagnosesoftware und dem Diagnoseinterface herstellt. Diese DLL implementiert den J2534 Standard, stellt also eine API zur Verfügung.

    Es geht um diese Funktion hier:

    Code:
        QUOKAJ2534_API long PassThruIoctl(
            unsigned long ChannelID
            , unsigned long IoctlID
            , void* pInput
            , void* pOutput
        )
        {
            LOG(PROTOCOL, "PassThruIoctl called with IoctlID %d", IoctlID);
    
            switch (IoctlID)
            {
                case GET_CONFIG:
                    LOG(PROTOCOL, "Get config");
                    LOG_SBYTE(PROTOCOL, (SBYTE_ARRAY*)pInput);
                    break;
                case SET_CONFIG:
                    break;
                case READ_VBATT:
                    LOG(PROTOCOL, "Reading battery voltage");
                    *(unsigned long*)pOutput = 143 * 100;
    
                    break;
                case FIVE_BAUD_INIT:
                    {
                        LOG(PROTOCOL, "Five baud init");
                        SBYTE_ARRAY* inputMsg = (SBYTE_ARRAY*)pInput;
    
                        if (inputMsg->NumOfBytes > 1)
                        {
                            LOG(PROTOCOL, "Wake up message length is greater than 1 Byte");
                            return ERR_INVALID_MSG;
                        }
                        
                        LOG(PROTOCOL, "Wake up for ECU with id 0x%02x called", inputMsg->BytePtr[0]);
    
                        unsigned char KeyWords[2]{};
                        SBYTE_ARRAY outputMsg{};
                    
                        LOG_SBYTE(PROTOCOL, inputMsg);
    
                        KeyWords[0] = 0x00;
                        KeyWords[1] = 0x81;
    
                        outputMsg.NumOfBytes = 2;
                        outputMsg.BytePtr = &KeyWords[0];
                    
                        LOG_SBYTE(PROTOCOL, &outputMsg);
                    }
                    break;
                case FAST_INIT:
                    break;
                case CLEAR_TX_BUFFER:
                    break;
                case CLEAR_RX_BUFFER:
                    break;
                case CLEAR_PERIODIC_MSGS:
                    break;
                case CLEAR_MSG_FILTERS:
                    break;
                case CLEAR_FUNCT_MSG_LOOKUP_TABLE:
                    break;
                case ADD_TO_FUNCT_MSG_LOOKUP_TABLE:
                    break;
                case DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE:
                    break;
                case READ_PROG_VOLTAGE:
                    LOG(PROTOCOL, "Read prog voltage");
                    *(unsigned long*)pOutput = 10 * 1000;
                    break;
                // J2534-2 SW_CAN
                case SW_CAN_HS:
                    break;
                case SW_CAN_NS:
                    break;
                case SET_POLL_RESPONSE:
                    break;
                case BECOME_MASTER:
                    break;
                default:
                    break;
            }
            return STATUS_NOERROR;
        }
    und speziell um den case FIVE_BAUD_INIT.

    Beim Aufruf der Funktion PassThruIoctl(...) gibt der Aufrufer (also die Diagnosesoftware) eine IoctlID (in diesem Fall 0x04, was FIVE_BAUD_INIT) und ein pInput, das ein SBYTE_ARRAY ist.

    SBYTE_ARRAY sieht wie folgt aus:

    Code:
    typedef struct _SBYTE_ARRAY
    {
        unsigned long NumOfBytes;        // Number of bytes in the array
        unsigned char *BytePtr;            // Array of bytes
    } SBYTE_ARRAY;
    Der Aufrufer erwartet als Antwort ebenfalls ein SBYTE_ARRAY, aber mit zwei Bytes drin. Diese zwei Bytes schreibe ich ins KeyWords rein hänge es an den BytePtr vom Array. So weit so gut, das scheint auch zu funktionieren, wenn im Log steht dann folgendes:

    Zum pInput:

    pInput array
    Wake up for ECU with id 0x33 called
    pInput array end
    Absolut richtig, der Aufrufer hat eine 0x33 runtergegeben.

    Und zum pOutput:

    pOutput array
    Byte: 0, value: 0x00
    Byte: 1, value: 0x81
    pOutput array end
    auch richtig, ich gebe ein 0x00 und ein 0x81 rein. Passt.

    Das Problem:

    Beim Aufrufer kommt komplett was anderes an... mal kommt ein 0x00 und 0x00 an oder was anderes. Wenn ich es mehrmals probiere, bekomme ich unterschiedliche Werte, also völlig random...

    Im Anhang ist das Bild vom Tool, mit dem ich das Ganze teste

    ECU Address ist diese besagt 0x33, Keyword 1 und Keyword 2 kommen aus meiner DLL.

    Das Testool kommt von einem renomierten Diagnosesoftwarehersteller, die Wahrscheinlichkeit, dass da ein Fehler ist, ist doch sehr geringt... klar, möglich aber geringt.
    Andere implementierte CASEs funktionieren fehlerfrei...

    Hat jemand eine Idee?

    Vielen Dank im Voraus und sorry für den langen Text
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken TestTool.jpg  
    Geändert von Hemi (06.07.2023 um 09:51 Uhr)

  2. #2
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    20.08.2004
    Ort
    Unterschleissheim
    Beiträge
    325
    Sorry, trotz langer Erklärung??? Also die Vaiablen, die mit dem void* übergeben werden, kommen nicht richtig an?
    Und wenn ja, wieso soll das ein void* sein. Pointer sind immer ein bisschen problematisch, wenn dafür kein Speicher definiert wurde. Und wieso void? Der kein Format, ist also auch ein Problem. Und wie sieht der Code aus, der diese Funkton aufruft?

    Gruß

  3. #3
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2008
    Ort
    Korntal-Münchingen
    Alter
    42
    Beiträge
    15
    Zitat Zitat von gunzelg Beitrag anzeigen
    Sorry, trotz langer Erklärung??? Also die Vaiablen, die mit dem void* übergeben werden, kommen nicht richtig an?
    Richtig. Das Komische ist auch noch, das es immer unterschiedliche Werte beim Aufrufer ankommen, völlig Banane.

    Und was auch noch komisch ist, dass zum Beispiel "case READ_VBATT" und auch "case READ_PROG_VOLTAGE" korrekt beim Aufrufer ankommen, also wenn unsigned long auf void gecastet wird, kein Problem.

    Beim Array zickt er...

    Zitat Zitat von gunzelg Beitrag anzeigen
    Und wenn ja, wieso soll das ein void* sein. Pointer sind immer ein bisschen problematisch, wenn dafür kein Speicher definiert wurde. Und wieso void? Der kein Format, ist also auch ein Problem.
    Weil es so im J2534 Standard definiert ist... Also, ist aus der Kategorie "ist so und kann man nicht ändern".

    Zitat Zitat von gunzelg Beitrag anzeigen
    Und wie sieht der Code aus, der diese Funkton aufruft?
    Keine Ahnung, da closed source... Da ist ein C# Wrapper, der die DLL lädt und die Exports reinzieht. So in Etwa...

  4. #4
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    868
    Wenn ich davon ausgehe, dass die Struktur in pOutput zurückgegeben werden soll:
    - Da finde ich die Zuweisung nicht.
    - Und es ist auch (zumindest in C, soweit ich weiß) keine gute Idee, ein Array in der Unterfunktion zu definieren. Das Array wird auf dem Stack angelegt und ist damit spätestens nach dem nächsten Aufruf einer weiteren Unterfunktion potentiell wech.

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2008
    Ort
    Korntal-Münchingen
    Alter
    42
    Beiträge
    15
    Zitat Zitat von Holomino Beitrag anzeigen
    - Da finde ich die Zuweisung nicht.
    Code:
    KeyWords[0] = 0x00;
    KeyWords[1] = 0x81;
    
    outputMsg.NumOfBytes = 2;
    outputMsg.BytePtr = &KeyWords[0];
    Oder was vermisst Du genau?

    Zitat Zitat von Holomino Beitrag anzeigen
    Und es ist auch (zumindest in C, soweit ich weiß) keine gute Idee, ein Array in der Unterfunktion zu definieren. Das Array wird auf dem Stack angelegt und ist damit spätestens nach dem nächsten Aufruf einer weiteren Unterfunktion potentiell wech.
    Und das ist genau das gewünschte Verhalten...

    Dadurch, dass diese Funktion mit unterschiedlichen Parameter aufgerufen, bei den pInput und pOutput (mal mit NULL, mal mit unsigned long, mal mit Arrays unterschiedlicher Art), sind die beiden als void* definiert.

    Der Aufrufer gibt die Referenz rein und alles ist gut. In der Spec sind die Parameter auch definiert, das hängt davon ab, was als IoctlID reingegeben wird. Die Daten in dieser Funktion werden nicht "persistiert", sondern (im Normalfall) von woanders geholt (aus der Diagnosehardare) und nur durchgereicht.

  6. #6
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    20.08.2004
    Ort
    Unterschleissheim
    Beiträge
    325
    Folgendes steh zwar nicht zur debatte, ist für mich aber auch suspekt:
    case READ_VBATT:
    LOG(PROTOCOL, "Reading battery voltage");
    *(unsigned long*)pOutput = 143 * 100;

    Aber um da weiter zu kommen, würde ich das ganze mal in sofern simulieren, dass ich ein Programm schreibe, das deine Funktion aufruft.
    Dann kannst du das ganze leicht debuggen unds schaun was ankommt und gegebenfalls in der Funktion den Code anpassen.

    Gruß

  7. #7
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    07.04.2015
    Beiträge
    868
    Zitat Zitat von Hemi Beitrag anzeigen
    Oder was vermisst Du genau?
    Die Zuweisung der Adresse von outputMsg auf den pOutput-Parameter?
    Such mal im Zweig "case FIVE_BAUD_INIT:" nach "pOutput". Der kommt da gar nicht vor.

    Wenn das so gewollt ist, hab ich wohl nicht begriffen, was Du da vor hast - dann tschuldige, dass ich lebe.

  8. #8
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2008
    Ort
    Korntal-Münchingen
    Alter
    42
    Beiträge
    15
    Zitat Zitat von gunzelg Beitrag anzeigen
    Folgendes steh zwar nicht zur debatte, ist für mich aber auch suspekt:
    case READ_VBATT:
    LOG(PROTOCOL, "Reading battery voltage");
    *(unsigned long*)pOutput = 143 * 100;
    Warum suspekt?

    Wir haben eine Funktion, die wie folgt Deklariert ist:

    Code:
    extern "C" QUOKAJ2534_API long PassThruIoctl(
        unsigned long ChannelID
        , unsigned long IoctlID
        , void* pInput
        , void* pOutput
    );
    Der erste Parameter ist für uns uninteressant.

    unsigned long IoctlID zeigt, was genau gemacht werden soll, ob die Batteriespannung gelesen werden soll oder eben die 5-Baud-Initialisierung.

    Die beiden void* Parameter werden je nach IoctlID bedatet...

    Die J2534 Spec sagt zum Beispiel aus:

    Wenn READ_VBATT (IoctlID = 0x03) aufgerufen wird, dann:
    -> pInput ist NULL, weil dieser Parameter nicht verwendet wird
    -> pOutput ist ein Pointer auf ein unsigned long, der eine Spannung in Millivolt hält.

    Dieses void* Konstrukt ist an und für sich ganz cool, weil man eben alles Mögliche reinstopfen kann, man muss nur irgendwie sagen, was da drin steckt und das passiert hier über die Variable IoctlID.

    Alternative wäre für jeden Satz an Variablentypen eine eigene Funktion zu schreiben, macht das Ganze nicht gerade übersichtlicher.

    Der Aufrufer, der die Funktion PassThruIoctl aufruft, weißt ja, was er zurückerwartet und übergibt an die beiden void* Parameter die Referenzen auf die Variablen, die er zuvor (vor dem Aufruf) angelegt und initialisiert hat. Das ist ein klassisches "call by referenz" Verfahren, da wird eben nicht der Wert einer Variable (wie bei "call by value") übergeben, sondern die Referenz.

    Beantwortet es Deine Frage?

    Zitat Zitat von gunzelg Beitrag anzeigen
    Aber um da weiter zu kommen, würde ich das ganze mal in sofern simulieren, dass ich ein Programm schreibe, das deine Funktion aufruft.
    Dann kannst du das ganze leicht debuggen unds schaun was ankommt und gegebenfalls in der Funktion den Code anpassen.
    Ja, damit habe ich schon angefangen, dann sehe ich, was da passiert. Eine sehr gute Idee.

    Zitat Zitat von Holomino Beitrag anzeigen
    Die Zuweisung der Adresse von outputMsg auf den pOutput-Parameter?
    Such mal im Zweig "case FIVE_BAUD_INIT:" nach "pOutput". Der kommt da gar nicht vor.
    Du hast Recht... irgendwie habe ich es verrafft...

    Da hat in der Tat das hier gefehlt:

    Code:
    *(SBYTE_ARRAY*)pOutput = outputMsg;
    Nun sieht der Case so aus:

    Code:
    case FIVE_BAUD_INIT:
                    {
                        LOG(PROTOCOL, "Five baud init");
                        SBYTE_ARRAY* inputMsg = (SBYTE_ARRAY*)pInput;
    
                        if (inputMsg->NumOfBytes > 1)
                        {
                            LOG(PROTOCOL, "Wake up message length is greater than 1 Byte");
                            return ERR_INVALID_MSG;
                        }
                        
                        LOG(PROTOCOL, "pInput array");
                        LOG(PROTOCOL, "Wake up for ECU with id 0x%02x called", inputMsg->BytePtr[0]);
                        LOG(PROTOCOL, "pInput array end");
    
                        unsigned char KeyWords[2]{};
                        SBYTE_ARRAY outputMsg{};
                    
                        LOG_SBYTE(PROTOCOL, inputMsg);
    
                        KeyWords[0] = 0x00;
                        KeyWords[1] = 0x81;
    
                        outputMsg.NumOfBytes = 2;
                        outputMsg.BytePtr = &KeyWords[0];
    
                        *(SBYTE_ARRAY*)pOutput = outputMsg;
                    
                        LOG(PROTOCOL, "pOutput array");
                        LOG_SBYTE(PROTOCOL, (SBYTE_ARRAY*)pOutput);
                        LOG(PROTOCOL, "pOutput array end");
                    }
                    break;
    Das hat nun die Auswirkung, dass ich im Test-Tool einen festen Wert bekomme, der sich auch nicht ändert, wenn ich mehrmals auf Execute drücke. Bild ist im Anhang.

    Zitat Zitat von Holomino Beitrag anzeigen
    dann tschuldige, dass ich lebe.
    Was soll das denn bitte???
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken TestTool_2.jpg  

  9. #9
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2008
    Ort
    Korntal-Münchingen
    Alter
    42
    Beiträge
    15

    Ich hab's....

    Moin miteinander,

    ich wollte mal verkünden, dass ich das Problem gefunden und gelöst habe.

    Wie vermutet, arbeitet das Test-Tool richtig, das Problem lag in meiner DLL...

    Der Aufrufer (also das Test-Tool) gibt die Referenz auf eine SBYTE_ARRAY - Struktur rein und erwartet, dass genau diese Referenz beschrieben wird. Nicht eine neue erstellt und dann die ursprüngliche überschrieben, nein, die originale wird verwendet.

    Das sieht im Code dann wie folgt aus (habe nur den betreffenden CASE reinkopiert):

    Code:
                case FIVE_BAUD_INIT:
                    {
                        LOG(PROTOCOL, "Five baud init");
    
                        SBYTE_ARRAY& inputArray = *(SBYTE_ARRAY*)pInput;
                        SBYTE_ARRAY& outputArray = *(SBYTE_ARRAY*)pOutput;
                        unsigned long numberOfInputBytes = inputArray.NumOfBytes;
    
                        LOG(PROTOCOL, "pInput array");
                        LOG(PROTOCOL, "Wake up for ECU with id 0x%02x called", inputArray.BytePtr[0]);
                        LOG(PROTOCOL, "pInput array end");
    
                        unsigned long numberOfOutputBytes = outputArray.NumOfBytes;
    
                        outputArray.NumOfBytes = 2;
                        outputArray.BytePtr[0] = 0x00;
                        outputArray.BytePtr[1] = 0x81;
    
                        LOG(PROTOCOL, "pOutput array");
                        LOG_SBYTE(PROTOCOL, (SBYTE_ARRAY*)pOutput);
                        LOG(PROTOCOL, "pOutput array end");
                    }
                    break;
    pInput und pOutput sind die beiden Referenzen, die werden auf SBYTE_ARRAY gecastet und dann "ganz normal" verwendet.

    Nun tut es so wie gewünscht.

  10. #10
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    20.08.2004
    Ort
    Unterschleissheim
    Beiträge
    325
    Sehr gut, und danke für die Rückmeldung.

    Gruß

Ähnliche Themen

  1. Tiny13(A), PWM, $crystal: Komisches Verhalten
    Von M_I_B im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 21
    Letzter Beitrag: 18.09.2012, 10:19
  2. RP6-Wetterstation komisches Verhalten
    Von Virus im Forum Allgemeines zum Thema Roboter / Modellbau
    Antworten: 5
    Letzter Beitrag: 03.01.2010, 11:35
  3. Komisches AVR Verhalten...
    Von EDatabaseError im Forum AVR Hardwarethemen
    Antworten: 2
    Letzter Beitrag: 21.02.2007, 19:44
  4. Komisches Verhalten bei SD21 mit Picaxe mit ADC
    Von Anubisbot im Forum Sensoren / Sensorik
    Antworten: 9
    Letzter Beitrag: 17.11.2005, 11:46
  5. Komisches verhalten meiner CC1
    Von d4rkwolf im Forum Elektronik
    Antworten: 7
    Letzter Beitrag: 01.03.2005, 20:58

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen