Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi PostMessage: Objekte "verschicken" führt zu Access Violation (https://www.delphipraxis.net/158809-postmessage-objekte-verschicken-fuehrt-zu-access-violation.html)

s.h.a.r.k 4. Mär 2011 01:41

PostMessage: Objekte "verschicken" führt zu Access Violation
 
Gute Nacht,

ich steh gerade glaub übelst auf dem Schlauch und hoffe, dass ihr mir helfen könnt. Hintergrund: Ich schreibe gerade eine Logging-Klases in der eine Add-Methode ein Objekt erzeugt, also die Message die geloggt werden soll. Via PostMessage schicke ich dann die Referenz auf das Objekt an verschiedene Module. Hier mal der Auszug, der das Erzeugen und Versenden beinhaltet:
Delphi-Quellcode:
procedure TApLog.Add(const AMessage: String; const ACategory: TApLogCategory);
var
  OriginalMsg : TApLogMessage;
begin
  OriginalMsg := TApLogMessage.Create(AMessage, ACategory);
  try
    SendToModules(OriginalMsg);
  finally
    OriginalMsg.Free();
  end;
end;

procedure TApLog.SendToModules(Source: TApLogMessage);
var
  i : Integer;
  ClonedMsg : TApLogMessage;
begin
  // Clone and send message to each module
  for i := 0 to FModuleHandles.Count - 1 do
  begin
    ClonedMsg := TApLogMessage.Create(Source);
    PostMessage(FModuleHandles[i], APM_LOG_MESSAGE, Integer(@ClonedMsg), 0);
  end;
end;
So, nun zum Empfängercode -- hier wird der Pointer dereferenziert (also das "Objekt") in eine Queue gespeichert:
Delphi-Quellcode:
procedure TApLogCustomModule.APMRecieveMessage(var Msg: TMessage);
var
  p : ^TApLogMessage;
begin
  try
    p := Pointer(Msg.WParam);
    FMessageQueue.Enqueue(p^);
    while (Cardinal(FMessageQueue.Count) > FMaxBufferElements) do
      FMessageQueue.Dequeue().Free();
  finally
    Msg.Result := 1;
  end;
end;
Der Pointer wird korrekt verschickt und stellt somit nicht das Problem dar. Allerdings erhalte ich eine Zugriffsverletztung, wenn die Enqueue()-Methode aufgerufen wird. Zudem habe ich mit der Überwachung festgestellt, dass sicher hinter p kein TApLogMessage verbirgt, also als wäre es freigegeben worden. Aber das geschieht nirgends! Ich habe das mind. 5 mal geprüft.

Nun drängt sich mir die Frage auf: Wird Speicher vom Delphi-Speichermanager überschrieben, wenn keine Referenz mehr darauf exisitert? So weit ich weiß, ist das aber doch nicht der Fall?! Anders kann ich mir aber nicht erklären, warum ich sonst die Zugriffsverletztung erhalte.

PS: Nein, ich werde und will kein SendMessage verwenden.

Björn Ole 4. Mär 2011 03:50

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Was das Senden angeht: keinen Pointer darauf (doppelt gemoppelt), sondern das Objekt selbst:
Delphi-Quellcode:
PostMessage(FModuleHandles[i], APM_LOG_MESSAGE, Integer(ClonedMsg), 0);

hoika 4. Mär 2011 07:00

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Hallo,

ausserdem würde ich nicht wParam, sondern lParam benutzen.
Oder ist Word seit neuestem 32Bit gross ?


Heiko

implementation 4. Mär 2011 07:03

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Was sind das für Module? Befinden sie sich in einem eigenen Prozess?
Dann hat der Empfänger evtl. einfach keinen Zugriff auf den Speicherraum des Senders.

Blup 4. Mär 2011 08:33

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Und natürlich auch den Empfang des Objektes anpassen:
Delphi-Quellcode:
procedure TApLogCustomModule.APMRecieveMessage(var Msg: TMessage);
var
  p : TApLogMessage;
begin
  try
    p := Pointer(Msg.WParam);
    FMessageQueue.Enqueue(p);
    while (Cardinal(FMessageQueue.Count) > FMaxBufferElements) do
      FMessageQueue.Dequeue().Free();
  finally
    Msg.Result := 1;
  end;
end;
PS.
WParam und LParam sind beide jeweils 32Bit.

himitsu 4. Mär 2011 08:55

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Um den Grund nochmal zu nennen:
Du versendest einen Zeiger auf die Variable und nach Beenden der Prozedur ist diese Variable nicht mehr vorhanden.

PS: Ein Objektzeiger ist, wie der Name vermuten läßt, intern auch "nur" ein Zeiger, weswegen man ihn auch direkt casten kann.

rollstuhlfahrer 4. Mär 2011 11:24

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Versuche es mal mit SendMessage. Da damit die Ausführung direkt durchgeführt wird, sind die lokalen Variablen der aufrufenden Funktion noch vorhanden. Bei PostMessage sind diese meist schon wieder aufgeräumt, da du .Free aufrufst, bevor die Nachricht überhaupt ankam.

Bernhard

jfheins 4. Mär 2011 11:27

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1085751)

Delphi-Quellcode:
...
  finally
    OriginalMsg.Free();...
... also als wäre es freigegeben worden. Aber das geschieht nirgends! Ich habe das mind. 5 mal geprüft.

*rofl* An der Stelle musste ich echt lachen :mrgreen:

Mit records würde das funktionieren, ansonsten darfst du das Objekt nicht freigaben wenn du es noch verwenden willst! Bist du nen GC gewohnt?

@himi: Wenn man ein Objekt erzeugt, sollte das aber doch eigentlich auf dem Heap geschehen, also sollte das eigentliche Objekt (sofern man es nicht freigibt) doch auch nach der Funktion vorhanden sein, oder?

@hoika: Beide Parameter sind 32 bit groß. Trotz des Namens ;)

himitsu 4. Mär 2011 11:37

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Zitat:

Zitat von jfheins (Beitrag 1085835)
@himi: Wenn man ein Objekt erzeugt, sollte das aber doch eigentlich auf dem Heap geschehen, also sollte das eigentliche Objekt (sofern man es nicht freigibt) doch auch nach der Funktion vorhanden sein, oder?

Das Objekt ja, aber die lokale Variable, worin der Objektzeiger abgelegt wurde und worauf der übergebene Zeiger zeigt, die wird natürlich spätestens zum Prozedurende weg sein.

Bummi 4. Mär 2011 11:40

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
@jfheins
Zitat:

*rofl* An der Stelle musste ich echt lachen
wieso, er erzeugt doch einen Clone
Delphi-Quellcode:
ClonedMsg := TApLogMessage.Create(Source);

jfheins 4. Mär 2011 11:50

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Zitat:

Zitat von Bummi (Beitrag 1085839)
@jfheins
Zitat:

*rofl* An der Stelle musste ich echt lachen
wieso, er erzeugt doch einen Clone

Ups, das hatte ich übersehen ... :oops:

s.h.a.r.k 4. Mär 2011 13:16

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Erst mal vielen Dank für die Antworten! Dann fangen wir mal ganz oben an:

Zitat:

Zitat von Björn Ole (Beitrag 1085753)
Was das Senden angeht: keinen Pointer darauf (doppelt gemoppelt), sondern das Objekt selbst:
Delphi-Quellcode:
PostMessage(FModuleHandles[i], APM_LOG_MESSAGE, Integer(ClonedMsg), 0);

Werde ich gleich testen und dann Bericht erstatten.

Zitat:

Zitat von hoika (Beitrag 1085762)
ausserdem würde ich nicht wParam, sondern lParam benutzen. Oder ist Word seit neuestem 32Bit gross ?

Seit geraumer Zeit ist selbst WPARAM ein Integer. Habe sogar im Source-Code der VCL nachgeschaut, da ich da auch einen Fehler vermutet hatte. Aber wie schon erwähnt: es kommt der richtige Pointer in der Recieve-Methode an, ganz ohne Zweifel!

Zitat:

Zitat von implementation (Beitrag 1085764)
Was sind das für Module? Befinden sie sich in einem eigenen Prozess? Dann hat der Empfänger evtl. einfach keinen Zugriff auf den Speicherraum des Senders.

Die Module werden bisher im selben Prozess und im selben Thread erzeugt! Unter Module verstehe ich eine Klasse, die von einer Grundklasse abgeleitet, die Logs entgegennimmt und verarbeitet, z.B. TApLogFileModule schreibt die eingehend Log-Messages in eine Datei.

Zitat:

Zitat von rollstuhlfahrer (Beitrag 1085834)
Versuche es mal mit SendMessage. Da damit die Ausführung direkt durchgeführt wird, sind die lokalen Variablen der aufrufenden Funktion noch vorhanden. Bei PostMessage sind diese meist schon wieder aufgeräumt, da du .Free aufrufst, bevor die Nachricht überhaupt ankam.

Habe doch schon erwähnt, dass ich auf SendMessage verzichten will :) Der Vollständigkeit halber hier noch die Begründung: ich will nicht, dass meine Log-Klasse anderen Code blockiert und evtl. ausbremsts. Daher will ich, dass die Module erst dann mit der Verarbeitung beginnen, wenn weiter Nachrichten verarbeitet werden. Somit sollte es weniger blockierend sein.

Und nein, ich rufe Free nur auf dem Original-Objekt auf, nicht auf den geklonten Versionen!
Zitat:

Zitat von jfheins (Beitrag 1085835)
Zitat:

Zitat von s.h.a.r.k (Beitrag 1085751)

Delphi-Quellcode:
...
  finally
    OriginalMsg.Free();...
... also als wäre es freigegeben worden. Aber das geschieht nirgends! Ich habe das mind. 5 mal geprüft.

*rofl* An der Stelle musste ich echt lachen :mrgreen:

Mit records würde das funktionieren, ansonsten darfst du das Objekt nicht freigaben wenn du es noch verwenden willst! Bist du nen GC gewohnt?

Tja, nur zerstöre ich hier ein Objekt, welches wirklich freigegeben werden soll ;) Schau mal -- wie Bummi
schon bemerkt hat -- in die SendToModules() Methode. Dort klone ich die Log-Message genau so oft, wie ich registrierte Module habe. Somit bekommt jedes Modul sein eignes Log-Message-Objekt.

Was meinst du mit GC? GNU Compiler? Games Convention? ;)

Zitat:

Zitat von jfheins (Beitrag 1085835)
@himi: Wenn man ein Objekt erzeugt, sollte das aber doch eigentlich auf dem Heap geschehen, also sollte das eigentliche Objekt (sofern man es nicht freigibt) doch auch nach der Funktion vorhanden sein, oder?

Davon ging ich aus. Aber aus irgendeinem Grund scheint das Objekt nicht mehr zu existieren. Daher meine Befürchtung, dass genau der Speicherbereich aufgeräumt oder überschrieben wurde.

s.h.a.r.k 4. Mär 2011 13:22

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
So, himitsu hatte Recht. Nachdem ich das nun getestet hatte, habe ich nochmal seine Antwort gelesen und verstanden. Nun verstehe ich auch warum es nicht geklappt hat. Herzlichen Dank! Da wäre ich bei Gott nicht drauf gekommen.

himitsu 4. Mär 2011 13:28

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1085873)
Davon ging ich aus. Aber aus irgendeinem Grund scheint das Objekt nicht mehr zu existieren. Daher meine Befürchtung, dass genau der Speicherbereich aufgeräumt oder überschrieben wurde.

Deine Variable, auf die du zugreifen wolltest, war ja wirklich weg, also stimmte der Fehler schon. :wink:

Bei PostMessage müßte man eventuell nochaufpassen, daß der MessageQueue nicht überläuft.
Sind zuviele unverarbeitete Messages vorhanden, dann wird deine Message nicht eingetragen und keiner gibt mehr das Objekt frei.
Delphi-Quellcode:
if not PostMessage(..., LPARAM(x)) then begin
  x.Free;
  // und eventuell noch eine Fehlermeldung
end;
PS: Irgendein "Idiot" hat beschlossen, daß Integer nicht mehr mitwächst, also unter 64 Bit bleibt das Ding 32 Bit klein.
Also nimm' lieber den direkten Typen für den lParam-Parameter.

s.h.a.r.k 4. Mär 2011 13:35

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Jo, seltsamerweise hat es in einer vorherigen Version irgendwie funktioniert gehabt. Daher bin ich wohl auch nicht selbst drauf gekommen. Naja, nun funktioniert es ;)

Danke für den Hinweis mit der Message-Anzahl. Werde ich nun berücksichtigen!

Und zum Thema 32- vs. 64-Bit: Wie meinst du das? wParam und lParam bleiben 32-Bit Integers? Was verstehst du unter direktem Typ? Nicht den Cast via Integer() sondern via LPARAM()?

himitsu 4. Mär 2011 13:38

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
WPARAM und LPARAM sollten wachsen, sowie auch der Pointer, aber Integer leider nicht, weswegen du dir beim Cast von Pointer auf Integer so knapp die Häfte der Bits wegschneiden würdest. :wall:
(ja, ich weiß, man könnte glattt glauben daß der Integer auf 64 bit wird, so wie er damals mal 16 Bit war,
aber aus unerfindlichen Gründen hat man sich dagegenentschieden ... diesmal liegt aber die Schuld wenigstens nicht beim Delphi :D, außer daß wir jeden Sch*** nachmachen müssen )

s.h.a.r.k 4. Mär 2011 13:43

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Sehr seltsame Entscheidung... Warum hat man dann string als UnicodeString definiert? Aber okay, danke für die Hinweis!

himitsu 4. Mär 2011 13:46

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Wie gesagt, das wurde vor vielen Jahren schon in C, C++ und Co. so eingeführt und wir machen das jetzt nach.
(tja, wer zuspät kommt, den bestraft das Leben)

s.h.a.r.k 4. Mär 2011 13:49

AW: PostMessage: Objekte "verschicken" führt zu Access Violation
 
Solange man dann explizit darauf hingewiesen wird, sollte es zu keinerlei Problemen führen. Der Compilier sollte an der Stelle dann nur eine entsprechende Warning melden. Dann wäre es durchaus vertretbar.


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:56 Uhr.

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