| Autor |
Beitrag |
Ra2k
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: Sa 26.03.05 22:21
Hi folks,
ich habe in C++ eine DLL geschrieben - diese enthält zum einen eine abstrakte Basis-Klasse und eine abgeleitete. Da ich in C++ mit Namespaces und Klassen arbeite, und die Funktionen über die C++ Direktive " _declspec( dllexport )" exportiere, entstehen komplizierte Export-Symbole wie bspw. " ?Init@GePackage@Core@GEL@@QAEJXZ" - Das ist an und für sich nicht das Problem, die Deklarationen in Delphi sind dann nur ziemlich lang - So sieht meine Klassen Deklaration in Delphi aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| type GePackage = class public constructor Create(); overload; constructor Create( szFileName: PChar ); overload;
destructor Destroy(); virtual;
class function Init(): longint; cdecl; class procedure Release(); cdecl;
class function Open( szFileName: PChar ): longint; virtual; abstract; class function Close(): longint; virtual; abstract; ...
constructor GePackage.Create(); external 'core.dll' name '??0GePackage@Core@GEL@@QAE@XZ'; constructor GePackage.Create( szFileName: PChar ); external 'core.dll' name '??0GePackage@Core@GEL@@QAE@PBD@Z';
destructor GePackage.Destroy(); external 'core.dll' name '??1GePackage@Core@GEL@@UAE@XZ';
class function GePackage.Init(): longint; external 'core.dll' name '?Init@GePackage@Core@GEL@@QAEJXZ'; ... |
Und dies hier ist mein Aufruf im Programm:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| var frmMain: TfrmMain; kPackage: GePackage;
...
procedure TfrmMain.FormCreate( Sender: TObject ); var szBuffer: string; begin szBuffer := 'data.gpf'; kPackage := GeGillianPackage.Create( PChar( szBuffer )); end; |
Ich habe alle Fehler ausgemerzt, in denen die Rede von "Prozedureinsprungspunkten" waren, welche nicht gefunden werden konnten. Jetzt begrüßt mich jedoch ein hässlicher "EAbstractError" und ich bin durch mit meinem Delphi Latein.
Hier die genaue Meldung:
| Zitat: | ---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt packager.exe ist eine Exception der Klasse EAbstractError aufgetreten. Meldung: 'Abstrakter Fehler'. Prozess wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen.
---------------------------
OK Hilfe
--------------------------- |
Ja, das hat mich zu euch geführt. Hoffe Ihr könnt mir helfen  Danke im voraus!
Sebastian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 08:36
Wenn die Fehlermeldung sinnvoll ist, bedeutet das, das du uns mal die abgeleitete Klasse und den darin ggf. aufgerufenen Teil der Basisklasse zeigen musst. Du hast in der Basisklasse zwei abstrake Methoden deklariert, und aus den Schnipseln lässt sich nicht erkennen, wann die aufgerufen werden.
Möglich wäre z.B. dass du im Konstruktor der abgeleiteten Klasse explizit den der Basisklasse aufrufst, und darin dann Aufrufe der vermeindlich überladenen Methoden zu Aufrufen der abstrakten Methoden führen.
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 12:33
wdbee hat folgendes geschrieben: | | Möglich wäre z.B. dass du im Konstruktor der abgeleiteten Klasse explizit den der Basisklasse aufrufst, und darin dann Aufrufe der vermeindlich überladenen Methoden zu Aufrufen der abstrakten Methoden führen. |
Hi wdbee! Nun ja, vllt hätte ich noch hinzufügen sollen, dass die DLL in C++ Programmen funktioniert und ich sie seit dem nicht mehr verändert, weil einfach kopiert, habe. Ich habe die DLL auch mit Microsoft VisualC++ geschrieben (sollte das irgendwelche unterschiede zu CBuilder machen).
Aber zu Deiner Bitte der abgeleiteten Klasse:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| type GeGillianPackage = class( GePackage ) public constructor Create(); overload; constructor Create( szFileName: PChar ); overload;
destructor Destroy();
class function Open( szFileName: pchar ): longint; stdcall; class function Close(): longint; stdcall;
class function Flush(): longint; stdcall; ...
constructor GeGillianPackage.Create(); external 'core.dll' name '??0GeGillianPackage@Core@GEL@@QAE@XZ'; constructor GeGillianPackage.Create( szFileName: PChar ); external 'core.dll' name '??0GeGillianPackage@Core@GEL@@QAE@PBD@Z';
destructor GeGillianPackage.Destroy(); external 'core.dll' name '??1GeGillianPackage@Core@GEL@@UAE@XZ';
class function GeGillianPackage.Open( szFileName: pchar ): longint; external 'core.dll' name '?Open@GeGillianPackage@Core@GEL@@UAEJPBD@Z'; ... |
Im Constructor der abgeleiteten Klasse, wird allerdings nur die Open() Methode aufgerufen, die aber wiederum in der abgeleiteten Klasse definiert wurden. Und da es in C++ Programmen läuft, denke ich nicht das hier der Fehler liegt.
Im Delphi Debugger wird jedoch eine Adresse angegeben, welche im Standard-Constructor der Basis-Klasse liegt. In diesem Constructor wird eine Instanz einer Klasse (GeLog) erstellt, diese Klasse ist mit einer statischen LIB in die DLL gelinkt worden. Als weiteres enthält dieser Constructor nur noch den Aufruf der Init() Methode, welche die Initialisierung der Klassen-Attribute beinhaltet.
Sollte sich hier jemand noch genauer mit Assembler Symantic auskennen, würde ich ein paar Zeilen aus dem Debugger posten.
Wenn sonst noch was fehlt, einfach schreien
Sebastian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 12:46
Ra2k hat folgendes geschrieben: |
Im Delphi Debugger wird jedoch eine Adresse angegeben, welche im Standard-Constructor der Basis-Klasse liegt. |
Das ist doch genau das, was ich vermutet habe. Du kannst dir die Fehlersuche erleichtern, indem du aus den abstrakten Methoden in der DLL mal leere Methoden machst. Wenn du genau wissen willst, was wann von wem tatsächlich aufgerufen wird, dann schreib in alle Funktionen zu Beginn und zum Ende per Append in eine Datei den Datei/Funktionsnamen und ggf. auch Parameter. Anhand der Datei findest du sicher am schnellsten heraus, was da tatsächlich abläuft. Wenn du das mit Compilerdirektiven klammerst kannst du es an einer Stelle aktivieren/deaktivieren.
Das Ergebnis wird sein, dass es nicht so abläuft wie du es erwartest. Ich vermute da Probleme mit den Tabellen der virtuellen Methoden.
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 12:57
wdbee hat folgendes geschrieben: | | Du kannst dir die Fehlersuche erleichtern, indem du aus den abstrakten Methoden in der DLL mal leere Methoden machst. |
Mit den abstrakten meinst Du doch jetzt die der Basis-Klasse 'gell? Also falls ja, diese sind schon leer, sprich mit "= 0;" deklariert.
Na jut Okay, ich werd mal 'ne kleine I/O Append Session starten
Ach und danke für die rasche Antwort!
Sebastian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 13:05
Ich meine alle abstrakten Methoden. Auch die sollen sich im Logfile verewigen! Nur dann siehst du ja, das da was falsch abgebildet wird. 
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 13:27
wdbee hat folgendes geschrieben: | Ich meine alle abstrakten Methoden. Auch die sollen sich im Logfile verewigen! Nur dann siehst du ja, das da was falsch abgebildet wird.  |
Na das ist ja sonderlich, ich habe jetzt aus den virtuellen abstrakten Methoden, einfach nur noch virtuelle gemacht in denen jeweils ihr Funktionsname, die Zeilennummer und Quellcodedatei geloggt wird. Wenn ich mein Delphi Programm starte, wird nicht mal eine "log.txt" erstellt. Wenn ich mein kleines C++ Test Programm anwerfe, und ein paar Methoden (oben nicht aufgelistet außer Open(), Close & Flush) aufrufe, loggt er mir nur diese Zeilen:
| Zitat: | GEL::Core::GePackage::Init (96 .\src\GePackage.cpp)
GEL::Core::GePackage::GePackage (42 .\src\GePackage.cpp) |
Mich wundert an dieser Stelle, warum der Constructor erst nach der Init() geloggt wird, der Constructor ist der einzige "Typ" der die Init() selbst aufruft, und der Log-Eintrag steht im Constructor auch vor dem Init() Aufruft *konfus*
Führe ich das C++ Programm einfach nur aus und schließe es (das beinhaltet beim Starten die Instanzierung inkl. Init(); dann Open() und vor dem Beenden Close(); und beim Beenden den Constructor inkl. Release(). Also ohne mit dem Package zu arbeiten, wird dann diese Log-File erstellt:
| Zitat: | GEL::Core::GePackage::Init (96 .\src\GePackage.cpp)
GEL::Core::GePackage::GePackage (42 .\src\GePackage.cpp)
GEL::Core::GePackage::Release (129 .\src\GePackage.cpp)
GEL::Core::GePackage::~GePackage (74 .\src\GePackage.cpp) |
Und naja, von den Log-Einträgen der virtuellen Methoden fehlt auch jede Spur, was doch aber auch irgendwie logisch ist oder nicht? Die abgeleitete Klasse überschreibt doch die virtuellen Methoden..
Sebastian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 14:26
Habe ich das richtig verstanden: Du hast jeweils ein Paar von Einträgen in allen Routinen, einen ganz am Anfang und einen ganz am Schluss? Aber du bekommst nur eine Zeile pro Funktion! Schau noch mal nach, ob du wirklich am Anfang eine Zeile ausgibst. Wenn nicht und du führst Init aus dem Konstruktor heraus aus, dann würde das doch stimmen.
Create >>>(kéin Text)
Init >>> (kein Text)
Init <<< (Text)
Create <<< (Text)
Aber jetzt kommt keine (Abstrakte) Fehlermeldung mehr?
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 16:57
wdbee hat folgendes geschrieben: | | Aber jetzt kommt keine (Abstrakte) Fehlermeldung mehr? |
Korrekt, also der Abstrakte Fehler da.. lag wohl daran, dass ich die Constructor und Destructor in der Delphi Deklar. vergessen hatte, im ersten Post aber noch angefügt.
Nun also, ich habe nur am Anfang jeder Methode eine Log Message, bei den virtuellen Methoden, die ja so und so leer sind, macht das mit am Anfang und Ende ja keinen Sinn, aber was den Constructor angeht könnte ich das grad mal machen.. Moment *g*
Also hier die Log, die "{" und "}" sind ja selbstklärend..
| Zitat: | GEL::Core::GePackage::Init { - 99 .\src\GePackage.cpp
GEL::Core::GePackage::Init } - 121 .\src\GePackage.cpp
GEL::Core::GePackage::GePackage { - 42 .\src\GePackage.cpp
GEL::Core::GePackage::GePackage } - 49 .\src\GePackage.cpp
GEL::Core::GePackage::Release { - 133 .\src\GePackage.cpp
GEL::Core::GePackage::Release } - 158 .\src\GePackage.cpp
GEL::Core::GePackage::~GePackage { - 76 .\src\GePackage.cpp
GEL::Core::GePackage::~GePackage } - 86 .\src\GePackage.cpp |
Aber es ist immernoch falsch.. wenn im Constructor GePackage::GePackage der Aufruf zu GePackage::Init stattfindet, wie kann Init dann schon vorher fertig ausgeführt sein?
Edit:
Das beißt sich nämlich mit meiner eigentlichen Log File (s.o. GeLog), die sieht so aus:
| Zitat: | [18:03:49 Systm] ### Package System is online!
[18:03:49 Systm] Init()
[18:03:49 Systm] Init() succeeded
[18:03:49 Systm] Open()
[18:03:49 Systm] Info: Package "runtime.gaf" has been created
[18:03:49 Systm] Open() succeeded
[18:03:52 Systm] AddGroup()
[18:03:52 Systm] AddGroup() succeeded
[18:03:54 Systm] AddFile()
[18:03:54 Systm] Info: Buffer size of "piano.wav": 495134 Bytes
[18:03:54 Systm] AddFile() succeeded
[18:03:57 Systm] Close()
[18:03:57 Systm] Close() succeeded
[18:03:57 Systm] Release()
[18:03:57 Systm] Release() succeeded
[18:03:57 Systm] ### Package System is offline! |
diese ### Messages sind im Constructor bzw. Destructor, was meint: In diesem Bsp. wird Init() erst nach dem Constructor ausgerufen, da funktioniert doch was nich richtg...
Sebastian
Zuletzt bearbeitet von Ra2k am So 27.03.05 17:35, insgesamt 4-mal bearbeitet
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 17:06
Ich weiß nicht, wie du deine Konstruktoren in C++ geschrieben hast und ob du mit Optimierung kompiliert hast. Wenn der Kompiler da etwa in der Richtung "Initialisierung statt Zuweisung" optimiert, wer weiß.
Aber jetzt die spannende Frage: Wenn du das unter C++ laufen lässt, sieht das genauso aus? Oder war das C++ und unter Delphi geht es noch nicht?
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 17:12
wdbee hat folgendes geschrieben: | | Aber jetzt die spannende Frage: Wenn du das unter C++ laufen lässt, sieht das genauso aus? Oder war das C++ und unter Delphi geht es noch nicht? |
Also in Delphi geht garnichts, da erstellt er nicht einmal die log.txt, in C++ läuft alles einwandfrei, ich denke es liegt nur noch an der Delphi Deklaration, sprich an dem Import..
Ach und btw., siehe einen Post von mir weiter oben, der Edit!
Edit:
Hier einmal der Constructor in C++:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| GePackage::GePackage( void ) { file *log = fopen( "log.txt", "a+" ); fprintf( log, "%s { - %i %s\n", __FUNCTION__, __LINE__, __FILE__ );
m_pkLog = new GeLog( "Systm", "core.log", true ); mlog( "### Package System is online!" );
Init();
fprintf( log, "%s } - %i %s\n", __FUNCTION__, __LINE__, __FILE__ ); fclose( log ); log = NULL; } |
Sebastian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: So 27.03.05 17:46
Ok, dann denke ich folgendes: Verwendest du in deiner DLL zufällig mehrere Threads und/oder hast HyperThraeding aktiv? Dann müsstest du dein Protokoll noch mit CriticalSections und FlushBuffer nachrüsten, um sicher zu sein, dass alles so in der Datei steht, wie es aufgerufen wurde.
Aber da es unter C++ läuft, denke ich wir sollten am anderen Ende weitersuchen. Ich sehe folgende Fehlerquellen:
Falsche Zuordnung der Funktionen durch fehlerhafte Interpretation des Namemanglings
Abweichende VMT bei Delphi und MS-C++
Unterschiedliche Speicherbereiche in DLL und EXE
Falsche Aufrufkonvention (stdcall, etc.)
Eine Möglichkeit sich das Leben einfacher zu machen, wäre das Namemangling durch extern C-Deklaration zu ersetzen und auf Overload zu verzichten.
Dann wäre es möglich, sich die Adressunterschiede zwischen den einzelnen Funktionen der DLL unter Delphi und C++ ausgeben zu lassen. Damit sollten ggf. Verwechselungen erkennbar werden.
Das gleiche gilt für die VMT. Einfach mal unter C++ und unter Delphi die Zeiger in der VMT und auf die VMT kontrollieren.
Die Aufrufkonvention sollte auf beiden Seiten gleich und möglichst stdcall sein.
Wenn da alles stimmt, dann hast du wohl das Problem, dass die Speicherbereiche der DLL und der EXE nicht zusammenpassen.
|
|
Ra2k 
Hält's aus hier
Beiträge: 8
WinXP Build 2600 SP2
D7 Prof
|
Verfasst: So 27.03.05 18:05
wdbee hat folgendes geschrieben: | | Eine Möglichkeit sich das Leben einfacher zu machen, wäre das Namemangling durch extern C-Deklaration zu ersetzen und auf Overload zu verzichten. |
Das möchte ich nur sehr ungern machen, weil mir das viel zu viel Arbeit machen würde.. bevor ich mich detailierter mit deiner Liste auseinandersetze, hier nochmal ein Hint von mir den ich ebend erst entdeckt habe:
Im Debugger kommt der Fehler direkt zwischen dem Aufruf des Constructors an sich, und dem Sprung zur Init() Methode. Obwohl Moment, wenn er auch keine log.txt erstellt, kann der Fehler auch nicht an meiner GeLog Klasse liegen, also eher im Aufruf bzw. der Initialisierung des Constructors.. was mich dann doch wieder auf Deine Liste führt.
Nun ja, wie vergleiche ich Zeiger in und auf dem VMT? Hab sowas noch nie gemacht..
Und Aufrufkonventionen, in C++ ist ja cdecl standardmäßig; Ich könnte ja mal ein paar Methoden zu stdcall's machen und sehen was geht..
Sebastian
|
|
|