Frage zu DLLs / Objekten und ihrer Gültigkeit.
-
Servus!
Auch wenns viel ist. Bitte lesen. Meine Diplomarbeit hängt mehr oder weniger davon ab.
Ich versuchs kurz und knackig:
Ich programmiere mit Borland.
Ich verwende eine Bildverarbeitungslibrary namens Halcon.
Ich habe DLLs, Libs und die Header für die CPP Libs.
Problem: Halcon funktioniert nicht mit Borland! Auch wenn ich die Libs mit implib neu erzeuge.
IDS/MVTech geben keinen Support. Da Borland seit X Versionen schon nicht auf die Problemanfragen reagiert (Wieso hör ich das ständig???)
Ich programmiere hier seit Monaten an einer Anlagensteuerung unter Borland. Mit der VCL. 80% Meiner Software ist VCL basiert.
Und jetzt 6 Wochen vor Ende der Big Bang. Halcon, der Kern der BV, läuft nicht. Nirgends steht das... Nirgends...
Meine Idee: Ich kann zwar mit den Halcon Klassen nicht arbeiten in Borland... Aber ich kann doch DLLs einbinden.
Ich packe meine Halcon funktionalität unter VS in eine DLL und benutze die dann in Borland. Sollte doch gehen oder?
Jetzt die Frage:
Ich habe mehrere Threads. Einer acquiriert das Bild. Einer macht die Vorverarbeitung und einer die Auswertung. Alle müssen mit Halcon Komponenten arbeiten.
Geplant war, daß ich die Adressen der Objekte hin und her schaukle. So ein 6500x640 Bild ist halt nicht mal eben kopiert.
ABER ich kann ja nicht auf die Klassen zugreifen.
Jetzt war mein Gedanke INNERHALB der DLL zu casten. Also Funktion der DLL liefert eine Adresse als integer return. Einen Integer kann ich in Borland ja schaukeln. Den Integer Wert gebe ich einem anderen Thread. Der wirft in der DLL eine Funktion an, der er den Integer übergibt. Die Funktion castet den dann eifnach zur Adresse eines Bildobjekts. Ende. So schaukel ich bisher auch immerwieder mal Objektaadressen in MEssages durch die Pampa.
Eben das Böse Erwachen. Halcon hat eine böse Garbage Collection. Die räumt auf oO bin ich ja gar nicht gewohnt. Am Ende einer Funktion werden alle lokalen Variablen geräumt.
Halcon exportiert prozeduralen Code.... Seis drum. Ich hab mal probiert:
void FunktionA () { HImage* bild = new HImage(); //AUfnahme int adresse = (int) Himage; return adresse; }void FunktionB ( int param) { HImage* auchnbild = (HImage*) param; //Verarbeitung }main() { int uebergabe = FunktionA(); FunktionB(uebergabe); }Nach dem return von A war das Objekt im Arsch. Normalerweise kann ich da fröhlich damit jonglieren, bis ich irgendwann ein delete aufrufe.
Wie deichsle ich das jetzt? Für mich gehts jetzt wirklich um meinen Kragen....
Wie ist denn das mit DLLs? Kann ich irgendwie Objekte erzeugen, die nur in der DLL existieren oder so? Also den Typ HImage kann ich in Borland selbst NICHT verwenden. Aber wenn ich das in einer DLL amche und nur die Funktion aufrufe sollte das gehen.
Aber WIE erzeuge ich in der DLL Objekte, die mehrere Aufrufe überleben?
Also quasi in Borland in ThreadA rufe ich AcquireImage(); auf. Das ist eine der DLL Funktionen, die ein Bild aufnimmt. Wie sorge ich jetzt dafür daß das Objekt des Bilds überlebt, bis ich Calculate() aufrufe?
Kann ich in meiner IDE ein Objekt einer Klasse erzeugen, die in der DLL programmiert ist und deren schnitstellen keinerlei unbekannte Typen beinhaltet, deren private Objekte aber aus einer fremden Bibliothek sind und deren Funktionen ich aufrufe, die fremde Kommandos beinhalten?
SOS? Jemand Bock auf Lebensretter?
Wie allokier ich ein Objekt über ne DLL, jonglier die Adresse abstrakt aus ner Funktion ruas, in ne andre rein ohne daß da aufgeräumt wird inzwischen.
GRRR normal geht das....
TStringList* A= erzeuge();
TStringList* erzeuge()
{
TStringList B=new TStringList();
return B;
}Mit A kann ich nun anstellen was ich will.
Ich muss also mit Funktionen INNERHALB von DLL Code Objekte erzeugen, die zwischen mehreren Funktionsuafrufen gültig bleiben. Also entweder ne globale Variable oder die ADresse rumjonglieren.
-
Dein Problem ist IMHO nicht die DLL, sondern wohl der von Halcon generierte Code, bzw. dein Verständnis davon.
Du kannst aus einer DLL locker einen UINT_PTR oder einen void* oder sonstwas rausgeben damit der Aufrufer sich den merkt, und den dann an anderer Stelle vom Aufrufer wieder entgegennehmen, casten, dereferenzieren, fertig. Alles kein Problem. Probleme könnte es geben wenn du auf einer Seite (DLL) new/malloc machen willst und auf der anderen Seite (EXE) delete/free - aber so wie ich das verstehe muss das ja in deinem Fall nicht gehen.
... in der Doke der Halcon steht doch dass das Teil auch C-Code (ohne ++) generieren kann. Lässt sich der auch nicht mit dem Borland verarbeiten? Oder verstehe ich jetzt was falsch?
Hm...
-
Borland ist insgesamt außergewöhnlich wählerisch, was Kompatibilität zu Bibliotheken betrifft. Zumindest bist zum Builder 6 war das so, und da der jahrelang brach lag, denke ich nicht, dass Borland da in den letzten zwei Versionen so viel dran gerüttelt hat.
-
Wie meinst du das @Hustbaer?
Du kannst aus einer DLL locker einen UINT_PTR oder einen void* oder sonstwas rausgeben damit der Aufrufer sich den merkt, und den dann an anderer Stelle vom Aufrufer wieder entgegennehmen, casten, dereferenzieren, fertig. Alles kein Problem. Probleme könnte es geben wenn du auf einer Seite (DLL) new/malloc machen willst und auf der anderen Seite (EXE) delete/free - aber so wie ich das verstehe muss das ja in deinem Fall nicht gehen.
Von mir aus kann das delete auch in der DLL passieren. Aber sobald ich eine Funktion verlasse räumt Halcon schon freudig auf. Sprich sobald ich UINT_PTR oder void* oder so rausgeb und die Funktion verlass, ist das Objekt im Nirvana... OBWOHL eine, wenn auch anderen Typs, Variable existiert.
Ich müsste also die Ursprungsvariable. also das Objekt irgendwie innerhalb der DLL am Leben halten... Und das kann ich nicht lösen. Da brauche ich dringend Hilfe. Irgendwo hängts da bei mir.
Mein eines Beispiel stammt aus einem von mir erstellten Visual Studio Konsolenprogramm. EInfach mal zum Testen. HImage ist eine Halcon Klasse, die ein Bild beinhaltet.
Ich werfe also eine Funktion an, die mit new ein solches Bild erzeugt. Caste dessen ADresse auf ein INT64. (Halcon arbeitet schon mit 64er Adressierung...
Gebe dieses INT64 mit return zurück. Rufe in der nächsten Zeile eine Funktion auf und gebe ihr das INT64. In dieser Funktion caste ich INT64 wieder als Pointer auf ein HImage. Die Adresse existiert noch. Aber das Bild ist schon aufgeräumt an dieser stelle.
Der gleiche Code mit irgend einem sTL Zeiger... Geht...
Wie löse ich sowas nun innerhalb einer DLL, daß der allokierte Speicher am Leben bleibt...
Deswegen meine Frage mit der Klasse innerhalb der DLL. Wenn ich doch nun eine Klasse habe, mit HImage* m_bild; als private. Im Konstruktor new. Im Destruktor delete.
Und ich nun die Klasse exportiere...... Geht das nun ohne die Halcon Header nochmal in Borland einzubinden (Was ja ned geht)? Aufruf der Klasse ist ja ohne Typen... Nur die Klasse... Die Halcon Typen sind ja in der DLL gekapselt.
Ich werde auf kurz oder lang auf VS umsteigen müssen. Denn da kommen noch Kameratreiber dazu. CAN Treiber etc. Ich hab noch keine gesehen, die auf Borland zugeschnitten waren... Evtl. versuche ich aber dann möglichst plattformunabhängig zu arbeiten. Sprich Boost und wxwidgets oder so statt MFC.
-
OK. Sorry falls dich das jetzt ärgern sollte was ich gleich schreibe, aber naja.
Wie kommst du überhaupt auf die Idee dass diese Halcon Garbage Collection verwendet?
Ich kenne keine Möglichkeit in einem C++ Programm ohne Compilerunterstützung einen INT64 von einem Zeiger unterscheiden zu können. Soll heissen: selbst WENN die Halcon Garbage Collection verwendet müsste diese konservativ sein, und als solche den INT64 für einen "möglicherweise-gültigen-Zeiger" ansehen, und müsste das Objekt stehen lassen. Die "DLL-Grenze" spielt dabei überhaupt keine Rolle. Mir wäre auch keine C++ Library bekannt die soetwas macht, ausser eben spezielle Garbage Collection Libraries die eben Garbage Collection und sonst nix implementieren.Nochwas: die Halcon kann nicht mit 64 Bit Zeigern arbeiten wenn die Plattform nur 32 Bit Zeiger verwendet. Wenn du also ein 64 bittiges Windows und einen 64 Bit Compiler verwendest sind alle deine Zeiger 64 Bit (nicht nur die der Halcon), wenn du ein normales 32 bittiges Windows oder einen 32 Bit Compiler verwendest sind alle deine Zeiger 32 Bit.
Ich vermute das Problem liegt woanders.
Hast du das Bild denn schon einmal im VC Code inspiziert bevor die DLL verlassen wird? Ist es da OK? Hast du schonmal die Zeigerwerte vor dem "rausreichen" und nach dem "reinbekommen" ins Debug-Out geschrieben?
Verwendest du mehrere Threads? Bist du sicher dass das Problem nicht daher kommen kann?Oder hast du denn überhaupt schon versucht ob es denn einen Unterschied macht wenn du den Zeiger in der DLL in einer globalen Variable speicherst?
----
Im Übrigen wäre es wohl besser wenn du Code postest der wirklich so vorkommt, und nicht etwas zusammengeschustertes was sich nichtmal compilieren lässt. Ne void Funktion die einen Integer zurückgibt... sicher nicht.
-
hustbaer schrieb:
OK. Sorry falls dich das jetzt ärgern sollte was ich gleich schreibe, aber naja.
Wie kommst du überhaupt auf die Idee dass diese Halcon Garbage Collection verwendet?
Ich kenne keine Möglichkeit in einem C++ Programm ohne Compilerunterstützung einen INT64 von einem Zeiger unterscheiden zu können. Soll heissen: selbst WENN die Halcon Garbage Collection verwendet müsste diese konservativ sein, und als solche den INT64 für einen "möglicherweise-gültigen-Zeiger" ansehen, und müsste das Objekt stehen lassen. Die "DLL-Grenze" spielt dabei überhaupt keine Rolle. Mir wäre auch keine C++ Library bekannt die soetwas macht, ausser eben spezielle Garbage Collection Libraries die eben Garbage Collection und sonst nix implementieren.DAs Bild ist VOR der Übergabe ok. NACH der Übergabe ist der Inhalt im Eimer.
Nochwas: die Halcon kann nicht mit 64 Bit Zeigern arbeiten wenn die Plattform nur 32 Bit Zeiger verwendet. Wenn du also ein 64 bittiges Windows und einen 64 Bit Compiler verwendest sind alle deine Zeiger 64 Bit (nicht nur die der Halcon), wenn du ein normales 32 bittiges Windows oder einen 32 Bit Compiler verwendest sind alle deine Zeiger 32 Bit.
Ok was ist dann __w64 (Compiler #Option für 64 bit Systeme) und wieso beschwert sich VS wenn ich die Zeiger auf int32 caste, daß ich den Zeiger beschneide mit einer Warnung.
Ich vermute das Problem liegt woanders.
Hast du das Bild denn schon einmal im VC Code inspiziert bevor die DLL verlassen wird? Ist es da OK? Hast du schonmal die Zeigerwerte vor dem "rausreichen" und nach dem "reinbekommen" ins Debug-Out geschrieben?
Verwendest du mehrere Threads? Bist du sicher dass das Problem nicht daher kommen kann?Oder hast du denn überhaupt schon versucht ob es denn einen Unterschied macht wenn du den Zeiger in der DLL in einer globalen Variable speicherst?
Ja, Nein, Nein, Ja definitiv.
Zur Aufklärung: DLL technisch habe ich noch nichts. Meine Versuche mache ich in einem prozeduralen Konsolenprogramm unter VS. Ich will da nicht zuviel Zeit investieren eine DLL zu bauen, bevor ich weiß wie ichs anzustellen habe. Deswegen überhaupt der Thread.
Im Übrigen wäre es wohl besser wenn du Code postest der wirklich so vorkommt, und nicht etwas zusammengeschustertes was sich nichtmal compilieren lässt. Ne void Funktion die einen Integer zurückgibt... sicher nicht.
Ok hast du mal eben 7000€ etwa über für eine Developer Lizens? Dann gebe ich dir gerne laufenden Code. Meinst du ich mach hier aus Langeweile abstrakte Beispiele? Und es tut mir ja leid, daß ich nach 19 Stunden auf Arbeit und nachts um 12 einen kleinen Tippfehler einbaue *grrrr*
-
Und wenn du das Objekt HImage in deinem Hauptprogramm erstellst und nur dessen Zeiger an die DLL übergibst? Dann kannst du in der DLL machen was du willst, der Garbage Collector sollte das Objekt nicht zerstören.
-
HImage --> Include "Halcon.cpp" -->Unresolved Externals --> BCB Inkompatibilität --> Genau das was ich umgehen muss
-
__w64 heisst in dem Fall nicht dass der Zeiger 64 Bit gross ist. Lies es bitte in der MSDN nach wenn du es mir nicht glaubst.
Ich will da nicht zuviel Zeit investieren eine DLL zu bauen, bevor ich weiß wie ichs anzustellen habe.
Ok, verstehe ich.
Ok hast du mal eben 7000€ etwa über für eine Developer Lizens?
Willst du mich verarschen oder willst du (meine) Hilfe?
Dann gebe ich dir gerne laufenden Code. Meinst du ich mach hier aus Langeweile abstrakte Beispiele?
Was weiss ich?
Bloss bringen deine abstrakten Beispiele IMO genau nichts, da ich mir ziemlich sicher bin dass sie den Fehler den du suchst "maskieren". Ich will ja nicht ein riesen Programm welches ich kompilieren kann + Libraries, aber ich erwarte mir dass du ein kleines Testprogramm schreibst wo das besagte Problem auftritt, und dann den Code den DU geschrieben hast (und den du daher auch posten darfst) 1:1 mittels copy & paste hier postest. Genau das KANNST du nicht gemacht haben, da der Code den du gepostet hast nichmal kompilieren könnte, wegen besagtem Fehler.
Irgendwie nervt es auch etwas dass du hier (falsche) Vermutungen anstellst und diese als Tatsachen formulierst (z.B. "Halcon arbeitet schon mit 64er Adressierung") - und hilfreich ist es ganz sicher auch nicht.
-
con capisce un tubo...
Wenn du nicht ständig versuchen würdest mich anzumachen wäre es eine Ecke einfacher.
Nochmal: Ein LAUFENDES Beispiel für Halcon kann ich dir nicht geben, da die Typen zu Halcon gehören und du dafür eine Developer Lizens brauchst, da sonst
die DLLs nicht laufen.CPP eines dyn dll Projekts in Borland.
int* foo; extern "C" _declspec(dllexport) void Anlegen() { foo = new int(); *foo = 9969; } extern "C" _declspec(dllexport) int Auslesen() { return (int) foo; }Holzhammermethode. Prozeduraler Code, Dll daraus gebaut.
Erzeugt, kopiert, Konsolenprogramm aufgemacht.
HINSTANCE hdll; hdll = LoadLibrary("Lan_German.dll"); typedef void (*Anlegen)(); typedef int (*Auslesen)(); Anlegen anleg; Auslesen ausles; anleg= (Anlegen) GetProcAddress (hdll, "_Anlegen"); ausles= (Auslesen) GetProcAddress (hdll, "_Auslesen"); anleg(); int* p = (int*)ausles();Inhalt p ->9969. Wird also zwischen den Aufrufen der DLL Funktion nicht gelöscht. Sowas wollte ich wissen... Jetzt habe ich mir halt doch die Zeit aus den Fingern gesaugt, die ich nicht habe.
Visual Studio. Konsolenprogramm
#include "stdafx.h" #include "HalconCpp.h" #include <iostream> #include <vector> INT64 action() { using namespace std; using namespace Halcon; //int width = 10; // int height = 10; HWindow hWindow(""); HTuple OpSys; get_system("operating_system",&OpSys); HTuple Pointer, Type, Width, Height, WindowHandle, WindowHandle1; HTuple Pointer1, Type1, Width1, Height1, WindowHandle2; HTuple HorProjection, VertProjection; Hobject QImage,EImage, Rectangle, Rectangle1, ImageMean, Rect, Result; Hobject RectangleResult; WORD* quellbild; char* qtype=new char(10); Hlong qwidth=0; Hlong qheight=0; WORD* ergbild; char* etype=new char(10); Hlong ewidth=0; Hlong eheight=0; //Generieren des Quellbilds INT64 adresse =(INT64) &QImage; Hobject* neuesbild; neuesbild = (Hobject* ) adresse; gen_image_const(neuesbild,"uint2",100,100); return adresse; } void speichern(INT64 puffer) { using namespace Halcon; Hobject* ergimage = (Hobject*) puffer; //BANG Bild existiert gar nicht write_image(*ergimage, "png fastest",0,"C:\\test"); } int _tmain(int argc, _TCHAR* argv[]) { using namespace Halcon; set_system("do_low_error","false"); INT64 bild = action(); Hobject* ergimage = (Hobject*) bild; //BANG hier krachts eigentlich schon write_image(*ergimage, "png fastest",0,"C:\\test"); speichern(bild); return 0; }Bei write_image krachts. Egal welches der beiden ich aktiv habe.
Was mir nach genug Schlaf aufgefallen ist, ist daß es eventuell daran liegt, daß die Variablen nur in den Funktionen gültig sind, mein foo in der dll global.
Die Kombination aus beiden Programmen muss das ERgebnis werden... Ausserhalb der DLL darf KEINERLEI Halcon auftauchen. Nur normale C/C++ Typen. DEswegen der Umweg über das Casten.
-
ich weis jetzt nicht ob das auch ein schreibfehler ist. aber ersetzte mal diese Zeile hier
Hobject QImage,EImage, Rectangle, Rectangle1, ImageMean, Rect, Result;durch diese hier:
Hobject EImage, Rectangle, Rectangle1, ImageMean, Rect, Result; Hobject* QImage = new Hobject;In deinem Beispiel legst du nämlich wirklcih nur eine lokale Variable QImage vom Typ Hobject an. Die wird dann beim verlassen der Funktion gelöscht.
Mit dem hier:
INT64 adresse =(INT64) &QImage; Hobject* neuesbild; neuesbild = (Hobject* ) adresse;konvertierstu du wirklich nur die Adresse des (lokalen Objektes) QImage nach INT64 und dann wieder nach Hobject*.
-
Fuck... (Wieso konvertiert das Forum Fu... in Love? LOL?)
Du hast Recht. Das kommt davon wenn man sowas mit nem Kollegen nebendran schreibt, der nicht wirklich C++ programmiert und mit dem diskutiert.Ok. Danke Chris! Das dürfte es gewesen sein. Wenn mein Dongle nachher wieder kommt probier ich das aus. Daran hab ich überhaupt nicht gedacht.
QImage ist lokal und ich jongliere nur die Adresse durch die Gegend.
-
Siehste, genau deswegen wollte ich kein für's Forum gettipseltes Beispiel sondern Copy & Paste

Und... wie gesagt ich bin mir sicher dass die Halcon keine Garbage Collection macht, d.h. du solltest die Objekte auch löschen (in der DLL, mit delete, sollte klar sein), sonst musst du mit Memory Leaks rechnen.
Und weil's zum Guten Stil gehört änder doch deine INT64 einfach noch in "void*" - dadurch ist dann auch automatisch egal ob es 64 Bit oder 32 Bit Zeiger sind. Und mit "void*" sollte der Borland schon klarkommen, und das 'extern "C"' macht auch kein Problem (weiss ich aus eigener Erfahrung, selbst schon gemacht).
Wichtig ist wie gesagt dass du alles was du mit Code in der DLL anlegst auch wieder mit Code in der DLL löscht, sonst wird's krachen, da der MSVC und der Borland nicht den selben Heap verwenden. Solange es alles Halcon Objekte sind die du per INT64/void* rumreichst geht es sowieso nicht anders, bloss falls du irgendwo new char[] oder malloc verwendest eben der Hinweis: alles dort freigeben wo es angefordert wurde.
Noch ein Tip (sollte klar sein, bloss zur Sicherheit): du solltest aus deiner DLL keine Exceptions rauswerfen, das wird der Borland u.U. auch nicht mögen. Also überall wo was knallen könnte einen try{}catch(...){} rummachen und den Fehler dann über den Returnwert "weiterleiten".