Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Fehler in Record-Ausrichtung bei Übergabe an Visual C DLL (https://www.delphipraxis.net/1917-fehler-record-ausrichtung-bei-uebergabe-visual-c-dll.html)

oki 30. Dez 2002 16:09


Fehler in Record-Ausrichtung bei Übergabe an Visual C DLL
 
Hallo,

ich hab hier ein ganz blödes Problem. Für ein aktuelles Projekt rufe ich Methoden aus einer Dll mit Parameterübergabe mittels Zeiger auf Strukturen auf. Hierbei bekomme ich sporadisch Lesefehler auf Adressen aus der DLL.
Nach einem Gespräch mit dem Ersteller der DLL habe ich folgenden Tip überprüft.
Die DLL ist in Visual C geschrieben und erwartet eine 8 Byte Ausrichtung für Structuren (Records) im Speicher.
Nun richtet Delphi in der Regel an 32 Bit (4 Byte) Grenzen aus.
Somit habe ich den notwendigen Speicher mit GlobalAlloc reserviert (hier soll nach SDK auf jeden Fall an 8 Byte Grenzen ausgerichtet werden).
Jetzt mußte ich feststellen, dass meine Recordausrichtung zwar auf 32 Bit erfolgt, aber der Lesefehler trotzdem bleibt. Nach mühsammen debuggen mußte ich feststellen, dass mit dem Methodenaufruf aus der DLL die Structure an eine Speicherstelle kopiert wird die nicht an 8 Byte Grenzen ausgerichtet ist.

Jetzt kommt der absolute Spaß!!!!!!!!!

Füge ich in meinem Quellcode zusätzliche Codezeilen ein (müssen nichts mit benannter Structure zu tun haben) klappt auch mall alles prima.

Was ist da los?

Wie kann ich meinen Compiler überreden Speicher automatisch an 8 Byte Grenzen auszurichten? Ich bin hier aktuell etwas hilflos und finde zu diesem Thema keine vernünftigen Info's.

Ach ja, ich benutze Delphi 5 Enterprise.

Gruß oki. :cry:

sakura 30. Dez 2002 16:15

Einfach an den Anfang der Units, welche es betrifft
Code:
{$ALIGN 8}
einfügen. Wenn der Compiler das nicht schluckt, dann hast Du leider Pech. Der Schalter ist in D7 beschrieben, ob D5 den schon hat, ich glaube leider nicht.

Brüggendiek 30. Dez 2002 16:23

Hallo!

Zitat:

Zitat von sakura
Der Schalter ist in D7 beschrieben, ob D5 den schon hat, ich glaube leider nicht.

D5 Standard kennt nur {$ALIGN ON} / {$A+}: Record-Werte ausrichten bzw. {$ALIGN OFF} / {$A+}: Record-Werte nicht ausrichten, also alle RECORDs als PACKED speichern. Voreinstellung: {$A+}.

Leider!

Gruß

Dietmar Brüggendiek

Christian Seehase 30. Dez 2002 18:25

Moin Oki,

also dass man auf Grund eines falschen Alignments CPU Strafzyklen erhält hab' ich ja schon gehört, aber das die Daten auf eine bestimmte Adresse ausgerichtet sein müssen, damit der Zugriff überhaupt erfolgen kann ist mir neu.

Diese Ausrichtung soll ja eigentlich nur dafür sorgen, dass die CPU das Programm schneller abarbeiten kann, wenn die Daten an einer Grenze liegen die der generischen Adressbus Breite der CPU entspricht, was bei einem 8 Byte Alignment ja eindeutig auf eine 64 Bit CPU hindeutet.

Ausser die DLL geht mit übergebenen Pointern sehr eigenwillig um ( ;-)) dürfte das nicht zu einem Fehler führen, sondern schlimmstenfalls zu einer langsameren Abarbeitung des Programmes.

Kann es sein, dass der von Dir benutzte Record als record und nicht als packed record deklariert ist?

oki 30. Dez 2002 18:51

Erst mal herzlichen Dank für die schnellen Antworten.

Leider stimmt es, dass D5 die Direktive ALIGN 8 nicht kennt. Ich werde es trotzdem einfach mal probieren.

Leider ist es wirklich so, dass die Daten an einer 8 Byte Ausrichtung erwartet werden. Ich habe es im Debuger geprüft. Bei einem Versatz der Structure um 4 Byte im Speicher wird beim kopieren das erste DWord einfach ignoriert. Mein Kontakt hat mir außerdem berichtet, dass Visual C mit Sicht auf 64 Bit-Processoren grundsätzlich eine 64 Bit-Ausrichtung erwartet.
Ob das alles so korrekt ist weiß ich nicht. Vielleicht ist die Speicherbearbeitung auch wieder einmal so'n komisches C-Programmiererteil.
Mein Problem ist aber nachweisbar das beschriebene.

