Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Thread in dll (https://www.delphipraxis.net/104749-thread-dll.html)

clownxx 10. Dez 2007 22:12


Thread in dll
 
Hey, ich benutze die Komponente TComport von

http://comport.sf.net/

um mit der seriellen Schnittstelle zu arbeiten.

Ich habe nicht das Package installiert sondern lediglich die Unit eingebunden. Klappt alles wunderbar.
Nun möchte ich diese Komponente über ein neues Interface in eine DLL packen. Klappt auch soweit.
Diese Komponente TComport erstellt einen Thread, welcher nach neuen Charactern auf der seriellen Schnittstelle sucht. Wurde eines gefunden, wird ein Event ausgelöst. Das klappt auch, jedoch nicht in der DLL. Hier ergbit sich folgendes Problem:

Im Execute des Threads der TComport Komponente wird auf neue Zeichen geprüft. Dann wird die Thread-Methode DispatchComMsg aufgerufen. Diese ruft dann

synchronize(DoEvents)

auf. Do Events ist auch einen Threadmethode. Sie feuert dann das Event. Wenn ich den haltepunkt nun auf den synchronize-Aufruf setze gelangt der Compiler dort an, aber er geht nicht in die Methode DoEvents. Lasse ich den Befehl synchronize weg, geht er in DoEvents. Allerdings kommen dann nicht immer sinnvolle Zeichen an. Kann mir jemand einen Ansatz geben, wie ich das Problem angehen kann?

Danke schonmal.

Güße

Muetze1 10. Dez 2007 22:35

Re: Thread in dll
 
Synchronize arbeitet mit der VCL Nachrichtenschleife im VCL Thread. Wenn du keinen VCL Thread hast, hast du keine entsprechende Nachrichtenverarbeitung und somit läuft Synchronize in's leere.

clownxx 10. Dez 2007 23:03

Re: Thread in dll
 
vielen Dank für die Antwort. Die dll binde ich ja in eine Anwendung ein, also sollte ich ja auch einen VCL Thread haben oder sehe ich das falsch? Was kann ich tun um einen VCL Thread zu bekommen? Gibt es da eine Möglichkeit?

Muetze1 10. Dez 2007 23:08

Re: Thread in dll
 
DLL und Anwendung sind zwei unterschiedliche Dinge. Die haben (im Normalfall) nichtmal den Speichermanager gemeinsam, somit ist da nichts mit Synchronisation von Dingen in einer DLL. Wie sollte das die Anwendung mir nichts, dir nichts bewerkstelligen? Du kannst sonstwas für DLLs mit LoadLibrary z.B: laden und da soll die Anwendung automatisch und völlig konfigurationslos Threads synchronisieren? Wenn du da eine gute Formel hinbekommst: patentieren lassen. Damit kannste auch Geld machen...

Also: Die DLL hat soweit keine Oberflächen VCL mit drin, somit muss auch nichts synchronisiert werden. Rufe einen vorher von der Anwendung übergebenen Callback einfach vom Thread aus auf und gut ist. Die Anwendung muss ich darum kümmern das zu synchronisieren. Sie muss einfach so programmiert werden, dass dieser Callback jederzeit aufgerufen werden kann....

clownxx 10. Dez 2007 23:24

Re: Thread in dll
 
ja das hieße also, ich nehme einfach synchronize weg, das klappt ja auch. Allerdings kommen dann keine gescheiten werte an der Callback an. Muss ich dann in der Callback synchronisieren? Denn diese verwendet dann den Mainthread des Comports um aus der schnittstelle zu lesen. Ich denke das, was ich synchronisieren muss ist nur der Zugriff auf FEvents, da hier ja events geschrieben werden können, wärend sie gelesen werden. das sollte ja denke ich auch mit synchronize bewerkstelligt werden. Ist das so richtig?

Muetze1 10. Dez 2007 23:37

Re: Thread in dll
 
Schau dir mal in Bezug auf die Events die Klasse Delphi-Referenz durchsuchenTMultiReadExclusiveWriteSynchronizer an, die macht genau das gesuchte. Eine Instanz bilden und dann alle Lese und Schreibzugriffe mit den entsprechenden Methodenaufrufen säumen (BeginRead/EndRead; BeginWrite/EndWrite)

clownxx 11. Dez 2007 00:45

Re: Thread in dll
 
vielen Dank für deine Hilfe!!!! Ich glaube ich habe das Problem nun in den Griff bekommen.

Viele Grüße

clownxx 11. Dez 2007 09:42

Re: Thread in dll
 
eines ist jedoch nun komisch. Wenn ich das ganze mit deiner vorgeschlagenen Klasse versuche, wird die Procedure DoEvents auch nie abgearbeitet, geanu wie wenn ich synchronize verwende. Lass ich die ganzen Synchronisationsmechanismen weg klappt das ja. Wenn ich nun in der Callback ein Showmessage(text) aufrufe, ist der Button des Fensters ganz klein und es steht erstmal nichts drin. Schreibe ich den TExt den ich in der Callback empfange einfach in ein Memo ist alles top. Dann wird dieser korrekt angezeigt. Ist das normal oder was habe ich hier noch falsch gemacht?

Muetze1 11. Dez 2007 13:46

Re: Thread in dll
 
Zitat:

Zitat von clownxx
Wenn ich das ganze mit deiner vorgeschlagenen Klasse versuche, wird die Procedure DoEvents auch nie abgearbeitet, geanu wie wenn ich synchronize verwende.

DoEvents kannst du doch dann direkt aufrufen. Somit kann ich nun erstmal schlecht nachvollziehen, warum du niemals zum DoEvents kommst.

Zitat:

Zitat von clownxx
Lass ich die ganzen Synchronisationsmechanismen weg klappt das ja. Wenn ich nun in der Callback ein Showmessage(text) aufrufe, ist der Button des Fensters ganz klein und es steht erstmal nichts drin.

Weil du dann immernoch im Thread hängst und nicht im VCL Thread. Dass müsstest du in den VCL Thread synchronisieren. Dadurch auch der Fehler, da die Elemente vom falschen (der VCL unbekannten) Thread aus aufgerufen werden. Die VCL ist grundsätzlich nicht thread-safe!

Zitat:

Zitat von clownxx
Schreibe ich den TExt den ich in der Callback empfange einfach in ein Memo ist alles top. Dann wird dieser korrekt angezeigt.

Es tritt nur kein offensichtlicher Fehler auf, heißt aber nicht, dass alles klappt.

Zitat:

Zitat von clownxx
Ist das normal oder was habe ich hier noch falsch gemacht?

Ok, eine Idee, wie ich das ganze anfassen würde.

Anforderungen:

1. Du hast einen Thread der ab und zu Daten ermittelt, welche in einen Datenspeicher abgelegt werden.
2. Wenn was empfangen wurde, dann müssen die Daten an die Applikation weitergereicht werden.
3. Zu diesem Zeitpunkt muss dieses Ereignis der Applikation mitgeteilt werden

Schnittstelle ist der Datenspeicher. Dieser muss synchronisiert werden, damit nicht gleichzeitig gelesen und geschrieben wird.

Lösung:

Dazu wird eine Instanz von Delphi-Referenz durchsuchenTMultiReadExclusiveWriteSynchronizer erzeugt, welche dem Thread und einer DLL Funktion bekannt gemacht wird.

Der Thread läuft und immer wenn er Daten hat, dann nutzt er BeginWrite/EndWrite um in den Datenspeicher die neuen Daten abzulegen.

Die o.g. DLL Funktion dient der Applikation um die Daten abzuholen. Dazu ruft die Applikation die Funktion auf und darin (DLL Seite) wird die Delphi-Referenz durchsuchenTMultiReadExclusiveWriteSynchronizer Instanz mit BeginRead/EndRead verwendet.

Soweit haben wir die Datenübergabe samt thread-Sicherheit geschaffen. Nun fehlt nur noch, dass die Applikation Bescheid bekommt, wenn neue Daten angekommen sind. Dazu kann die Applikation einmalig eine DLL Funktion aufrufen, bei der sie ein Fensterhandle mitgibt. Dieses merkt sich die DLL und sendet u.a. mit PostMessage innerhalb des Threads eine Botschaft ab. Innerhalb von BeginWrite/EndWrite im Thread darf die Liste auch gelesen werden (Write ist Exklusiv) und dadurch könnte die abgesendete Message sogar die Anzahl der empfangenen Daten und noch vorhandenen Daten in dem Datenpuffer übermittelt werden. Damit weiss die Applikation auch gleich, wie oft sie die o.g. Funktion zum Daten abholen aufrufen kann/muss.

Damit wird das Auslesen innerhalb des VCL Threads angestossen (durch die Botschaft) und ist somit mit der GUI synchron.

Hinweis:
Die von der Applikation ausgelesenen/abgefragten Daten sollen bestimmt danach im Datenpuffer entfernt werden. Wenn die Applikation mehrfach liest bis sie alle Daten hat, dann wäre es besser, wenn es eine Löschen Funktion gibt. Dafür wäre nämlich ein exklusives BeginWrite/EndWrite nötig, und das würde für jeden einzelnen Datenblock den ganzen Thread so stark behindern, dass der Thread nichts mehr bringt. Von daher lieber so auslesen und extra löschen.

clownxx 11. Dez 2007 14:54

Re: Thread in dll
 
hey, vielen Danke für die Ausführliche Antwort. Ich werde mir das mal zu Gemüte führen. Aber vom Konzept her ist das für mich nun sehr gut nachvollziehbar. Vielen Dank nochmal.

clownxx 11. Dez 2007 22:23

Re: Thread in dll
 
so...die Probleme gehen weiter ;-)
Wenn ich nun BeginRead vor DoEvents setze, kehrt er niemals mehr wieder aus der BeginRead Funktion zurück, es kommt somit nie zur Ausführung von DoEvents. Es scheint also das gleiche Problem wie mit Synchronize....

