Link to home
Create AccountLog in
Delphi

Delphi

--

Questions

--

Followers

Top Experts

Avatar of glenn_benschop
glenn_benschop

messagebox do not display in a Thread
Hi,
I found some code about Thread (it was a link from this forum), a nice project, but the messagebox in it will not dsiplay. There are no syntax errors. When I debug the source, then the messagebox pops up., but when I run the program normally it doesn't. Also sometimes I get the error of : unknown software exception(...) occured in the application at (....), I click [OK] en then the following error occured: Exception EWin32error in Project1.exe at 00009D9c, code 1400, invalid window handle.
I'm using delphi 5 and Windows 2000 (SP4), the project has two units: it's about a project that's checking if a number is Prime or not. In both cases a message must be displayed. Can somebody help?

{****Begin First unit***************}
unit PrimeForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TPrimeFrm = class(TForm)
    NumEdit: TEdit;
    SpawnButton: TButton;
    procedure SpawnButtonClick(Sender: TObject);
  private
      { Private declarations }
  public
      { Public declarations }
  end;

var
  PrimeFrm: TPrimeFrm;

implementation

uses PrimeThread;

{$R *.DFM}

procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);

var
  NewThread: TPrimeThrd;

begin
  NewThread := TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate := True;
  try
    NewThread.TestNumber := StrToInt(NumEdit.Text);
    NewThread.Resume;
  except on EConvertError do
    begin
      NewThread.Free;
      ShowMessage('That is not a valid number!');
    end;
  end;
end;

end.
{****End of First unit**********}

{****Begin Second unit ************}
unit PrimeThread;

interface

uses
  Classes;

type
  TPrimeThrd = class(TThread)
  private
    FTestNumber: integer;
  protected
    function IsPrime: boolean;
    procedure Execute; override;
  public
    property TestNumber: integer write FTestNumber;
  end;

implementation

uses SysUtils, Dialogs;

function TPrimeThrd.IsPrime: boolean;

var
  iter: integer;

begin
  result := true;
  if FTestNumber < 0 then
  begin
    result := false;
    exit;
  end;
  if FTestNumber <= 2 then
    exit;
  for iter := 2 to FTestNumber - 1 do
  begin
    if (FTestNumber mod iter) = 0 then
    begin
      result := false;
      {exit;}
    end;
  end;
end;

procedure TPrimeThrd.Execute;
begin
  if IsPrime then
    ShowMessage(IntToStr(FTestNumber) + 'is prime.')
  else
    ShowMessage(IntToStr(FTestNumber) + 'is not prime.');
end;

end.
{****End of Second unit**********}

Zero AI Policy

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of anorganixanorganix

Hi!
You said that your project is about checking if a number is Prime or not. Here is the code (no need for PrimeThread.pas).

//////////////// begin code /////////////////

procedure TMainForm.CheckNumberButtonClick(Sender: TObject);
var
  I, J: integer;
begin
  if not TryStrToInt(Edit.Text,I) then
  begin
    MessageDlg('Please enter a valid integer value!',mtWarning,[mbOK],0);
    Exit;
  end;

  if I<=0 then
  begin
     MessageDlg('Please enter a positive integer value!',mtWarning,[mbOK],0);
     Exit;
  end;

  if I<=2 then
    Exit;

  for J:=2 to I-1 do
  begin
    if (I mod J)=0 then
      MessageDlg(IntToStr(I)+' is not a prime number!',mtInformation,[mbOK],0)
    else
      MessageDlg(IntToStr(I)+' is a prime number!',mtInformation,[mbOK],0);

    Break;
  end;
end;

////////////////// end code ////////////////////

Hope it helps!

 :: Cosmin

Avatar of glenn_benschopglenn_benschop

ASKER

Well, The prime function was needed to understand how thread works. I need to undersdtand how thread works to solve another problem. This example will be expand with more code. You can consider it a course. But in order to go further with the example, I want this one to work correctly otherwise it is worthless to put more code to it. So the code is all about thread learning, and the Isprime function was needed as an example. I'm looking for futher comments.

ShowMessage is part of the VCL which is not threadsafe. Use Windows.MessageBox.

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


ASKER CERTIFIED SOLUTION
Avatar of gwalkeriqgwalkeriq

Link to home
membership
Log in or create a free account to see answer.
Signing up is free and takes 30 seconds. No credit card required.
Create Account

