Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » Borland C++ Builder » C++ gepackte Struktur via #PRAGMA pack(1)

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
24.01.2016, 11:51 Uhr
DerSofti



Hallo zusammen,
ich versuche momentan ein Packed Record von Pascal in C++ zu implementieren und auf einen Zeiger dort zuzugreifen, der als PUCHAR dort die Adresse eines anderen Records bzw. Struktur enthält.

Zunächst die Strukturdeklarationen


C++:
typedef struct _NCB {
    UCHAR   ncb_command;            /* command code                   */
    UCHAR   ncb_retcode;            /* return code                    */
    UCHAR   ncb_lsn;                /* local session number           */
    UCHAR   ncb_num;                /* number of our network name     */
    PUCHAR  ncb_buffer;             /* address of message buffer      */
    WORD    ncb_length;             /* size of message buffer         */
    UCHAR   ncb_callname[NCBNAMSZ]; /* blank-padded name of remote    */
    UCHAR   ncb_name[NCBNAMSZ];     /* our blank-padded netname       */
    UCHAR   ncb_rto;                /* rcv timeout/retry count        */
    UCHAR   ncb_sto;                /* send timeout/sys timeout       */
    void (CALLBACK *ncb_post)( struct _NCB * ); /* POST routine address        */
    UCHAR   ncb_lana_num;           /* lana (adapter) number          */
    UCHAR   ncb_cmd_cplt;           /* 0xff => commmand pending       */
#ifdef _WIN64
    UCHAR   ncb_reserve[18];        /* reserved, used by BIOS         */
#else
    UCHAR   ncb_reserve[10];        /* reserved, used by BIOS         */
#endif
    HANDLE  ncb_event;              /* HANDLE to Win32 event which    */
                                    /* will be set to the signalled   */
                                    /* state when an ASYNCH command   */
                                    /* completes                      */
} NCB, *PNCB;


typedef struct _ADAPTER_STATUS {
    UCHAR   adapter_address[6];
    UCHAR   rev_major;
    UCHAR   reserved0;
    UCHAR   adapter_type;
    UCHAR   rev_minor;
    WORD    duration;
    WORD    frmr_recv;
    WORD    frmr_xmit;

    WORD    iframe_recv_err;

    WORD    xmit_aborts;
    DWORD   xmit_success;
    DWORD   recv_success;

    WORD    iframe_xmit_err;

    WORD    recv_buff_unavail;
    WORD    t1_timeouts;
    WORD    ti_timeouts;
    DWORD   reserved1;
    WORD    free_ncbs;
    WORD    max_cfg_ncbs;
    WORD    max_ncbs;
    WORD    xmit_buf_unavail;
    WORD    max_dgram_size;
    WORD    pending_sess;
    WORD    max_cfg_sess;
    WORD    max_sess;
    WORD    max_sess_pkt_size;
    WORD    name_count;
} ADAPTER_STATUS, *PADAPTER_STATUS;

typedef struct _NAME_BUFFER {
    UCHAR   name[NCBNAMSZ];
    UCHAR   name_num;
    UCHAR   name_flags;
} NAME_BUFFER, *PNAME_BUFFER;


    typedef struct asb ASB ;


 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
24.01.2016, 12:04 Uhr
DerSofti



Dann folgt die Testroutine dazu, hier zunächst die Deklaration der lokalen Variablen


C++:
void __fastcall TForm1::ButtonTestStartenClick(TObject *Sender)
{
    // Steuervariablen von Pascal übernommen
    int Result;
    _NCB NCB;
    LANA_ENUM Enum;
    ADAPTER_STATUS Adapter;
    int I, L, NameLen;
    std::string Machine, MachineName;
//    Adapter: AStat;
    // Eigene Steuervariablen zum Ersatz fehlender Funktionen aus Pascal
    int iCount1, iCount2;
    _NCB *ncbptr;
    LANA_ENUM *lanaptr;
//    ADAPTER_STATUS *adaptr;

#pragma pack(1)
    struct _Astat {
        ADAPTER_STATUS adapt;
        _NAME_BUFFER NameBuff[29];
    };
    _Astat Astat;
    _Astat *adaptr;




Hier der Quellcode der lokalen Test-Routine, die auskommentierten Befehle sind aus Pascal und dienen mir zur Orientierung, was gerade nach C++ übersetzt wird.


C++:
    NetBiosLib = 0;
    Machine = "TEST-PC";
    MachineName = "TEST-PC";
    if (MachineName == "")
      MachineName = "*";
    NameLen = strlen(MachineName.c_str());

/////////////////////////////////////////////////////////////////////////////
//    L := NCBNAMSZ - NameLen;
//    if L > 0 then
//    begin
//      SetLength(MachineName, NCBNAMSZ);
//      FillChar(MachineName[NameLen + 1], L, ' ');
//    end;


    L = NCBNAMSZ - NameLen;
    if (L > 0) {
        //memset(MachineName, NCBNAMSZ);
        for (iCount1 = NameLen; iCount1 < NCBNAMSZ; iCount1++) {
//            MachineName[iCount1] = ' ';
            MachineName.append(" ");
        }
    }
/////////////////////////////////////////////////////////////////////////////

//    MachineName[NCBNAMSZ] := #0;
//    ResetMemory(NCB, SizeOf(NCB));
//    NCB.ncb_command := NCBENUM;
//    NCB.ncb_buffer := Pointer(@Enum);
//    NCB.ncb_length := SizeOf(Enum);

    MachineName[NCBNAMSZ] = '\0';
    ncbptr = &NCB;
    memset ( ncbptr , '\0' , sizeof (  NCB ) );
    NCB.ncb_command = NCBENUM;
    lanaptr = &Enum;
    NCB.ncb_buffer = (UCHAR *)&Enum;
    NCB.ncb_buffer = (PUCHAR(&Enum));
//    PUCHAR(isENABLED)^ := $00;
//    NCB.ncb_buffer = (UCHAR((System::Pointer) &Enum));
//    System::Pointer(Enum);
    NCB.ncb_length = sizeof(Enum);
/////////////////////////////////////////////////////////////////////////////

//  function InitNetbios: Boolean;
//  begin
//    Result := True;
//    if NetBiosLib = 0 then
//    begin
//      NetBiosLib := SafeLoadLibrary('netapi32.dll');
//      Result := NetBiosLib <> 0;
//      if Result then
//      begin
//        @_NetBios := GetProcAddress(NetBiosLib, PChar('Netbios'));
//        Result := @_NetBios <> nil;
//        if not Result then
//          ExitNetbios;
//      end;
//    end;
//  end;


    if (NetBiosLib == 0) {
        NetBiosLib = SafeLoadLibrary(L"netapi32.dll"); // Laden der DLL
        if (Result) {
            Result = 0;
            _NetBios = (DLLFUNCTION*)::GetProcAddress((HMODULE)NetBiosLib, "Netbios"); // Laden der DLL-Funktion
            if (_NetBios != NULL) {
                Result = 0;
            }
        }
    }

/////////////////////////////////////////////////////////////////////////////

//    if NetBios(@NCB) = NRC_GOODRET then
//    begin
//      Result := Enum.Length;
//      for I := 0 to Ord(Enum.Length) - 1 do
//      begin
//        ResetMemory(NCB, SizeOf(NCB));
//        NCB.ncb_command := NCBRESET;
//        NCB.ncb_lana_num := Enum.lana[I];
//        if NetBios(@NCB) = NRC_GOODRET then
//    begin
//      Result := Enum.Length;
//      for I := 0 to Ord(Enum.Length) - 1 do
//      begin
//        ResetMemory(NCB, SizeOf(NCB));
//        NCB.ncb_command := NCBRESET;
//        NCB.ncb_lana_num := Enum.lana[I];
//        if NetBios(@NCB) = NRC_GOODRET then
//        begin
//          ResetMemory(NCB, SizeOf(NCB));
//          NCB.ncb_command := NCBASTAT;
//          NCB.ncb_lana_num := Enum.lana[I];
//          Move(MachineName[1], NCB.ncb_callname, SizeOf(NCB.ncb_callname));
//          NCB.ncb_buffer := PUCHAR(@Adapter);
//          NCB.ncb_length := SizeOf(Adapter);
//          if NetBios(@NCB) = NRC_GOODRET then
//            Addresses.Add(AdapterToString(@Adapter.adapt));
//        end;
//      end;
//    end;
//  end;


    if (_NetBios(&NCB) == NRC_GOODRET) // Start der DLL-Funktion mit Prüfung
    {
        Result = Enum.length;
        for (I = 0; I <Enum.length; I++) {
            NCB.ncb_command = NCBRESET;
            NCB.ncb_lana_num = Enum.lana[I];
            if (_NetBios(&NCB) == NRC_GOODRET) {
                Result = 0;
                //  ResetMemory(NCB, SizeOf(NCB));
                //  NCB.ncb_command := NCBRESET;
                memset ( ncbptr , '\0' , sizeof (  NCB ) );
                NCB.ncb_command = NCBASTAT;
                NCB.ncb_lana_num = Enum.lana[I];
                //  Move(MachineName[1], NCB.ncb_callname, SizeOf(NCB.ncb_callname));
                memcpy(NCB.ncb_callname, MachineName.c_str(), strlen(MachineName.c_str()));
                //  NCB.ncb_buffer := PUCHAR(@Adapter);
//(UCHAR *)&Enum;
//                NCB.ncb_buffer = PUCHAR(&Adapter);
                adaptr = &Astat;
                memset ( adaptr , '\0', sizeof ( Astat ) );
                NCB.ncb_buffer = (UCHAR *)&Astat;
//                NCB.ncb_buffer = (UCHAR *)&Enum;

                NCB.ncb_length = sizeof(Astat);
                NCB.ncb_length = 600;
//                NCB.ncb_buffer = (char far *)Adapter;
//                NCB.ncb_buffer = (PUCHAR far *)adaptr;
// (char far *)&asb_buf;
                if (_NetBios(&NCB) == NRC_GOODRET)
                {
                    Result = 0;
                }

            }
        }
    }
}



Hier ein Aufruf, der funktioniert mit der Struktur Enum:


C++:
    NCB.ncb_buffer = (UCHAR *)&Enum;
    NCB.ncb_buffer = (PUCHAR(&Enum));
....
    if (_NetBios(&NCB) == NRC_GOODRET) // Start der DLL-Funktion mit Prüfung




Hier der Auruf, wo ich eine Access-Violation bekomme als Fehlermeldung und im CPU-Fenster lauter ??? anstatt Assemblerbefehle sind. Als Ursache vermute ich das mit dem Packed Record und habe gegoogelt, daß der Pendant zu Packed Record der Compiler-Befehl #pragma pack in C++ ist. Allerdings bekomme ich auch weiterhin denselben Fehler mit #Pragma pack, soll in die Klammer vielleicht die Größe der Struktur rein bei pack? Steh da gerade völlig auf dem Schlauch :/


C++:
                NCB.ncb_buffer = (UCHAR *)&Astat;
//                NCB.ncb_buffer = (UCHAR *)&Enum;

                NCB.ncb_length = sizeof(Astat);
                NCB.ncb_length = 600;
//                NCB.ncb_buffer = (char far *)Adapter;
//                NCB.ncb_buffer = (PUCHAR far *)adaptr;
// (char far *)&asb_buf;
                if (_NetBios(&NCB) == NRC_GOODRET)
                {
                    Result = 0;
                }

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
24.01.2016, 14:20 Uhr
ao

(Operator)


Die Syntax fürs Packen ist

C++:
#pragma pack (push)   // bisher gültige Einstellung zwischenspeichern
#pragma pack(1)     // Packungsdichte auf 1 Byte setzen
typedef struct .....   // alle Strukturen definieren, die gepackt werden sollen
#pragma pack (pop)  // vorher gültige Einstellung wieder herstellen.



Und ich würde mich nicht zu sehr drauf versteifen, dass die Packerei die Ursache ist. Der Code ist reichlich chaotisch und ich bin nicht mehr der Pascal-Fachmann, aber ich würde nicht sagen dass

C++:
            _NetBios = (DLLFUNCTION*)::GetProcAddress((HMODULE)NetBiosLib, "Netbios"); // Laden der DLL-Funktion
            if (_NetBios != NULL) {
                Result = 0;
            }


äquivalent ist zu

Code:
        @_NetBios := GetProcAddress(NetBiosLib, PChar('Netbios'));
        Result := @_NetBios <> nil;
        if not Result then
          ExitNetbios;



Soll heißen, der Pascal-Code steigt bei einem Initialisierungsfehler mit ExitNetbios aus, der C++-Code setzt Result auf 0 und läuft einfach weiter, mit möglicherweise undefinierten Folgen.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
25.01.2016, 18:13 Uhr
DerSofti



Danke für die Info bezüglich des Packens. NetBios braucht das scheinbar, weil es aus den 80ger Jahren stammt, wo Speicher noch Mangelware war. Hab da uralte Sourcen, teils C und Assembler sogar, was heute die DLL-Funktion ist, war früher ein Interrupt. Naja sehr umständlich das ganze und dann noch Zeiger als Felder einer Struktur, welche auf eine andere Struktur zeigen

Die Ausstiegsprozedure habe ich vorerst bewußt weggelassen, mir geht es in erster Linie darum, daß der Befehl Adapterstatus ein verwertbares Ergebnis liefert.
Die Befehle Enum und Reset wurden ohne Fehler ausgeführt, also das Feld NCB.ncb_retcode lieferte zumindest bei diesen beiden Befehlen 0 zurück. Bei Enum wurde dann auch korrekt die Adapteranzahl eingetragen, dessen Adresse zuvor bei NCB.ncb_buffer mitgeliefert wurde, insofern funktionieren die DLL-Funktionsaufrufe. Nur bei Adapterstatus selber hackt es, wenn ich exakt den Namen bei NCB.ncb_callname übergebe wie bei dem Pascalprogramm auch, kommt ein Speicherzugriffsfehler. Wenn ich den Namen NCB.ncb_callname um ein Zeichen kürze, kommt kein Speicherfehler aber es kommt vom NetBios der Fehlercode 40h (Systemfehler) im Feld NCB.ncb_retcode. Sowohl in Pascal als auch in C++ sind die Adressen der einzelnen Zeichen von [0] bis [15] gleich, daran kann es also doch nicht liegen. Darum habe ich jetzt auch NCB gepackt, leider ohne Ergebnis. Die Suche geht also weiter und für weitere Hinweise währe ich dankbar.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
25.01.2016, 18:29 Uhr
DerSofti



Achso ein Fehler hab ich noch behoben und zwar das Array von NameBuffer bei Adapterstatus,
beim funktionierenden Orginal von Pascal war es von 0 bis 29, also 30 Felder, bei C++ hingegen habe ich das von 29 auf 30 ändern müßen, da Arrays sich da ein klein wenig unterscheiden. Aufgefallen ist mir das parallelen Durchlauf beider Programme, wo sizeof(Adapter) um 18 Bytes abwich. Leider war dies aber auch nicht die Fehlerursache, es bleibt bei entweder "Access violation at" oder bei dem Fehlercode 40h.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
25.01.2016, 18:55 Uhr
DerSofti



Die nächste Spur, die ich als heiß ansehe, ist das mit den Pointern selbst, denn

im Uraltprogramm unter DOS wird die Adresse so gespeichert


C++:
    adapter_status.buffer = (char far *)&asb_buf;



in Pascal so
NCB.ncb_buffer := PUCHAR(@Adapter);
während Enum so übergeben wird

NCB.ncb_buffer := Pointer(@Enum);

beides Adressen nur anders, bei Adapter mit "far" und bei Enum gehen folgende Zuweisungen anstandslos, die DLL-Funktion meldet keinen Fehler. Ich werde also mal schauen, was das nochmal mit dem "far" war, kenne sowas von früher aus Assembler, aber schon lange her.


C++:
    NCB.ncb_buffer = (UCHAR *)&Enum;
    NCB.ncb_buffer = (PUCHAR(&Enum))

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
25.01.2016, 21:54 Uhr
ao

(Operator)


far gibts schon lange nicht mehr, das ist ein Überbleibsel aus alten 16-Bit-Tagen. Das solltest du eigentlich ersatzlos streichen können.

Kann es sein, dass die anderen Strukturen (NCB und ADAPTER_STATUS) ebenfalls gepackt werden müssen? Hast du die ganzen Element-Typen (WORD, DWORD, UCHAR, PUCHAR) richtig definiert? Prüf nach, welche Größe die haben müssen. Wenn das ursprünglich aus der 16-Bit-Welt kommt, dann kann es sein, dass du 16 Bit große Pointertypen brauchst - das wird in der 32-Bit-Welt ein Problem.

Und wieso schreibst du da adapter_status.buffer? ADAPTER_STATUS hat kein Member, das buffer heißt. Hast du etwa unterschiedliche Definitionen? Dann ist es kein Wunder, dass das kracht, oder?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
26.01.2016, 16:58 Uhr
DerSofti



Ja mit far fiel mir heute ein, früher in Assembler hatte ein Datensegment 64kb und wenn eine Adresse im anderen Segment war, dann war die Adressierung mit "far". 100% kann ich das aber nicht mehr so sagen. Was mich aber stutzig macht, sind die zwei unterschiedlichen Adresszuweisungen in Pascal


Code:
  NCB.ncb_buffer := Pointer(@Enum);
  NCB.ncb_buffer := PUCHAR(@Adapter);



funktionieren auch beide in Pascal sowie die ganze Routine in Pascal.