Muetze1 11. Dez 2007 23:00

Re: Thread in dll
 
Zitat:

Zitat von clownxx
Wenn ich nun BeginRead vor DoEvents setze, kehrt er niemals mehr wieder aus der BeginRead Funktion zurück, es kommt somit nie zur Ausführung von DoEvents. Es scheint also das gleiche Problem wie mit Synchronize....

Das kann eigentlich nur sein, wenn du ein BeginWrite nie wieder mit EndWrite abschließt. Du musst nur den reinen Zugriff auf den Datenspeicher entsprechend umfassen, aber nicht den Code drumherum.

clownxx 12. Dez 2007 09:01

Re: Thread in dll
 
mh ok. Es geht ja eigentlich bei dem Datenspeicher um diese TComport komponenten, da kann ich nun nicht so wirklich feststellen was da nun der Datenspeicher ist....Ich habe es nun so gemacht:

Delphi-Quellcode:
procedure TComThread.DispatchComMsg;
begin
  FComPort.FSynchronizer.beginRead;
  DoEvents;
  FComPort.FSynchronizer.EndREad;
end;
DoEvents macht dann folgendes:

Delphi-Quellcode:
procedure TComThread.DoEvents;
begin
  FComPort.CallRxChar;
end;
und in Call RxChar (wo die Callback aufgerufen wird) passiert das:

Delphi-Quellcode:
procedure TCustomComPort.CallRxChar;
var
  Count: Integer;

  // read from input buffer
  procedure PerformRead(var P: Pointer);
  begin
    GetMem(P, Count);
    Read(P^, Count);
    // call OnRxBuf event
    DoRxBuf(P^, Count);
  end;

  // check if any component is linked, to OnRxChar event
  procedure CheckLinks;
  {$WARNINGS OFF}
  var
    I: Integer;
    P: Pointer;
    ComLink: TComLink;
    ReadFromBuffer: Boolean;
  begin
    // examine links
    if (Count > 0) and (not TriggersOnRxChar) then
    begin
      ReadFromBuffer := False;
      try
        // cycle through links
        for I := 0 to FLinks.Count - 1 do
        begin
          ComLink := TComLink(FLinks[I]);
          if Assigned(ComLink.OnRxBuf) then
          begin
            // link to OnRxChar event found
            if not ReadFromBuffer then
            begin
              // TCustomComPort must read from comport, so OnRxChar event is
              // not triggered
              ReadFromBuffer := True;
              PerformRead(P);
            end;
            // send data to linked component
            ComLink.OnRxBuf(Self, P^, Count);
          end
        end;
        if (not ReadFromBuffer) and (not FTriggersOnRxChar) then
        begin
          ReadFromBuffer := True;
          PerformRead(P);
        end;
      finally
        if ReadFromBuffer then
        begin
          FreeMem(P);
          // data is already out of buffer, prevent from OnRxChar event to occur
          Count := 0;
        end;
      end;
    end;
  end;