hello glenn_benschop , , To use threads it is often nessary to know something about the windows API methods and things that the system does (and does not do) with threads. There is alot of info about threads, but you ask about why the VCL ShowMessage(  ) does not work in a separate thread, gwalkeriq  gives a link to a "thread for Delphi information web site", I looked at some of the pages there, and it seems to give much information about data access across threads (thread synconozation), but I did not see any info there about why the ShowMessage thing does not work in another thread, it is NOT because of thread sync, although that is a factor.
I will start by saying, that the windows system has threads to give processor time segments (time slice) to processes and threads, so to a windows user, it seems like that more than one thread or process is doing something at the same time, however since most computers have only one processor, there can be only one thing being done by the processor at any time. The system will switch the thread using the processor when the time alotment (time slice) for that thread ends or the request for processing from that ends (thread finishes processing or releases its processor time slice). MS says -
A multitasking operating system divides the available processor time among the processes or threads that need it. Windows is designed for preemptive multitasking; it allocates a processor time slice to each thread it executes. The currently executing thread is suspended when its time slice elapses, allowing another thread to run.
The system thread scheduler controls multitasking by determining which of the competing threads receives the next processor time slice. The length of the time slice depends on the operating system and the processor. Because each time slice is small (small for "People" time, approximately 20 milliseconds), to the user multiple threads appear to be executing at the same time.
I will not say anything about thread data syncronozation, except that I have seen several many references that all say the 32-Bit processor can NOT ever access (read, write) a memory block of Less than 32-Bits (4 Bytes), so in my thread usage, for some 32-Bit values I may not use any thread sync at all, to read or write, especially for reading, if incorrect data will not cause an exception. But there are plenty of places that say you must always sync up your thread data access.
Now for the reason that ShowMessage( ) does not work. . To properly keep track of threads and resources assigned to threads, I beleive that there is a system "list of windows created in that thread" maintained for each thread, the windows system has a window message processing (message handling and a message-queue) for each thread, that processes that thread's window messages for only the windows beloning to that thread. The ShowMessage( ) creates a pop-up window (Form) that is owned by the Application window, if you do a Showmessage( ) in a separate thread, there is no GetMessage loop (or other message handling) created for that separate thread by the Forms unit, so the ShowMessage does not work since the Application GetMessage loop is in another thread processing messages from that thread's message-queue.
Since the default message handling methods for forms are all related to the Application window, you probally should create forms ONLY in the main thread, not in any other threads.
You can create windows that work correctly in another thread, but this will require API methods of the system, not methods of the delphi Application window.

For your question, I would not create a window in that thread, even a MessageBox( ) window, since those windows would NOT make the windows of another thread (main thread) Modal. . I would use the API SendMessage( ) function, to get a form in the main thread to use ShowMessage( ) and have a Modal window.
Some code for Form2 - -



const
  MsgToThread = WM_USER + 130;


type
  TShowMsgThread = class(TThread)
  protected
    FFormHnd: THandle;
    procedure Execute; override;
  public
    constructor Create(FormHnd: THandle);
  end;


  TForm2 = class(TForm)
    but_ThreadSendMsg: TButton;
    procedure but_ThreadSendMsgClick(Sender: TObject);
  private
    { Private declarations }
    procedure ThreadMsg(var Msg: TMessage); message MsgToThread;
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}


constructor TShowMsgThread.Create(FormHnd: THandle);
begin
FreeOnTerminate := True;
inherited Create(False);
FFormHnd := FormHnd;
end;

procedure TShowMsgThread.Execute;
var
MsgRe: Integer;
begin
// MessageBox( ) shows a window, but does not make main thread windows modal
MessageBox(0,'Thread MessageBox with text display'#10'Does NOT have forms in other threads as Modal',
           'Thread MessageBox', MB_ICONINFORMATION);
// I read a 32-bit Application.Handle with-out thread-sync
// even if this Application.Handle is NON-Correct Data, it does not matter since
// the SetForeGroundWindow( ) will not exception, even with bad data in Application.Handle
SetForeGroundWindow(Application.Handle);
if not IsWindow(FFormHnd) then Exit;
Sleep(2000);
// No Form windows are created in this thread
MsgRe := SendMessage(FFormHnd, MsgToThread, 37, 100);
if MsgRe <> 37 then Exit;
Sleep(2000);
SendMessage(FFormHnd, MsgToThread, 37, 54321);
end;

// / / / / / / / / / / / / / / / / / / / / / / / / /


procedure TForm2.ThreadMsg(var Msg: TMessage);
begin
if Msg.WParam = 37 then
  begin
  Msg.Result := 37;
  ShowMessage('This is message 37 from thread '+IntToStr(Msg.LParam)+#10'Forms in this thread ARE Modal');
  end else
  if Msg.WParam = 73 then
  begin
  Msg.Result := 73;
  ShowMessage('73 - This is message 73 from thread '+IntToStr(Msg.LParam));
  end
end;


procedure TForm2.but_ThreadSendMsgClick(Sender: TObject);
begin // button click
TShowMsgThread.Create(Handle);
end;

end;

If you want to create a window in another thread you might try this code for form2 - -


type
  TShowWndThread = class(TThread)
  private
    FMsg1: String;
  protected
    procedure Execute; override;
  public
    constructor Create(const Msg: String);
    destructor Destroy; override;
  end;


// / / / / / / / / / / / / /  / / /


implementation

{$R *.DFM}


const
ID_OKBut = 100;

var
aSelf: TShowWndThread;
hThreadWnd: THandle = 0;

function WndProc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
begin
case Msg of
  WM_DESTROY: PostQuitMessage(0);
  WM_COMMAND: if LOWORD(wParam) = ID_OKBUT then PostMessage(hWnd,WM_CLOSE,0,0);
  end;
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;

constructor TShowWndThread.Create(const Msg: String);
begin
FreeOnTerminate := True;
inherited Create(False);
if aSelf <> nil then
  Raise Exception.Create('Create Failed - There can only be one instance of TAskThread');
aSelf := Self;
FMsg1 := Msg;
end;

destructor TShowWndThread.Destroy;
begin
aSelf := nil;
inherited Destroy;
end;


procedure TShowWndThread.Execute;
var
wClass:   TWndClass;
mainMsg: TMSG;
begin
ZeroMemory(@wClass, SizeOf(wClass));

with wClass do
  begin
  hInstance := sysInit.hInstance;
  hIcon := LoadIcon(0,IDI_QUESTION);
  lpfnWndProc := @WndProc;
  hbrBackground := COLOR_BTNFACE+1;
  lpszClassName := 'TMW Class';
  hCursor := LoadCursor(0,IDC_ARROW);
  end;

Windows.RegisterClass(wClass);

hThreadWnd := CreateWindowEX(0,wClass.lpszClassName,'No See',
          WS_SYSMENU or WS_DLGFRAME or WS_CAPTION or WS_VISIBLE,
          140,140,180,110,0,0,hInstance,nil);

CreateWindow('BUTTON', 'O K',
    WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or WS_TABSTOP,
    57,50,66,24,hThreadWnd,ID_OKBut,hInstance,nil);

SendMessage(CreateWindow('STATIC', PChar(FMsg1), WS_VISIBLE or WS_CHILD or SS_CENTER,
    1,1,173,46,hThreadWnd,46,hInstance,nil),
    WM_SETFONT,GetStockObject(ANSI_VAR_FONT),0);


// IMPORTANT
// below is the GetMessage loop for this thread, which does ONLY window message for this thread
// without it the CreateWindow( ) functions abouve are useless
// the ShowMessage( ) procedure does NOT place this in any thread
if hThreadWnd <> 0 then
  while GetMessage(mainMsg,0,0,0) do
    if not IsDialogMessage(hThreadWnd, mainMsg) then
      begin
      TranslateMessage(mainMsg);
      DispatchMessage(mainMsg);
      end;
end;

// / / / / / / / / / / / / / / / /


procedure TForm2.but_ThreadSendMsgClick(Sender: TObject);
begin // button click
TShowMsgThread.Create(Handle);
end;

initialization

finalization
PostMessage(hThreadWnd, WM_DESTROY, 0,0);

end.

 = = = = = = = = = = = = = = = = = = = = = = =
maybe this give you alittle more info?

My example is taken from the link that gWalkeriq suggested( http://www.pergolesi.demon.co.uk/prog/threads/ToC.html) It is from chapter 2.  as I understand from this link and the comments of Slick812, the VCL is not threadsafe, so the eaxample from the link is wrong. They went futher in  chapter 3 and they use a memo instead of a messagebox to display the result of Prime function and that works (I tried it myself), because then they use the function Synchronize. So, if I want to, let say repaint my form, I have to call synchronize. What do I have to do with the points, I don't feel like the comments solved the problem, although the statements in the comments were true, so I 'll keep the points. But, many thanks for all your efforts and  comments to my question.

Here is the working code of the second unit (with a memo and the Synchornize function.

unit PrimeThread;

interface

uses
  Classes;

type
  TPrimeThrd = class(TThread)
  private
    FTestNumber: integer;
    FResultString: string;
  protected
    function IsPrime: boolean;
     procedure UpdateResults;
    procedure Execute; override;
  public
    property TestNumber: integer write FTestNumber;
  end;

implementation

uses SysUtils, Dialogs, PrimeForm;

function TPrimeThrd.IsPrime: boolean;

var
  iter: integer;

begin
  result := true;
  if FTestNumber < 0 then
  begin
    result := false;
    exit;
  end;
  if FTestNumber <= 2 then
    exit;
  for iter := 2 to FTestNumber - 1 do
  begin
    if (FTestNumber mod iter) = 0 then
    begin
      result := false;
      {exit;}
    end;
  end;
end;

procedure TPrimeThrd.UpdateResults;
begin
  PrimeFrm.ResultsMemo.Lines.Add(FResultString);
  showmessage(FResultString);  //also works


end;

procedure TPrimeThrd.Execute;
begin
  if IsPrime then
      FResultString := IntToStr(FTestNumber) + ' is prime.'
  else
    FResultString := IntToStr(FTestNumber) + ' is not prime.';
     Synchronize(UpdateResults);

  {
  if IsPrime then
    ShowMessage(IntToStr(FTestNumber) + 'is prime.')
  else
    ShowMessage(IntToStr(FTestNumber) + 'is not prime.');
   }
end;

end.


Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


I read your last comment above, but I did not understand what you were talking about. I tried to get what you may be refering to in your comment, but I think I may be "Mixing Up" tech terms. Your question here was about why the ShowMessage did not work in a separate thread, I tried to give you some info about that. But it may be that we are not talking about the same thing, to me there is a difference between a method (Showmessage) "working" in a thread and being "called" from a thread. My comments (and  maybe others) were for runnning that code in the TTHread , not calling that code from the thread. In your comment code above you use the
    Synchronize(UpdateResults);
that adds to the Memo and uses ShowMessage( ), I seemed to get from your comment that you think that the memo addition and ShowMessage are executed in that Thread, This is NOT the case - - they are executed in the main thread, because you call the  -
Synchronize( ) method, it is true that you "call" these methods (ShowMessage) in the code for your TThread, but they are "working" (processor time slice) in the main thread, not in your thread's time slice. I even looked at the explanation in the web site you gave for chapter three, and I think he says this about Syncronize, and even has a chart that shows you that the TThread is suspended and the main thread is allowed to run the code in the Syncronize( ) method.

here is the code I have in Classes.pas for the TThread.Synchronize -


procedure TThread.Synchronize(Method: TThreadMethod);
begin
FSynchronizeException := nil;
FMethod := Method;
SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self));
// please notice that this uses the thread-safe and thread-efficient API SendMessage( )
// which runs the code in the FMethod in the main thread, not in this thread
if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;

I guess I did not understand what you were asking?
The  Synchronize( )   TThread method is what you use to run code in the main thread, it has this in the Delphi help, it says -

 "Executes a method call within the main VCL thread " but I did not think your question was about that.


Slick812 and ohters,
I should be also more specific, I apologize for that. Next time I'll be more precisely.

By the way, using  Synchronize( ) is thread safe , , ,
I will guess that you may not get any more posts here,  you may should finalize this question. . . .
as to your points question -
since gwalkeriq  gave you the link to the Info that you used to develop the code that worked (display the ShowMessage, this a correct way to do that), here at EE it seems the usual is that you might should award him the points. . . ?

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


Slick812,
You're right about the points, I will give those to gwalkeriq.
Delphi

Delphi

--

Questions

--

Followers

Top Experts

Delphi is the most powerful Object Pascal IDE and component library for cross-platform Native App Development with flexible Cloud services and broad IoT connectivity. It provides powerful VCL controls for Windows 10 and enables FMX development for Windows, Mac and Mobile. Delphi is your choice for ultrafast Enterprise Strong Development™. Look for increased memory for large projects, extended multi-monitor support, improved Object Inspector and much more. Delphi is 5x faster for development and deployment across multiple desktop, mobile, cloud and database platforms including 32-bit and 64-bit Windows 10.