![]() |
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: |
Einfach an den Anfang der Units, welche es betrifft
Code:
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.
{$ALIGN 8}
|
Hallo!
Zitat:
Leider! Gruß Dietmar Brüggendiek |
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? |
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 |
Moin Olaf,
Zitat:
So wie es aussieht scheint's da wohl nur zwei Lösungsansätze zu geben:
|
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 |
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... |
Moin Oregon Ghost,
wenn eine Ausrichtung von 1 bis 16 einstellbar ist, ginge doch auch 4 oder nicht? |
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 |
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.
|
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 |
Zitat:
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. |
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
|
Hallo jbp,
wenn das stimmt gebe ich einen aus. Ich prüf das gleich. Gruß Olaf |
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 |
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.
|
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 |
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:
[/quote]
procedure TForm1.MakeCon(Anzahl: Integer);
... begin Viewer := MapForm.Viewer; if Anzahl < 2 then Exit; // alle bestehenden Temp. Objecte entfernen Viewer.RemoveAllTempObjects; <---- Viewer.RemoveAllConnections; <---- |
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:
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).
TMultiTempConnect = record
mtc_startid : UINT; // Ausgangsobjekt der Linie mtc_connection : TTempConnect; // Struktur mit Zielobjekt und // Verbindungsattributen 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 |
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.
|
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:
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.
TMapColorIndex = (MCI_..., ..., MCI_TRIM_TO_32BIT=$0fffffff);
Delphi-Quellcode:
Setzen kann man nun die einzelnen Werte über das Feld Value:
_TMapColorIndexType =
(MCI_BUILDUP_INNER, MCI_WATER_INNER, ...); TMapColorIndex = packed record case Integer of 0: (Value: _TMapColorIndexType); 1: (Int: Integer); end;
Delphi-Quellcode:
var m: TMapColorIndex;
m.Value := MCI_BUILDUP_INNER; |
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] |
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.
|
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