Vieleicht hat ja noch einer 'ne Idee.

Bis dann Olaf

Christian Seehase 30. Dez 2002 23:48

Moin Olaf,

Zitat:

dass Visual C mit Sicht auf 64 Bit-Processoren grundsätzlich eine 64 Bit-Ausrichtung erwartet.
Also ich kann mir vorstellen, dass das als Defaulteinstellung gilt, aber nicht, dass es unabänderlich ist. (hab' leider gerade keine VC zur Hand)

So wie es aussieht scheint's da wohl nur zwei Lösungsansätze zu geben:
  1. Du steigst auf D7 um damit Du die DLL sicher benutzen kannst. (wenn's denn mit Align 8 wie gewünscht geht)
  2. Die DLL wird mit 32 Bit Alignment neu kompiliert.

oki 31. Dez 2002 14:33

Hallo Christian,

Dank für die Antwort. Ich werd mal versuchen den C-Programmierer zu beschwatzen mir ein neues Compiling zu schicken bevor ich Geld für D7 in die Hand nehme.

Gruß Olaf

OregonGhost 31. Dez 2002 15:55

Kannst du nicht einfach die struct, Verzeihung, den record packed machen und die zur 8-Byte-Ausrichtung fehlenden Bytes einfach als leere Bytes hinzufügen? Wenn ich das Problem richtig verstanden habe :roll: , müsste das doch so gehen...

Leider kann man zumindest in VC6.01 SP4 nur 1 bis 16 Byte als struct-Elementausrichtung einstellen...

Christian Seehase 31. Dez 2002 17:45

Moin Oregon Ghost,

wenn eine Ausrichtung von 1 bis 16 einstellbar ist, ginge doch auch 4 oder nicht?

oki 1. Jan 2003 17:36

Hallo ihr alle zusammen.

Für mein erstes Problem habe ich eine Lösung gefunden (der erste Eindruck ist jedenfalls so). Ich habe für den Record einfach ausreichend Speicher mittels GlobalAlloc auf einen Zeiger vom Typ PChar reserviert. Laut SDK soll GlobalAlloc Adressen in einer 8 Byte-Ausrichtung reservieren. Die entsprechenden Einträge habe ich dann mittels move-Befehl an die entsprechende Stelle Zeiger[x] kopiert.
Das sah alles erstmal richtig gut aus.

Ergebnis der Aktion -----------> Fehlermeldung aus der DLL!!!

Jetzt habe ich mich mühsam mittels CPU-Debugger durch den Code gequält und folgendes festgestellt.
Mein Record wird ordnungsgemäß angelegt und an die DLL-Methode übergeben. Die DLL-Methode legt einen eigenen Speicherbereich an der aber jetzt nicht an einer 8 Byte Grenze ausgerichtet ist!!!! :witch: Jetzt wird mein Speicherinhalt mit einer Ausrichtung an einer 8 Byte-Adresse in diesen Speicher kopiert. Somit entsteht ein Versatz von 4 Byte.

Jetzt kommt meine doofe Frage:

Das passiert doch alles in der DLL. Was habe ich damit zu tun?
Muscht der Delphi-Compiler irgentwo rum, dass der an die Basisadresse geladene DLL-Code Mist baut?
Ich weiß, dass sich das jetzt alles ganz schön blöd anhört, aber ich habe C-Beispielcode bekommen, bei dem alles super mit der DLL klappt.

Vielleicht hat ja noch einer eine Idee.

Übrigens vielen Dank für die Tips mit dem Auffüllen und den packed record's. Hab ich leider vorher schon probiert (sicher erfolglos weil das Problem woanders steckt).

Gruß Olaf

jbg 1. Jan 2003 18:19

Den C Beispiel-Code (zumindest das Interessante) könntest du doch mal posten. Deine Delphi deklaration sowie die Einbindung der Funktion (function ... external 'dllname.dll') wäre auch nicht schlecht. Dann würde diese Diskussion nicht nur auf theoretischer Basis ablaufen.

oki 1. Jan 2003 20:00

Erst mal an alle die mir bei meinem blöden Problem helfen Dank!!!! :oops: :oops:

Ich habe jbg gerade den Code in das private Postfach gestellt. Da die DLL's lizensrechtlich geschützt sind möchte ich das Ganze nicht so doll öffentlich machen. Sorry :oops: :oops: !!

Wer mir sonst noch weiter helfen kann soll sich melden. Ich sende dann den Code auch dort hin.

So, dann Dank, Dank, Dank bis hierhin

Gruß Olaf

jbg 1. Jan 2003 21:28

Zitat:

Zitat von oki
Ich habe jbg gerade den Code in das private Postfach gestellt.

Da kam nichts an.
Ich wollte auch nicht, dass du gleich die ganze DLL schickst, sondern aus dem C Beispiel nur die Deklaration der Struktur und den Aufruf sowie die Dekl. der Funktion. Dasselbe auch von deinem Delphi Beispiel.

jbg 2. Jan 2003 15:07

Ich habe den Fehler gefunden. Und zwar liegt es an der Übersetzung des Typs BOOL nach Boolean. Der BOOL Typ ist als int deklariert und somit 32 Bit groß. Die Entsprechung in Delphi ist LongBool (32 Bit) und nicht Boolean (8 Bit). Also einfach in der Datei MapDefs.pas ein Suchen/Ersetzen machen: Boolean -> LongBool

oki 2. Jan 2003 18:20

Hallo jbp,

wenn das stimmt gebe ich einen aus. Ich prüf das gleich.

Gruß Olaf

oki 2. Jan 2003 18:32

Hallo jbp,

ähhhm, nach dem Motto suche einen Fehler und du findest 10. Ich habe Deinen Vorschlag gerade umgesetzt. Das hat meinen Fehler aber leider nicht beseitigt.

Sag mal kurz bescheid ob du das Projekt mal ausprobiert hast und mit welcher Delphi-Version.

Gruß Olaf

jbg 2. Jan 2003 21:28

Ausprobiert habe ich es unter Delphi 6 Pro und WinXP Pro. Das Programm läuft soweit, dass es ohne dass ein Button gedrückt wird, ohne Fehler startet. Also den wahren Fehler habe ich noch nicht gefunden. Aber so weit wie ich gerade bin, liegt es nicht an der Ausrichtung der Felder, denn die ist auch bei C++ trotz ALIGN:8 nicht anders als die von Delphi.

oki 3. Jan 2003 08:39

Hallo jbg,

herzlichen Dank für Deinen Code zu meinem Problem. Ich habe diesen mal ausprobiert. Dazu mußte ich aber die Direktive {$Align 8} auskommentieren da D5 diese nicht kennt. Das Setzen der Objecte klappt anstandslos. Beim Verbinden bekomme ich den Fehler Lesen von Adresse $FFFFFFFF.
Da Du auch diese Methode verändert hast gehe ich davon aus, dass sie bei Dir läuft.
Ich bin jetzt immer mehr davon überzeugt, dass ich auf mind. D6 updaten muß um die Sache in den Griff zu bekommen.

Meine getesteten BS sind übriegens Win98 und 2000. Bei beiden mein Fehler.

Gruß Olaf

jbg 3. Jan 2003 19:04

Ich habe die beiden Buttons natürlich einzeln getestet. Da kommt kein Fehler. Du musst folgendes einfügen, damit es ohne AV funktioniert.
Delphi-Quellcode:
procedure TForm1.MakeCon(Anzahl: Integer);
...
begin
  Viewer := MapForm.Viewer;
  if Anzahl < 2 then Exit;

  // alle bestehenden Temp. Objecte entfernen
  Viewer.RemoveAllTempObjects;  <----
  Viewer.RemoveAllConnections;  <----
[/quote]

oki 3. Jan 2003 20:46

Hallo jbg,

ich hab die ganze Zeit gegrübelt was Du mir beibringen willst. Da ich Dir die Doku zur DLL nicht mit zugestellt habe reden wir beide wahrscheinlich gerade aneinander vorbei. Mit der Methode MakeCon werden Verbindungslinien zwischen temp. Objekten gezogen. Sind diese nicht vorhanden, so wird keine Linie gezeichnet.

Mein Testbeispiel ist hier natürlich nicht sehr plausibel. Du mußt zuerst eine Anzahl von Objekten setzen und kannst diese dann mittels MultitempConnect verbinden. Die Elemente gelten wie folgt:
Delphi-Quellcode:
  TMultiTempConnect = record
    mtc_startid : UINT;              // Ausgangsobjekt der Linie
    mtc_connection : TTempConnect;   // Struktur mit Zielobjekt und                                            
                                      // Verbindungsattributen
Die ID wird beim Setzen der Objekte (SetTemoObjectExt) festgelegt. Im Normalfall sollte sie eindeutig sein (beim Test erst mal vernachlässigt, ist hierfür alles nicht so wichtig; wird dann alles in meiner eigentlichen Applikation berücksichtigt).

Also, der Normalablauf ist so:

- Setzen einer Anzahl von Objekten mittels SetTempObjectExt,
- Verbinden der Objekte mittels AddMultiConnections
- neu zeichnen der Karte mittels Redraw zur Anzeige der Verbindungslinien.

Dann auf ein Neues.

Gruß Olaf

jbg 3. Jan 2003 22:34

Also eine Verbindung zwischen 2 TempObjects bekomme ich hin. Wenn es mehrere werden, dann kommt nur eine AV. Hast du ein funktionierendes C(++) Beispiel für die Verbundungen. Denn der Beispiel C++ Code, den du mir geschickt hast, fügt nur TempObjects hinzu und entfernt sie wieder.

jbg 3. Jan 2003 23:43

Liste der Anhänge anzeigen (Anzahl: 1)
Eines würde mich mal interessieren. Wer hat denn diese Header-Übersetzung geschrieben? Der "BOOL -> Boolean" Fehler ist noch der kleinste. Was ich gerade feststellen musste, ist, dass Delphi die Aufzählungstypen in 1 Byte quetscht, wohingegen C++ 4 Bytes beansprucht. Somit stimmt der Typ TTempConnect nicht mehr. Nachdem ich die Headerdatei überarbeitet habe. Läuft das Verbindung Setzen reibungslos.

Da du schriebst, dass du Delphi 5 einsetzt, war es natürlich nicht gerade leicht, eine geeignete Möglichkeit zu finden, einen Aufzähungstyp auf 4 Byte zu erweitern. Unter Delphi 6 ginge dies ganz einfach so:
Delphi-Quellcode:
TMapColorIndex = (MCI_..., ..., MCI_TRIM_TO_32BIT=$0fffffff);
Unter Delphi 5 geht dies nicht. Und da ich keine Lust hatte für jeden Wert eine Konstante mit laufender Nummer zu schreiben (bei 270 Zeilen schreibe ich mir ja die Finger wund). habe ich einen kleinen Zwischen Record gebaut.
Delphi-Quellcode:
_TMapColorIndexType =
  (MCI_BUILDUP_INNER,
  MCI_WATER_INNER, ...);

TMapColorIndex = packed record
  case Integer of
    0: (Value: _TMapColorIndexType);
    1: (Int: Integer);
end;
Setzen kann man nun die einzelnen Werte über das Feld Value:
Delphi-Quellcode:
var m: TMapColorIndex;
m.Value := MCI_BUILDUP_INNER;

oki 4. Jan 2003 13:19

Hallo jbg,

ich habe gerade einen roten Kopf bekommen.

Die Header-Datei habe ich übersetzt. Vielleicht können wir das noch ein bischen runterspielen damit ich nicht ganz so dumm dastehe.

Ich habe leider nie in C programmiert und so sind meine Kenntnisse auch im Bereich der Deklarationen für C sehr stümperhaft.

Zu meiner Ehrenrettung sei gesagt, dass ich meine übersetzte Headerdatei den C-Leuten zukommen lassen hab. Die haben alles für o.k. befunden. Offensichtlich sind die in Delphi genauso blass wie ich in C.

Beispiel-Code für Verbindungen in C habe ich leider nicht. Ich denke aber der tut nach deiner zweiten Nachricht wohl nicht mehr zur Sache.

Jetzt dann mal eine Frage hinterher:

Kann mann den Eintrag im Record nicht als DWord-Typ deklarieren und bei der Verwendung über Type-casting füllen?
Somit würde ich einfach folgendes tun:

toc_collor := DWord(MCI_ROUTE_NORMAL);

o.k. schon mal unendlichen Dank. Ich teste mal den Rest, bis dann

Olaf[/delphi]

jbg 4. Jan 2003 13:23

Das kann man schon. Nur wollte ich genau dieses Typecasten verhindern. Aber wenn es dir gefällt, dann musst du nur die Deklarationen abändern.

oki 5. Jan 2003 15:18

Hallo jbg,

ich schreibe gerade meine Quellen um und räume in meinem Programm etwas auf.
Deine Quellen habe ich getestet und sie laufen auch bei mir. Komischerweise wird bei mir aber nicht der eingefügte Text über den Verbindungslinien ('Start', '1', ...) angezeigt. Offentsichtlich ist da noch was im Busch!
Ich melde mich noch mal wenn ich mit meinem Standardprogramm weiter bin.

Gruß Olaf


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz