Submitting string data from the Subject section to the main form

In Dephi, I create a thread, for example, that will occasionally send a message to the main form

Procedure TMyThread.SendLog(I: Integer); Var Log: array[0..255] of Char; Begin strcopy(@Log,PChar('Log: current stag is ' + IntToStr(I))); PostMessage(Form1.Handle,WM_UPDATEDATA,Integer(PChar(@Log)),0); End; procedure TMyThread.Execute; var I: Integer; begin for I := 0 to 1024 * 65536 do begin if (I mod 65536) == 0 then begin SendLog(I); End; End; end; 

where WM_UPDATEDATA is the custom message defined below:

 const WM_UPDATEDATA = WM_USER + 100; 

And in the main form, it will update the list as follows:

 procedure TForm1.WMUpdateData(var msg : TMessage); begin List1.Items.Add(PChar(msg.WParam)); end; 

However, since the log line sent to the main form is a local variable that will be destroyed after the SendLog call. Although TForm1.WMUpdateData processes the message asynchronously, it is possible that when it was called, the log line was already destroyed. How to solve this problem?

I think that maybe I can allocate space in the global system space and then pass it into a message, after TForm1.WMUpdateData processes this message, it can destroy the string space in the global space. Is this an acceptable solution? How to implement this?

thanks

+7
multithreading delphi message postmessage
source share
3 answers

If you have version D2009 or later, there is another way to send messages to your main form. TThread.Queue is an asynchronous call from a thread, where a method or procedure can be executed on the main thread.

The advantage is that the frame for setting up message transmission is less complicated. Just pass your callback method when creating the stream. There are no descriptors and no explicit row allocation / deallocation handling.

 Type TMyCallback = procedure(const s : String) of object; TMyThread = class(TThread) private FCallback : TMyCallback; procedure Execute; override; procedure SendLog(I: Integer); public constructor Create(aCallback : TMyCallback); end; constructor TMyThread.Create(aCallback: TMyCallback); begin inherited Create(false); FCallback := aCallback; end; procedure TMyThread.SendLog(I: Integer); begin if not Assigned(FCallback) then Exit; Self.Queue( // Executed later in the main thread procedure begin FCallback( 'Log: current stag is ' + IntToStr(I)); end ); end; procedure TMyThread.Execute; var I: Integer; begin for I := 0 to 1024 * 65536 do begin if ((I mod 65536) = 0) then begin SendLog(I); End; End; end; 

 procedure TMyForm.TheCallback(const msg : String); begin // Show msg end; procedure TMyForm.StartBackgroundTask(Sender : TObject); begin ... FMyThread := TMyThread.Create(TheCallback); ... end; 
+9
source share

In addition to sending a local variable, the TWinControl.Handle property TWinControl.Handle also not thread safe. Instead, you should use the TApplication.Handle property or use AllocateHWnd() to create your own window.

You need to dynamically allocate the line on the heap, place the pointer on the main thread, and then free the memory when you are done with it.

For example:

 procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage := AppMessage; // or use a TApplicationEvents component... end; procedure TForm1.FormDestroy(Sender: TObject); begin Application.OnMessage := nil; end; procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean); var S: PString; begin if Msg.Message = WM_UPDATEDATA then begin S := PString(msg.LParam); try List1.Items.Add(S^); finally Dispose(S); end; Handled := True; end; end; 

 procedure TMyThread.SendLog(I: Integer); var Log: PString; begin New(Log); Log^ := 'Log: current stag is ' + IntToStr(I); if not PostMessage(Application.Handle, WM_UPDATEDATA, 0, LPARAM(Log)) then Dispose(Log); end; 

As an alternative:

 var hLogWnd: HWND = 0; procedure TForm1.FormCreate(Sender: TObject); begin hLogWnd := AllocateHWnd(LogWndProc); end; procedure TForm1.FormDestroy(Sender: TObject); begin if hLogWnd <> 0 then DeallocateHWnd(hLogWnd); end; procedure TForm1.LogWndProc(var Message: TMessage); var S: PString; begin if Message.Msg = WM_UPDATEDATA then begin S := PString(msg.LParam); try List1.Items.Add(S^); finally Dispose(S); end; end else Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam, Message.LParam); end; 

 procedure TMyThread.SendLog(I: Integer); var Log: PString; begin New(Log); Log^ := 'Log: current stag is ' + IntToStr(I); if not PostMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(Log)) then Dispose(Log); end; 
+7
source share

Use SendMessage ().

PostMessage () will process your message asynchronously, it is basically placed in the target message queue and returns immediately. While the handler code is accessing data sent to wparam / lparam, your caller has already freed the line.

Unlike SendMessage (), it bypasses the message queue and calls the proc window directly (synchronously). When returning SendMessage (), it is safe to free the string.

0
source share

All Articles