WinAPI Datentyp Inkompatiblität mit gcc 3.4.2



  • Hallo zusammen,

    Ich habe ein Problem im Zusammenspiel von WinAPI mit gcc Version 3.4.2 unter Windows XP.
    Gleich vorweg: Ich kann den Compiler weder wechseln noch updaten, keine Makefileänderung durchführen und auch keine anderen Konfigurationen ändern.

    Ich arbeite an zwei C-Funktionen in einer Software Komponente einer Simulation, in die ich Funktionalität einbaue und die dann mit einem ansonsten existierenden Projekt mit oben beschriebenen System zusammencompiliert/-gelinkt werden.

    An mehreren Stellen rufe ich die Funktionen

    QueryPerformanceCounter
    

    http://msdn.microsoft.com/en-us/library/ms644904%28VS.85%29.aspx
    und

    QueryPerformanceFrequency
    

    http://msdn.microsoft.com/en-us/library/ms644905%28VS.85%29.aspx
    auf.

    Diese haben einen out Parameter vom Typ LARGE_INTEGER*, der laut MSDN wie folgt definiert ist:

    typedef union _LARGE_INTEGER {
      struct {
        DWORD LowPart;
        LONG  HighPart;
      } ;
      struct {
        DWORD LowPart;
        LONG  HighPart;
      } u;
      LONGLONG QuadPart;
    } LARGE_INTEGER, *PLARGE_INTEGER;
    

    mein eigentlicher Ansatz war einfach, die Methoden wie folgt aufzurufen:

    LARGE_INTEGER value;
    QueryPerformanceCounter(&value);
    

    Leider wird der Code nicht compiliert mit der "aussagekräftigen" Fehlermeldung:

    Syntax error before value
    

    in der Zeile mit der Variablendefinition.

    Ich habe dann nach langem Suchen mal versucht, mir den Typ per typedef neu zu definieren:

    typedef union _L_I {
      struct {
        DWORD LowPart;
        LONG  HighPart;
      } ;
      struct {
        DWORD LowPart;
        LONG  HighPart;
      } u;
      LONGLONG QuadPart;
    } L_I, *PL_I;
    
    L_I value;
    

    Das compiliert auch nicht, und zwar bemängelt er dann einen

    Syntax error before DWORD
    

    (bezogen auf "DWORD LowPart;")
    Ich schätze daher, dass der Compiler mit anonymen structs in unions nicht klarkommt. (Diverse Treffer bei Google erhärten den Verdacht, aber können ihn nicht festklopfen)

    Meine erste Frage:
    Sind meine Schlussfolgerungen bis hierher richtig oder habe ich hier etwas nicht beachtet / übersehen / falsch gedacht?

    Ich habe dann mehrere Sachen ausprobiert, um ein Workaround zu basteln (die Funktion QueryPerformanceCounter ist ja implementiert und wenn ich ihr nur genug Speicher gebe, müsste das klappen, so meine Logik - bitte korrigieren, falls falsch gedacht) und folgender Code compiliert schon mal:

    void* value = malloc(8); // 8 Byte (8 = sizeof(LARGE_INTEGER), da 64 Bit INT) 
      QueryPerformanceCounter(value); 
      // ...
      free(value);
    

    Allerdings stürzt mir die Software ab, wenn ich value nach dem Aufruf von QueryPerformanceCounter versuche in einen Pointer auf ein 8 Byte Integer (long long 😉 zu casten und dann zu dereferenzieren

    void* value = malloc(8); // 8 Byte (8 = sizeof(LARGE_INTEGER), da 64 Bit INT) 
      QueryPerformanceCounter(value); 
      *((long long*)value) ; // BOOOM!
      free(value);
    

    Wenn ich den Speicher von value vorher in einen (long long*) caste, stürzt bereits der Auruf von QueryPerformanceCounter ab.

    void* value = (long long*)malloc(8); // 8 Byte (8 = sizeof(LARGE_INTEGER), da 64 Bit INT) 
      QueryPerformanceCounter(value); // BOOOM! 
      // ...
      free(value);
    

    Nun bin ich relativ ratlos. Eigentlich dachte ich, dass ich in C (habe meistens mit C++ gearbeitet), den Speicher relativ wahllos uminterpretieren kann, wenn nur die Anzahl der Bytes stimmt.

    Meine zweite Frage:
    Seht ihr, was ich falsch gemacht habe / vergessen habe zu tun oder was ich tun muss, um den Wert aus value zu kriegen? Bzw. seht irgendeinen anderen Grund, warum das Programm verreckt?



  • Beim gcc gibts glaube ich int64_t



  • Hast du dir den Prototype für QueryPerformanceCounter jetzt selbst definiert? Falls ja, hast du vielleicht vergessen die Calling-Convention anzugeben? Das könnte leicht komische Abstürze verursachen, speziell wenn man danach Zeiger dereferenziert die aufm Stack liegen (Base-Pointer kaputt und so).

    BTW: wenn du dir die Union selbst definieren willst, wieso dann nicht so:

    typedef union _LARGE_INTEGER_X {
      struct susi {
        DWORD LowPart;
        LONG  HighPart;
      } su;
      struct strolchi {
        DWORD LowPart;
        LONG  HighPart;
      } st;
      LONGLONG QuadPart;
    } LARGE_INTEGER_X, *PLARGE_INTEGER_X;
    

    Das sollte GCC dann auch fressen.
    Bzw. noch einfacher:

    typedef struct _LARGE_INTEGER_X {
      LONGLONG QuadPart;
    } LARGE_INTEGER_X, *PLARGE_INTEGER_X;
    


  • Was den Problem mit LARGE_INTEGER angeht: hast du einfach mal versucht, ein union vor den Typ bei der Deklaration der Variablen anzugeben? Meines miesen C-Wissens wird durch ein typedef dies zwar unnötig gemacht, aber vielleicht hilft es ja:

    union LARGE_INTEGER MyOne;
    

    Ist aber eher unwahrscheinlich.

    Was deinen Workaround angeht, so solltest du niemals versuchen, im Code eigene Konstanten für die Speicherbelegung anzugeben. Die meisten Compiler optimieren den Scheiß, den du eingibst, so grundlegend, dass das Speicherabbild einer Struktur hinterher nicht mehr viel mit dem ursprunglichen Objekt zu tun hat. Nur wenn du dir absolut sicher bist, kannst du dir dieses Vergnügen leisten, denn mehr ist es nicht ... der sizeof -Operator gibt dir alles, was du brauchst (auch hier mal mit union ausprobieren).

    Ansonsten würde ich einmal versuchen, eine ganz kleine, eigenständige Union zu schreiben und zu schauen, ob der gcc das frisst. Wenn nicht, probiere mit typedef vor der Typ- oder mit union oder struct vor der Objektdeklaration aus, ob's so funktioniert.

    @hustbaer: kann es sein, dass du vermutest, dass er den Prototyp von QueryPerformanceCounter selbst definiert hat? Ich frage mich nämlich, wie der Linker das linken kann ... ich meine, die Signatur stimmt ja nicht mehr mit den Bibliotheksfunktionen überein.



  • Vielen vielen Dank für die Antworten schon mal!
    Am Dienstag, wenn ich wieder an dem entsprechenden PC arbeite, kann ich eure Tipps ausprobieren. Ich melde mich danach dann wieder.

    @hustbaer:
    Nein, ich habe den Prototyp nicht selbst definiert. Da frisst er den aus der WinAPI (warum auch immer).

    @Glühbirne:
    Wie der Linker das linken kann, frage ich mich auch, ich gucke am Montag mal nach der Signatur in der windows.h auf dem System und poste sie.



  • Glühbirne schrieb:

    Was deinen Workaround angeht, so solltest du niemals versuchen, im Code eigene Konstanten für die Speicherbelegung anzugeben. Die meisten Compiler optimieren den Scheiß, den du eingibst, so grundlegend, dass das Speicherabbild einer Struktur hinterher nicht mehr viel mit dem ursprunglichen Objekt zu tun hat.

    Äh, nein, das tun sie ganz sicher nicht.

    @hustbaer: kann es sein, dass du vermutest, dass er den Prototyp von QueryPerformanceCounter selbst definiert hat? Ich frage mich nämlich, wie der Linker das linken kann ... ich meine, die Signatur stimmt ja nicht mehr mit den Bibliotheksfunktionen überein.

    Ja, hatte ich angenommen. Ein leiser Hinweis darauf was dass ich ihn explizit danach gefragt habe.

    Signatur ist doch Schnuppe so lange die Funktionen nicht mit dekorierten Namen gelinkt werden. "QueryPerformanceCounter" heisst in der LIB einfach "QueryPerformanceCounter" und nicht "_QueryPerformanceCounter@BLAH234".



  • Paul Manns schrieb:

    @hustbaer:
    Nein, ich habe den Prototyp nicht selbst definiert. Da frisst er den aus der WinAPI (warum auch immer).

    Sind das C-ohne-++ Files die da QueryPerformanceCounter aufrufen? Ich frage mich nämlich wie das sonst jemals gehen soll, dass du QueryPerformanceCounter ohne Cast mit einem void-Pointer aufrufst.



  • hustbaer schrieb:

    Paul Manns schrieb:

    @hustbaer:
    Nein, ich habe den Prototyp nicht selbst definiert. Da frisst er den aus der WinAPI (warum auch immer).

    Sind das C-ohne-++ Files die da QueryPerformanceCounter aufrufen? Ich frage mich nämlich wie das sonst jemals gehen soll, dass du QueryPerformanceCounter ohne Cast mit einem void-Pointer aufrufst.

    Ja, findet (zu meinem Leidwesen) alles in C statt.



  • Wenn ich ein union der Definition voranstelle, sagt mir der Compiler fuer die Variablen:

    "Storage size of 'BEZEICHNER' isn't known."

    Der folgende Code

    typedef struct _LARGE_INTEGER_X {
      LONGLONG QuadPart;
    } LARGE_INTEGER_X, *PLARGE_INTEGER_X;
    
    struct LARGE_INTEGER_X var;
    

    bricht ab mit:

    ERROR: syntax error before LONGLONG
    WARNING: no semicolon at end of struct or union (in der Zeile LONGLONG QuadPart;)
    WARNING: data definition has no type or storage class
    ERROR: storage size of var isn't known

    Folgendes funktioniert auch nicht:

    typedef struct {
      long long QuadPart;
    } LARGE_INTEGER_X, *PLARGE_INTEGER_X;
    
    struct LARGE_INTEGER_X var;
    

    ERROR: storage size of var isn't known

    /Gerade bemerkt:
    Folgender Code stuerzt nicht ab:

    long long * frequency
    // ...
    frequency = (long long *)malloc(8);
    QueryPerformanceFrequency(frequency);
    

    QueryPerformanceFrequency(frequency) liefert ausserdem true, allerdings ist das Ergebnis nach der Dereferenzierung eine negative Zahl (-1294507296), was mich etwas verwirrt. 😕

    Folgender Code stuerzt ausserdem allerdings weiter ab:

    long long * tStart
    // ...
    tStart = (long long *)malloc(8);
    QueryPerformanceCounter(tStart);
    

    Zudem kriege ich die zwei folgenden Warnungen (wenn er fehlerfrei compiliert):

    Warning: resolving _QueryPerformanceCounter by linking to _QueryPerformanceCounter
    Warning: resolving _QueryPerformanceFrequency by linking to _QueryPerformanceFrequency

    Aus denen werde ich auch ueberhaupt nicht schlau.
    Heisst das, dass er die beiden Funktionen nicht richtig gelinkt hat?
    ➡ Warum gibt es dann keine Fehlermeldung?
    ➡ Wieso funktioniert die eine und die andere nicht?



  • Ich habe jetzt eine Teilloesung des Problems.
    LARGE_INTEGER laesst sich verwenden, wenn man Windows.h als erste der Headerfiles einbindet, da eine der Bibliotheken fuer die Simulationsumgebung CONST neudefiniert. 😞

    Jetzt sind die Abstuerze, Compilerfehler etc. schonmal vorbei.
    Einzig komisch noch, dass QueryPerformanceFrequency eine negative Zahl als Ergebnis liefert.



  • Paul Manns schrieb:

    Einzig komisch noch, dass QueryPerformanceFrequency eine negative Zahl als Ergebnis liefert.

    Ist der Aufruf denn erfolgreich (returned es TRUE)!?


Anmelden zum Antworten