Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht Das deutsche QBasic- und FreeBASIC-Forum
Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
 
FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen  RegistrierenRegistrieren
ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin
Zur Begleitseite des Forums / Chat / Impressum
Aktueller Forenpartner:

Datenaustausch Hauptprogramm - DLL (STRING-Problem)

 
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC.
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 05.03.2017, 17:28    Titel: Datenaustausch Hauptprogramm - DLL (STRING-Problem) Antworten mit Zitat

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 happy 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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 05.03.2017, 18:54    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 05.03.2017, 20:05    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 05.03.2017, 20:14    Titel: Antworten mit Zitat

Ü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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 05.03.2017, 20:41    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 06.03.2017, 10:36    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Elor



Anmeldungsdatum: 12.07.2013
Beiträge: 205
Wohnort: Konstanz

BeitragVerfasst am: 06.03.2017, 12:12    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 06.03.2017, 18:22    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 06.03.2017, 19:39    Titel: Antworten mit Zitat

@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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Elor



Anmeldungsdatum: 12.07.2013
Beiträge: 205
Wohnort: Konstanz

BeitragVerfasst am: 06.03.2017, 20:58    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 06.03.2017, 22:29    Titel: Antworten mit Zitat

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. grinsen 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. happy
_________________
Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 07.03.2017, 10:50    Titel: Antworten mit Zitat

@Elor:
Du hast Recht, Asche auf mein Haupt. peinlich

@nemored:
Ja, es ist nicht leicht, bei dem ganzen Hin und Her den Überblick zu behalten. lächeln Also noch mal ganz langsam zum Mitdenken: Die zur Informationsübermittlung eingesetzte Variable vom Typ tUdt wird im Hauptprogramm erzeugt (und zwar nur dort):
Code:
Dim As tUdt udt
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.
Code:
cb(udtp)
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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4594
Wohnort: ~/

BeitragVerfasst am: 07.03.2017, 15:34    Titel: Antworten mit Zitat

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. happy
_________________
Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
MOD
Fleißiger Referenzredakteur


Anmeldungsdatum: 10.09.2007
Beiträge: 1003

BeitragVerfasst am: 07.03.2017, 17:05    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1208
Wohnort: Ruhrpott

BeitragVerfasst am: 08.03.2017, 11:26    Titel: Antworten mit Zitat

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? grinsen ). 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... grinsen

Gruß
grindstone
_________________
For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen!
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC. Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.

 Impressum :: Datenschutz