Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Object mit SendMessage an MainThread senden (https://www.delphipraxis.net/180871-object-mit-sendmessage-mainthread-senden.html)

Captnemo 26. Jun 2014 09:16

Delphi-Version: 5

Object mit SendMessage an MainThread senden
 
Hi,

ich möchte ein Object aus meinem Thread an den Hauptthread senden.

So hab ich mir das vorgestellt. (TLogMessage ist hier nur ein Beispiel, was ich zum Testen genommen habe).

Verschicken:
Delphi-Quellcode:
var
  LogMsg: TLogMessage;
const
   PM_MsgFromThread = WM_USER +4;

procedure TComThread.SendMsgToMain(LogText: string; Debug: Boolean);
begin
  LogMsg:=TLogMessage.Create;
  LogMsg.sLogText:=LogText;
  LogMsg.bIsDebug:=Debug;
  SendMessage(FMainFormHandle, PM_MsgFromThread, Integer(@LogMsg), 0);
end;
Empfangen im Hauptthread:
Delphi-Quellcode:
procedure Tfrm_main.LogMsgFromThread(var msg: TMessage);
var
  P: TLogMessage;
  fdebug: Boolean;
  fLogText: string;
begin
  P:=Pointer(msg.WParam);
  fdebug:=P.bIsDebug;
  fLogText:=P.sLogText;
  //P.Free;                     //Ich müßte das Object auch wieder freigeben.
end;
Ich befürchte aber, dass ich es mal komplett falsch mache.
Auf P.bIsDebug kann ich noch zugreifen, und bei P.sLogText knallt es mit einer Zugriffsverletzung.

himitsu 26. Jun 2014 09:38

AW: Object mit SendMessage an MainThread senden
 
Integer ist nicht gut, stell dir mal vor man verwendet den Code mal im Win64.
NativeInt, oder für Pointer besser IntPtr, oder im Fall von SendMessage/PostMessage den OriginalTyp LRESULT.
Delphi-Quellcode:
function SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;


Was mag wohl passieren, wenn mal zwei Threads zugleich diese Variable nutzen wollen?



Objekte sind schon Pointer und lassen sich problemlos in "Integer" konvertieren, aber egal wie, man muß das schon richtig machen. :stupid:
Delphi-Quellcode:
LPARAM(@LogMsg) => PPointer(msg.LParam)^
LPARAM(LogMsg) => PPointer(msg.LParam) // ohne externe Variable
Also warum ist LogMsg eine globale Variable, und warum gibt es die überhaupt? :stupid:



Und ja, das müsstest du. (Free)

Captnemo 26. Jun 2014 10:11

AW: Object mit SendMessage an MainThread senden
 
Zitat:

Zitat von himitsu (Beitrag 1263537)
Objekte sind schon Pointer und lassen sich problemlos in "Integer" konvertieren, aber egal wie, man muß das schon richtig machen. :stupid:
Delphi-Quellcode:
LPARAM(@LogMsg) => PPointer(msg.LParam)^
LPARAM(LogMsg) => PPointer(msg.LParam) // ohne externe Variable
Also warum ist LogMsg eine globale Variable, und warum gibt es die überhaupt? :stupid:



Und ja, das müsstest du. (Free)

Global, so wie ich es aus dem verstanden habe, was ich gelesen habe, muß sie ja global sein, damit sie im Heap liegt und auch von anderen Threads überhaupt gelesen werden kann, oder ist das jetzt wieder falsch?

Aber ich hab jetzt meinen Fehler gefunden, lag in der Deklaration von P
Delphi-Quellcode:
  P: ^TLogMessage;
So funktioniert's zwar, aber beim Free hab ich immer noch eine Exception.

Das mit den Int64 macht natürlich Sinn.
Also so:
Delphi-Quellcode:
SendMessage(FMainFormHandle, PM_MsgFromThread, Intptr(@LogMsg), 0);
?

Leider hab ich Probleme, deine durchaus sinnvollen und hilfreichen Anmeldungen in meinen oben beschriebenen Code umzusetzen. Weil ich tatsächlich nicht weiß, ob ich das jetzt richtig interpretiert habe. Problematisch ist halt immer, wenn es oft mehrere Möglichkeiten gibt, genau das gleiche zu machen. Und wenn man dann in dem Thema nicht fit ist, dann würfelt man (in dem Fall ich) halt schnell was durcheinander.

stoxx 26. Jun 2014 11:23

AW: Object mit SendMessage an MainThread senden
 
müsste eigenlich sein: (ohne @)

Delphi-Quellcode:
Integer(LogMsg)
Weil ein Object ist schon selbst ein Pointer.
Ein TObject auf Integer zu casten hat eigentlich immer funktioniert.

Captnemo 26. Jun 2014 11:39

AW: Object mit SendMessage an MainThread senden
 
Dann bekomme ich wieder beim zugriff auf sLogText eine Zugriffsverletzung.

Ich hab's jetzt so:

Senden:
Delphi-Quellcode:
procedure TComThread.SendMsgToMain(LogText: string; Debug: Boolean);
begin
  LogMsg:=TLogMessage.Create;
  LogMsg.sLogText:=LogText;
  LogMsg.bIsDebug:=Debug;
  SendMessage(FMainFormHandle, PM_MsgFromThread, Intptr(@LogMsg), 0);
end;
Empfangen:
Delphi-Quellcode:
var
  f: TextFile;
  fname: string;
  P: ^TLogMessage;
  fdebug: Boolean;
  fLogText: string;
begin
  P:=Pointer(msg.WParam);
  fdebug:=P.bIsDebug;
  fLogText:=P.sLogText;
  //TLogMessage(p).free;
end;
Funktioniert auch gut. Aber ich müßte das Object auch mal wieder Freigeben. Aber das Problem ist, ich weißt nicht wirklich wie?
So wie oben? oder p.free?

Blup 26. Jun 2014 12:41

AW: Object mit SendMessage an MainThread senden
 
Du versendest die Adresse einer lokalen Variablen (@LogMsg) in der die Adresse des Objektes gespeichert ist.

Sinnvoller ist es natürlich die Adresse des Objektes zu versenden.
Delphi-Quellcode:
SendMessage(FMainFormHandle, PM_MsgFromThread, Integer(Pointer(LogMsg)), 0);

{...}
var
  P: TLogMessage;
begin
  P := Pointer(msg.WParam);
  {...}
end;
Vermutlich ist dir nicht klar, das diese Nachricht mit SendMessage noch innerhalb des Absender-Threads ausgeführt wird!
Wenn das Objekt beim Empfänger nur wärend der Verarbeitung der Nachricht benötigt wird, sollte es beim Absender danach wieder freigegeben werden.
Es ist ja nicht sichergestellt, dass der Empfänger die Nachricht auch in jedem Fall erhält.
Delphi-Quellcode:
  LogMsg:=TLogMessage.Create;
  try
    SendMessage(FMainFormHandle, PM_MsgFromThread, Integer(Pointer(LogMsg)), 0);
  finally
    LogMsg.Free;
  end;
Im Gegensatz dazu wird die Nachricht mit Postmessage erst einmal in die Nachrichtenschlange des Fensters geschoben und dort vieleicht irgendwann vom Hauptthread abgeholt.
Hier muss der Empfänger die Freigabe übernehmen. Allerdings ist auch hier nicht sichergestellt, dass die Nachricht den Empfänger erreicht. Dann würde ein Speicherleck entstehen.

Für diesen Fall ist es möglich ein eigene Verwaltung der Nachrichtenobjekte anzulegen (z.B. eine Objektliste auf die aus beiden Threads nur über eine CriticalSection zugegriffen wird). Der Mainthread wird als Observer per Postmessage benachrichtigt, dass neue Nachrichtenobjekte eingegangen sind, muss diese aber selbst abholen. Nicht verarbeitete Nachrichtenobjekte können so auch z.B. beim Beenden des Threads oder spätestens bei der Freigabe der Nachrichtenverwaltung freigegeben werden.

stoxx 26. Jun 2014 12:45

AW: Object mit SendMessage an MainThread senden
 
jetzt übergibst Du einen Pointer von einem Pointer .. also doppelt gemoppelt.
So wie ich das geschrieben habe, geht es auf jeden Fall. Ich habe für sowas sogar extra eine Klasse gebaut, die (urpsrünglich) normale Events als asynchrone Events genau nach dieser Methode vom Thread an den Hauptthread schickt.

Umwandlung dann natürlich:
... nicht Pointer() sondern TLogMessage()


Delphi-Quellcode:
var
  logMessage: TLogMessage;
  fdebug: Boolean;
  fLogText: string;
begin
  logMessage := TLogMessage(msg.WParam);

mit dem doppelt gemoppelten Pointer in Deiner jetzigen Version wäre das dann:

Delphi-Quellcode:
logMessage := TLogMessage(msg.WParam^);

jojo-sp 26. Jun 2014 12:53

AW: Object mit SendMessage an MainThread senden
 
Blup hat recht und ist die beste Lösung,

Du läufst Gefahr, wenn du z.B. den Text direkt in einem Label ausgeben möchtest, dass du deinen Thread mit der VCL synchronisieren musst und je nach Menge der Messages deinen Thread damit auch ausbremst.

Generier eine
Delphi-Quellcode:
ObjectList<TLogMessage>
und schließe einen Gegenseitigen Zugriff auf die Liste mit den Critical Sections aus.

Du brauchst dann auch nur den Index der neuesten Nachricht an den Empfänger senden anstatt des Objects oder eines Pointers auf das Object.

Klaus01 26. Jun 2014 12:56

AW: Object mit SendMessage an MainThread senden
 
Zitat:

Zitat von jojo-sp (Beitrag 1263567)
Du brauchst dann auch nur den Index der neuesten Nachricht an den Empfänger senden anstatt des Objects oder eines Pointers auf das Object.

.. wenn zwischenzeitlich nichts aus der Liste gelöscht wird,
ansonsten ist der überlieferte Index nicht mehr korrekt.

Grüße
Klaus

jojo-sp 26. Jun 2014 13:00

AW: Object mit SendMessage an MainThread senden
 
Stimmt!

Eigentlich sollte der Index eh irrelevant sein, da der Empfänger alle Nachrichten nacheinander abarbeitet (arbeiten sollte) und dann entfernt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:47 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz