Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 05.03.2017, 18:28 Titel: Datenaustausch Hauptprogramm - DLL (STRING-Problem) |
|
|
Ich versuche mich gerade an einem PlugIn-System über dynamische Bibliotheken, und auch wenn die sich vehement sträuben, komme ich der Sache so langsam näher. Von Hauptprogramm oder einer DLL/SO aus eine Prozedur einer anderen DLL/SO aufzurufen, ist kein größeres Problem; für den Aufruf einer Hauptprogramms-Prozedur habe ich mich von MODs PlugIn-System inspirieren lassen, und das funktioniert prinzipiell auch. Das Hauptprogramm übergibt den (bzw. die) Pointer seiner globalen Funktionen an die DLL/SO, und die kann darüber zugreifen.
Ein seltsames Verhalten ist mir allerdings bei Funktionen aufgefallen, die einen String zurückgeben. Der Aufruf funktioniert exakt 256 mal und dann nicht mehr.
Hauptprogramm
Code: | function testFunction as string
static as integer count
count += 1
return str(count)
end function
dim as any ptr library = dylibload("libtestLib.so")
dim init as sub(param as function as string)
init = dylibsymbol(library, "init")
init(@testFunction)
dylibfree library |
Bibliothek (compiliert mit -dll)
Code: | sub init alias "init" (callback as function as string) export
dim as string value
do
value = callback()
locate 1, 1 : print value
sleep 10
loop until len(inkey)
end sub |
Wenn ich das mit INTEGER statt STRING mache, zählt er munter immer weiter; bei STRING endet er bei 256. Getestet unter Linux 32bit; mein VM-Windows hat zwar die Bibliothek laden können, DYLIBSYMBOL lieferte jedoch den Wert 0 zurück. Damit werde ich mich wohl auch noch beschäftigen müssen ...
Gibt es für die Begrenzung auf 256 Aufrufe eine plausible Erklärung?
edit: Ich habe es gerade noch mit einem einfachen UDT probiert - das klappt ebenfalls (auch wenn das UDT einen String enthält).
edit2: mit dem Schlüsselwort EXPORT klappt es auch unter Windows allerdings bei STRINGs auch nur bis 256. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 05.03.2017, 19:54 Titel: |
|
|
Es wird noch seltsamer. Probier mal das hier:
Code: | Sub init Alias "init" (callback As Function As String) Export
Dim As String value
Do
value = callback()
'Locate 1, 1
Print "value ";value;"*"
Sleep 100
Loop Until Len(Inkey)
End Sub |
Code: | Function testFunction() As String
Static As Integer count
count += 1
'Locate 10,1
? "-----------------------"
? "tF.............";count;" "
? "strCount ";Str(count)
'Sleep 100
Return "abc" + Str(count)
End Function
Dim As Any Ptr library = DylibLoad("testLib.dll")
If library = 0 Then
?"Fehler"
Sleep
End
EndIf
Dim init As Sub(param As Function As String)
init = DyLibSymbol(library, "init")
init(@testFunction)
DyLibFree library |
Gruß
grindstone
EDIT: Es sieht so aus, als wenn nach dem 256. Aufruf keine Strings mehr ausgegeben werden. Auch nicht von innerhalb der testFunction. _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 05.03.2017, 21:05 Titel: |
|
|
St_W hat dazu was ausgegraben:
https://forum.qbasic.at/viewtopic.php?p=99507
Offenbar ein Problem, das sich in absehbarer Zeit nicht lösen lässt. In meinem (deutlich aufwendigeren) Projekt scheint es auch zu "nicht ganz sauberer" Speicherverwaltung zu kommen, jedenfalls macht meine Konsole nach Programmende komische Sachen. Ich werde dynamische Bibliotheken wohl erstmal wieder fallen lassen. :o/ _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 05.03.2017, 21:14 Titel: |
|
|
Über einen kleinen Umweg funktionert es: Code: | Sub init Alias "init" (callback As Any Ptr, zsp As ZString Ptr) Export
Dim As String value
Dim cb As Sub(zsp As ZString Ptr)
cb = callback
Do
cb(zsp)
Locate 1, 1
Print "value ";*zsp;"*"
Sleep 10
Loop Until Len(Inkey)
End Sub |
Code: | Sub testFunction (zsp As ZString Ptr)
Static As Integer count
count += 1
*zsp = ">>> " + Str(count)
End Sub
Dim As ZString*200 zs
Dim As Any Ptr library = DylibLoad("testLib.dll")
Dim init As Sub(param As Any Ptr, zs As ZString Ptr)
init = DyLibSymbol(library, "init")
init(@testFunction, @zs)
DyLibFree library |
_________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 05.03.2017, 21:41 Titel: |
|
|
Mal schauen, ob das für mich tatsächlich in Frage kommt; wenn ich mich um die korrekte Behandlung der Speicherbereiche selbst kümmern muss, fürchte ich bei einem so großen Projekt fast notwendigerweise den einen oder anderen Speicher-Leak ...
(Und alle Datentypen von STRING aus ZSTRING PTR umzustellen ist eine ziemliche Herausforderung, schon wegen der umfangreichen UDT-Sammlung.)
Naja, mal sehen. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 06.03.2017, 11:36 Titel: |
|
|
Ich habe den ZString nur als Beispiel genommen, weil er am einfachsten zu handhaben ist. UDTs sind auch kein Problem, allerdings müssen sie dann in der Bibliothek und im Hauptprogramm definiert werden: Code: | Type tUdt
s As String
i As Integer
End Type
Sub init Alias "init" (callback As Any Ptr, udtp As tUdt Ptr) Export
Dim As String value
Dim cb As Sub(udtp As tUdt Ptr)
cb = callback
Do
cb(udtp)
Locate 1, 1
Print "value ";udtp->s;"*"
Print udtp->i
Sleep 10
Loop Until Len(Inkey)
End Sub
|
Code: | Type tUdt
s As String
i As Integer
End Type
Dim As tUdt udt
Sub testFunction (udtp As tUdt Ptr)
Static As Integer count
count += 1
udtp->s = ">>> " + Str(count)
udtp->i = count
End Sub
Dim As ZString*200 zs
Dim As Any Ptr library = DylibLoad("testLib.dll")
Dim init As Sub(param As Any Ptr, udtp As tUdt Ptr)
init = DyLibSymbol(library, "init")
init(@testFunction, @udt)
DyLibFree library |
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
Elor
Anmeldungsdatum: 12.07.2013 Beiträge: 205 Wohnort: Konstanz
|
Verfasst am: 06.03.2017, 13:12 Titel: |
|
|
Also mir kommt das mit den Pointern irgendwie umständlich vor, der String als Parameter einer SUB funktioniert doch. Ich würd das einfach so machen
LIB: Code: |
sub init alias "init" (callback as sub(s as string)) export
dim as string value
do
callback(value)
locate 1, 1 : print value
sleep 10
loop until len(inkey)
end sub |
und Testprogramm: Code: |
sub testFunction(s as string)
static as integer count
count += 1
s= str(count)
end sub
dim as any ptr library = dylibload("libtestLib.so")
dim init as sub(param as sub(s as string))
init = dylibsymbol(library, "init")
init(@testFunction)
dylibfree library |
|
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 06.03.2017, 19:22 Titel: |
|
|
Zitat: | UDTs sind auch kein Problem, allerdings müssen sie dann in der Bibliothek und im Hauptprogramm definiert werden: |
Ob die tatsächlich kein Problem sind, da bin ich mir nicht so sicher. Nachdem ich den String gestern in einen UDT-Container verpackt hatte, lief das Programm soweit, aber nach Programmende konnte ich auf meiner Konsole lustige Zeichenausgaben in Abhängigkeit von der Mausbewegung innerhalb des Konsolenfensters beobachten. Für mich ein deutliches Signal für unsaubere Speicherverwaltung - mit dem UDT schien es zu funktionieren, aber höchstwahrscheinlich wurden auch da die Strings nicht sauber freigegeben (geht ja laut Aussage des alten Threads auch gar nicht).
@Elor: Werden da die Strings sauber freigegeben? Ich fürchte, dass da dann einfach so lange Strings erzeugt werden, bis der Arbeitsspeicher voll ist. Leider kenne ich mich mit den entsprechenden Werkzeugen nicht aus, um das zu überprüfen. Anders sieht es vielleicht mit "BYREF s AS STRING" aus. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 06.03.2017, 20:39 Titel: |
|
|
@Elor:
Wenn du das so machen willst, mußt du den Parameter unbedingt ByRef übergeben, was ja in Wirklichkeit genauso eine Pointerübergabe ist wie im Beispiel mit dem UDT. Die (Standard-)Übergabe ByVal funktioniert hier nur, weil noch kein anderes Programm Gelegenheit hatte, den verwendeten (und inzwischen wieder freigegebenen) Speicher zu überschreiben.
@nemored:
Bist du sicher, daß die Datenübergabe das Problem verursacht? Die Rückübermittlung von Daten über vom aufrufenden Programm gelieferte Pointer (auf beliebig komplexe Datenstrukturen) ist -sofern es sich nicht um einfache Integer - Werte handelt- das Standardverfahren bei allen WinAPI - Funktionen.
Was passiert denn, wenn die "init"- Sub ganz normal im Programm codiert wird, statt sie aus einer Bibliothek nachzuladen?
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
Elor
Anmeldungsdatum: 12.07.2013 Beiträge: 205 Wohnort: Konstanz
|
Verfasst am: 06.03.2017, 21:58 Titel: |
|
|
grindstone hat Folgendes geschrieben: | Die (Standard-)Übergabe ByVal funktioniert hier nur, weil noch kein anderes Programm Gelegenheit hatte, den verwendeten (und inzwischen wieder freigegebenen) Speicher zu überschreiben. | Jetzt hast du mich aber ganz schön verwirrt, musste mir Grad noch mal die Referenz anschauen und es ist so schon richtig wie ich es gemacht hab. Laut Referenz werden Strings und UDTs standardmäßig ByRef übergeben. |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 06.03.2017, 23:29 Titel: |
|
|
grindstone hat Folgendes geschrieben: | @nemored:
Bist du sicher, daß die Datenübergabe das Problem verursacht? Die Rückübermittlung von Daten über vom aufrufenden Programm gelieferte Pointer (auf beliebig komplexe Datenstrukturen) ist -sofern es sich nicht um einfache Integer - Werte handelt- das Standardverfahren bei allen WinAPI - Funktionen. |
Wenn ich sicher wäre, wäre alles viel einfacher. Das geschilderte Problem taucht nicht in dem kleinen geposteten Programm-Schnipsel auf, das ich gepostet habe, sondern im umfangreichen Projekt, wo eine ganze Menge Daten zwischen den Bibliotheken und dem Hauptprogramm auftauchen. Ohne Bibliotheken (also alle Funktionen komplett im Hauptprogramm) ist das bisher noch nie passiert. Aber es ist durchaus möglich, dass sich noch ein anderer Speicher-Fehler eingeschlichen hat.
Die Datenübergabe des UDTs als Pointer mit sauberer Speicherbereinigung kann ich mir im Moment nicht so recht vorstellen. Die Funktion erzeugt ein UDT (und damit einen FB-String) und gibt den Pointer an die aufrufende DLL zurück. Dann kann die Funktion ja nicht die Zerstörung des UDTs einleiten. Wenn sich aber die DLL mit der Zerstörung beschäftigen muss, hat sie doch genau dasselbe Problem wie vorher, das der enthaltende String aus der anderen rtlib nicht zerstört werden kann - oder täusche ich mich da?
String-Parameterübergabe über BYREF klingt im Moment ganz sinnvoll, aber auch hier wäre es lieber, wenn da jemand mit entsprechenden Speicherüberwachungsmöglichkeiten drüberschauen könnte. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 07.03.2017, 11:50 Titel: |
|
|
@Elor:
Du hast Recht, Asche auf mein Haupt.
@nemored:
Ja, es ist nicht leicht, bei dem ganzen Hin und Her den Überblick zu behalten. Also noch mal ganz langsam zum Mitdenken: Die zur Informationsübermittlung eingesetzte Variable vom Typ tUdt wird im Hauptprogramm erzeugt (und zwar nur dort):Der Pointer auf auf diese Variable wird an die DLL - Funktion "init" übergeben Code: | init(@testFunction, @udt) | und von dieser an die Callback - Routine "testFunction" weitergereicht. Diese schreibt die gewünschten Informationen in die UDT - Variable Code: | udtp->s = ">>> " + Str(count)
udtp->i = count | und springt zurück zur aufrufenden DLL - Funktion. Dort wird die Information ausgewertet, sprich: ausgedruckt. Code: | Locate 1, 1
Print "value ";udtp->s;"*"
Print udtp->i | Sowohl die DLL - Funktion als auch die Callback - Routine greift dabei immer auf die im Hauptprogramm erzeugte Variable zu. In der DLL selbst wird keine Variable erzeugt (zumindest keine vom Typ tUdt) und auch keine zerstört. Die Typendeklaration in der DLL dient nur dazu, dem Compiler die Struktur des UDT mitzuteilen, damit auf die entsprechenden Records zugegriffen werden kann.
Daher dürfte es bei dieser Vorgehensweise auch keine Speicherlecks geben.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4599 Wohnort: ~/
|
Verfasst am: 07.03.2017, 16:34 Titel: |
|
|
Ja ok, so herum sollte es funktionieren. Andererseits bedeutet es aber dann, dass ich für jede Hauptprogramm-Funktion, die ich von der DLL aus aufrufen will, sowohl einen Pointer auf die Funktion als auch einen Pointer auf das Rückgabe-UDT übergeben muss (wenn die Rückgabe nicht nur aus Zahlendatentypen besteht). Und natürlich muss ich die Rückgabe anschließend sichern, da ein erneuter Funktionsaufruf ja notwendigerweise den letzten Rückgabewert überschreibt.
Letztendlich heißt das aber wohl, dass man sich getrost von Funktionen verabschieden kann und alles auf SUBs mit BYREF-Variablen auslagert. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
MOD Fleißiger Referenzredakteur
Anmeldungsdatum: 10.09.2007 Beiträge: 1003
|
Verfasst am: 07.03.2017, 18:05 Titel: |
|
|
Wenn du stattdessen ein Transport UDT verwenden willst, könntest du mdString versuchen. Es sollte sich in den meisten Fällen wie ein String verhalten, sodass du nur den Datentyp ändern musst, der Rest aber automatisch passiert. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1212 Wohnort: Ruhrpott
|
Verfasst am: 08.03.2017, 12:26 Titel: |
|
|
nemored hat Folgendes geschrieben: | Andererseits bedeutet es aber dann, dass ich für jede Hauptprogramm-Funktion, die ich von der DLL aus aufrufen will, sowohl einen Pointer auf die Funktion als auch einen Pointer auf das Rückgabe-UDT übergeben muss (wenn die Rückgabe nicht nur aus Zahlendatentypen besteht). | Stimmt, da habe ich jetzt wohl um eine Ecke zuviel gedacht (Wie war das mit der Übersicht? ). Natürlich geht es auch so: Code: | Type tUdt
s As String
i As Integer
End Type
Sub init Alias "init" (callback As Any Ptr) Export
Dim cb As Sub(udtp As tUdt Ptr) = callback
Dim As tUdt udt
Do
cb(@udt)
Locate 1, 1
Print "value ";udt.s;"*"
Print udt.i
Sleep 10
Loop Until Len(Inkey)
End Sub |
Code: | Type tUdt
s As String
i As Integer
End Type
Sub testFunction (udtp As tUdt Ptr)
Static As Integer count
count += 1
udtp->s = ">>> " + Str(count)
udtp->i = count
End Sub
Dim As Any Ptr library = DylibLoad("testLib.dll")
Dim init As Sub(param As Any Ptr)
init = DyLibSymbol(library, "init")
init(@testFunction)
DyLibFree library |
Und wenn nun die Callback - Routine ihrerseits wieder eine DLL aufruft...
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
|