begin
  Count := InputCount;
  //@clownxx
  FSynchronizer.BeginRead;
  ///
  if Count > 0 then
    SendSignalToLink(leRx, True);
  CheckLinks;
  if Count > 0 then
    DoRxChar(Count);
  //@clownxx
  FSynchronizer.EndRead;
  ///
end;
Viel Code, aber vielleicht ist es so verständlicher wie das Teil aufgebaut ist.

Muetze1 13. Dez 2007 08:44

Re: Thread in dll
 
Ich habe ein leichtes Verständnisproblem: Die Komponente für den COM Port arbeitet doch intern schon entweder mit einem Thread oder über Events. Warum baust du nun einen Thread da nochmal drumherum? Ich kapier das nicht.

Selbst nach einer Nacht drüber schlafen, ist mir das noch nicht klar geworden.

clownxx 13. Dez 2007 09:06

Re: Thread in dll
 
Sorry wenn ich mich unverständlich ausgedrückt habe! Es gibt die Comportcomponente und den Thread (der ist un der Unit der Comport komponente)

FEventThread: TComThread;

ist ein Memberobject der Klasse TCustomComport (worin sich ja auch onRXChar, wie im vorherigen Post beschrieben, befindet)

Weitere Threads brauche ich nicht. Der Thread schaut nach events und feuert die Callbacks.

Muetze1 13. Dez 2007 09:23

Re: Thread in dll
 
Ok, du hast diesen Thread hinzugefügt - direkt in die Komponente. Und wozu? Die Componente hat doch Callbacks. Da kannst du dich ranhängen und auf das jeweilige Event der COM Port Komponente reagieren - z.B. wenn Zeichen empfangen wurden, diese interpretieren. Wenn du was empfangen hast und es interpretieren konntest, dann rufe die Callbacks zu deiner App auf. Aber warum noch ein Thread?

clownxx 13. Dez 2007 11:11

Re: Thread in dll
 
nein, der Thrad ist schon vorhanden....ich habe das nicht hinzugefügt. Ich möchte diese Komponente lediglich in eine DLL Kapseln. Das klappt aber nicht, da der Thread synchronize ausführt um ein Event aufzurufen. Das klappt zwar wenn man die Komponente in eine Anwendung einbindet, aber nicht in der DLL. Da kommen dann keine Events an. Wenn du magst oder es einfach wird, kann ich dir auch die Komponente mal mailen oder auf einem Webspace zur verfügung stellen.
Danke!!

Grüße

Muetze1 13. Dez 2007 12:38

Re: Thread in dll
 
Ok, hat etwas gedauert, aber nun habe ich das ganze kapiert. Schick mir einfach mal die Komponente (hier im PN System sind Anhänge möglich) und dann schau ich mal...

clownxx 13. Dez 2007 14:27

Re: Thread in dll
 
ok getan ;-) vielen Dank schon mal wieder!


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:10 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