In C++ hingegen funktioniert alles bis zum Adapterstatus, da liegt das meiner Ansicht nach an der anderen Art der Adresszuweisung. Ich werde mir das mit den Adressen jetzt nochmal genauer anschauen und auch die Bib-Funktionen, die dahinterstecken. Pointer ist ein Member von System und PUCHAR von der Windows.Api. Die anderen Strukturen habe ich schon gepackt, daran lag es also nicht.
adapter_status.buffer verwende ich bei der Übersetzung nicht, ist nur ein Codeschnippsel den ich aus einen alten Beispielprogramm zu NetBios habe in C, wo das ganze noch über DOS-Interrupts läuft und wo auch far vorkommt. Das Beispielprogramm ist von dieser Seite
www.netbiosguide.com/
Die Definitionen der Strukturen sind in beiden Programmen gleich, da Pascal auch dieselbe Header-Datei verwendet, welche übrigens kurioserweise in C++ ist, aber trotzdem von Pascal verwendet wird.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
26.01.2016, 17:32 Uhr
DerSofti



Also mit den Adressen hab ich es jetzt mal probiert, wenn man die führenden Nullen bei C++ wegläßt, sind die von der länge her gleich.

Pascal:
$18F954 - Enum
1234567 Zeichen

$18F6FC - Adapter
1234567 Zeichen


C++:
:0018F300 - Enum
123456789 Zeichen

:0018F0A8 - Adapter
123456789 Zeichen

Jetzt hab ich mal zum Spaß die Adresse und die Länge von Enum bei dem dritten und entscheidenden Befehl Adapterstatus übergeben und siehe da, es funktioniert, die MAC-Adresse wird in Enum gespeichert. Allerdings eine unsaubere Lösung, also ganz zufrieden bin ich noch nicht. Und als nächstes kommt dann noch, die Hexzahlen aus einem Byte aufsplitten in jeweils 2 Bytes, damit die MAC-Adresse dann auch im ASCI-Format verfügbar ist.
Naja, Schritt für Schritt kommen wir dem Fehlerteufel auf die Schliche
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
28.01.2016, 11:08 Uhr
DerSofti



Das ganze läuft jetzt, habe die Kombinationen der Strukturen von Adapter und NameBuffer in der Headerdatei nochmals implementiert und aus der lokalen Funktion entfernt. Bei der lokalen Funktion konnte ich übrigens nichtmal ein lokales Char-Array mit Daten befüllen, trotz Zuweisung passierte nichts, sehr seltsam das ganze. Naja Hauptsache es läuft jetzt.

Die Übersetzung ging auch nicht so komfortabel wie in Pascal


Code:
function AdapterToString(Adapter: PJclByteArray): string;
begin
  Result := Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',
   [Integer(Adapter[0]), Integer(Adapter[1]),
    Integer(Adapter[2]), Integer(Adapter[3]),
    Integer(Adapter[4]), Integer(Adapter[5])]);
end;



Aber C++ kann ja schließlich alles, also hab ich mir selber so eine Funktion gebastelt :-)


C++:
TWinSystem::TransformAdress(UCHAR ucParam[6])
{
    byte bAX;
    byte bSplitt1, bSplitt2;
    int iCode, iCount;

    for (iCount = 0; iCount < 6; iCount++) {
        bAX = ucParam[iCount];
        asm
        {
            mov EAX, 0;
            mov al, bAX;
            shl AX, 4;
            mov bSplitt1, ah;
            shr al, 4;
            mov bSplitt2, al;
        }

        iCode = bSplitt1;
        if (iCode >= 10) {
            switch (iCode) {
                case 10: {
                            usMACAdr += 'A';
                            break;
                         }
                case 11: {
                            usMACAdr += 'B';
                            break;
                         }
                case 12: {
                            usMACAdr += 'C';
                            break;
                         }
                case 13: {
                            usMACAdr += 'D';
                            break;
                         }
                case 14: {
                            usMACAdr += 'E';
                            break;
                         }
                case 15: {
                            usMACAdr += 'F';
                            break;
                         }
            default:
                ;
            }
        }
        else
        {
            usMACAdr += IntToStr(iCode);
        }

        iCode = bSplitt2;
        if (iCode >= 10) {
            switch (iCode) {
                case 10: {
                            usMACAdr += 'A';
                            break;
                         }
                case 11: {
                            usMACAdr += 'B';
                            break;
                         }
                case 12: {
                            usMACAdr += 'C';
                            break;
                         }
                case 13: {
                            usMACAdr += 'D';
                            break;
                         }
                case 14: {
                            usMACAdr += 'E';
                            break;
                         }
                case 15: {
                            usMACAdr += 'F';
                            break;
                         }
            default:
                ;
            }
        }
        else
        {
            usMACAdr += IntToStr(iCode);
        }
        if (iCount < 5)
            usMACAdr += '-';
    }
    iCount = iCount;
}




usMACAdr ist übrigens ein privates UnicodeString-Feld dieser Klasse.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ Borland C++ Builder ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: