AllocConsole, SetConsoleCtrlHandler prevent terminating - delphi

Before thinking this is a duplicate Question, then please read this: Yes this question is around in on SO in different languages at least Delphi, C# and C++ but they all have something in common: They talk about handling a clean shut down not preventing it.
So here we go:
Form a VCL application I open a new Console Window using AllocConsole but when closing that window with the cross in the top right corner my application terminates. That I would like to prevent not handle!
Some code:
function Handler(dwCtrlType: DWORD): Boolean; cdecl;
begin
case dwCtrlType of
CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_BREAK_EVENT:
Exit(True);
else
Exit(false);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AllocConsole;
SetConsoleCtrlHandler(#Handler, True);
end;
I have read the WinAPI documentation but it doesn't say anything about preventing Application termination.
I have tried adding message handlers for WM_ENDSESSION, WM_QUERYENDSESSION, WM_CLOSE and WM_QUIT on my MainForm but none of them gets called. I've also tried to add a FormCloseQuery event but it doesn't get called either.
I have read and tried out the solution found here but SetConsoleCtrlHandler(nil, True); doesn't provide the application for termination
So in short how to prevent termination.

Related

Exception when calling DLL in delphi?

I have a procedure to call a function named [main()] from a DLL , this is the Caller procedure :
procedure call_dll(path:string);
var
lib: HMODULE;
mainfn: procedure(); stdcall;
begin
if FileExists(path) then
begin
try
lib := LoadLibrary(PAnsiChar(path));
Win32Check(lib <> 0);
try
#mainfn := GetProcAddress(lib, 'main');
Win32Check(Assigned(mainfn));
mainfn();
finally
FreeLibrary(lib);
end;
except
end;
end;
end;
Until yet every thing is working fine , I mean after writing the correct path of the DLL everything work without a problem but if I write a wrong path (other file path) in the path parameter it show me a popup error that this is is not a Win32 DLL but I don't want to bother the user with this type of errors , so I need a function to check the DLL and if it's not then it will automatically ask for another file again without showing the popup error ?
It is your code that is raising the exception. Your code makes an explicit choice to handle errors by raising exceptions. The exception is raised by your code here:
Win32Check(lib <> 0);
If you don't want to raise an exception, don't use Win32Check. Instead check the value of lib and handle any errors by whatever means you see fit.
The same issue is present for your other use of Win32Check.
Of course you are swallowing all exceptions with your catch all exception handler. A catch all exception handler is usually a bad idea. I don't understand why you have included that. I think you should remove it.
So if you are seeing dialogs when running outside the debugger it follows that the system is producing the dialogs. You should be disabling the system's error message dialogs by calling SetErrorMode on startup passing SEM_FAILCRITICALERRORS.
var
Mode: DWORD;
....
Mode := SetErrorMode(SEM_FAILCRITICALERRORS);
SetErrorMode(Mode or SEM_FAILCRITICALERRORS);
The somewhat clunky double call is explained here: http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx

How to avoid exceptions when using TGeckoBrowser in a Delphi app

Prompted by a q here yesterday, I'm trying to re-familiarise myself with TGeckoBrowser
from here: http://sourceforge.net/p/d-gecko/wiki/Home.
(Nb: requires the Mozilla XulRunner package to be installed)
Things seem to have moved backwards a bit since I last tried in the WinXP era, in that
with a minimal D7 project to navigate to a URL, I'm getting errors that I don't recall
seeing before. I've included my code below. These are the errors which I've run into
navigating to sites like www.google.com, news.bbc.co.uk, and here, of course.
The first exception - "Exception in Safecall method" - occurs as my form first displays, before naviagting anywhere at all. I have a work-around in the form of a TApplication.OnException handler.
My q is: a) Does anyone know how to avoid it in the first place or b) is there a tidier way of catching it than setting up a TApplication.Exception handler, which always feels to me like a bit of
an admission of defeat (I mean having one to avoid the user seeing an exception, not having an application-wide handler at all).
This exception occurs in this code:
procedure TCustomGeckoBrowser.Paint;
var
rc: TRect;
baseWin: nsIBaseWindow;
begin
if csDesigning in ComponentState then
begin
rc := ClientRect;
Canvas.FillRect(rc);
end else
begin
baseWin := FWebBrowser as nsIBaseWindow;
baseWin.Repaint(True);
end;
inherited;
end;
in the call to baseWin.Repaint, so presumably it's
presumably coming from the other side of the interface. I only get it the first
time .Paint is called. I noticed that at that point, the baseWin returns False for GetVisibility,
hence the experimental code in my TForm1.Loaded, to see if that would avoid it.
It does not.
2.a After calling GeckoBrowser1.LoadURI, I get "Invalid floating point operation"
once or more depending on the URL being loaded.
2.b Again, depending on the URL, I get: "Access violation at address 556318B3 in module js3250.dll. Read of address 00000008." or similar. On some pages it occurs every few seconds (thanks I imagine to some JS timer code in the page).
2a & 2b are avoided by the call to Set8087CW in TForm1.OnCreate below but I'm
mentioning them mainly in case anyone recognises them and 1 together as symptomatic
of a systemic problem of some sort, but also so google will find this q
for others who run into those symptoms.
Reverting to my q 1b), the "Exception in Safecall method" occurs from StdWndProc->
TWinControl.MainWndProc->[...]->TCustomGeckoBrowser.Paint. Instead of using an
TApplication.OnException handler, is there a way of catching the exception further
up the call-chain, so as to avoid modifying the code of TCustomGeckoBrowser.Paint by
putting a handler in there?
Update: A comment drew my attention to this documentation relating to SafeCall:
ESafecallException is raised when the safecall error handler has not been set up and a safecall routine returns a non-0 HResult, or if the safecall error handler does not raise an exception. If this exception occurs, the Comobj unit is probably missing from the application's uses list (Delphi) or not included in the project source file (C++). You may want to consider removing the safecall calling convention from the routine that gave rise to the exception.
The GeckoBrowser source comes with a unit, BrowserSupports, which looks like a type library import unit, except that it seems to have been manually prepared. It contains an interface which includes the Repaint method which is producing the SafeCall exception.
nsIBaseWindow = interface(nsISupports)
['{046bc8a0-8015-11d3-af70-00a024ffc08c}']
procedure InitWindow(parentNativeWindow: nativeWindow; parentWidget: nsIWidget; x: PRInt32; y: PRInt32; cx: PRInt32; cy: PRInt32); safecall;
procedure Create(); safecall;
procedure Destroy(); safecall;
[...]
procedure Repaint(force: PRBool); safecall;
[...]
end;
Following the suggestion in the quoyed documentation, I changed th "safecall" to StdCall on the Repaint member (but only that member) and, presto!, the exception stopped occurring. If it doesn't reappear in the next couple of days, I'll post that as an answer, unless anyone comes up with a better one.
My project code:
uses
BrowserSupports;
procedure TForm1.FormCreate(Sender: TObject);
begin
Set8087CW($133F);
Application.OnException := HandleException;
end;
procedure TForm1.HandleException(Sender: TObject; E: Exception);
begin
Inc(Errors);
Caption := Format('Errors %d, msg: %s', [Errors, E.Message]);
Screen.Cursor := crDefault;
end;
type
TMyGeckoBrowser = class(TGeckoBrowser);
procedure TForm1.Loaded;
begin
inherited;
GeckoBrowser1.HandleNeeded;
(TMyGeckoBrowser(GeckoBrowser1).WebBrowser as nsIBaseWindow).SetVisibility(True);
end;
procedure TForm1.btnLoadUrlClick(Sender: TObject);
begin
try
GeckoBrowser1.LoadURI(edUrl.Text);
except
end;
end;
Looking at the headers, the prototype for Repaint is effectively as follows:
HRESULT __stdcall Repaint(PRBool force);
and that means that
procedure Repaint(force: PRBool); safecall;
is a reasonable declaration. Remember that safecall performs parameter re-writing to convert COM error codes into exceptions.
This does mean that if the call to Repaint returns a value that indicates failure, then the safecall mechanism will surface that as an exception. If you wish to ignore this particular exception then it is cleaner to do so at source:
try
baseWin.Repaint(True);
except
on EOleException do
; // ignore
end;
If you wish to avoid dealing with exceptions then you could switch to stdcall, but you must remember to undo the parameter re-writing.
function Repaint(force: PRBool): HRESULT; stdcall;
Now you can write it like this:
if Failed(baseWin.Repaint(True)) then
; // handle the error if you really wish to, or just ignore it
Note that Failed is defined in the ActiveX unit.
If you want to troubleshoot the error further then you can look at the error code:
var
hres: HRESULT;
....
hres := baseWin.Repaint(True);
// examine hres
Or if you are going to leave the function as safecall then you can retrieve the error code from the EOleException instance's ErrorCode property.

OnShown event for TForm?

At program start, in the OnActivate event handler, I need to do something which blocks the program for a few seconds. During this time the form's client area is still not completely painted, which looks ugly for the user. (During this blocked time I don't need the program to respond to clicks or other user actions, so there is no need to put the blocking operation into a thread - I just need the form to be completely painted). So I use TForm.Update and Application-ProcessMessages to update the form before the blocking operation which works very well:
procedure TForm1.FormActivate(Sender: TObject);
begin
Form1.Update;
Application.ProcessMessages;
Sleep(7000);
end;
However, I wonder whether there is not another more elegant solution for this problem. This could be for example a OnShown event implemented in a descendant of TForm which will be fired AFTER the form has been completely painted. How could such an event be implemented?
Your real problem is that you are blocking the UI thread. Simply put, you must never do that. Move the long running task onto a different thread and thus allow the UI to remain responsive.
If you are looking for event which is fired when application finishes loading/repainting you should use TApplication.OnIdle event
http://docwiki.embarcadero.com/Libraries/XE3/en/Vcl.Forms.TApplication.OnIdle
This event is fired once application is read to recieve users input. NOTE this event will be fired every time application becomes idle so you need to implement some controll variable which will tel you when OnIdle even was fired for the first time.
But as David already pointed out it is not good to block your UI (main thread). Why? When you block your main thread the application can't normally process its messages. This could lead to OS recognizing your application as being "Hanged". And aou definitly wanna avoid this becouse it could cause the users to go and forcefully kill your application whihc would probably lead to data loss. Also if you ever wanna design your application for any other platforms than Windows your application might fail the certification proces becouse of that.
In the past a simple PostMessage did the trick.
Essentially you fire it during DoShow of the base form:
procedure TBaseForm.DoShow;
begin
inherited;
PostMessage(Handle, APP_AFTERSHOW, 0, 0);
end;
then catch the msg and create an AfterShow event for all forms inherited from this base form.
But that no longer works, well not if you are skinning and have a good number of VCL controls.
My next trick was to spawn a simple thread in DoShow and check for IsWindowVisible(Handle) and IsWindowEnabled(Handle). That really sped things up it cut 250ms from load time since db opening and other stuff was already in the AfterShow event.
Then finally I thought of madHooks, easy enough to hook the API ShowWindow for my application and fire APP_AFTERSHOW from that.
function ShowWindowCB(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall;
begin
Result := ShowWindowNext(hWnd, nCmdShow);
PostMessage(hWnd, APP_AFTERSHOW, 0, 0);
end;
procedure TBaseForm.Loaded;
begin
inherited;
if not Assigned(Application.MainForm) then // Must be Mainform it gets assigned after creation completes
HookAPI(user32, 'ShowWindow', #ShowWindowCB, #ShowWindowNext);
end;
To get the whole thing to completely paint before AfterShow it still needed a ProcessPaintMessages call
procedure TBaseForm.APPAFTERSHOW(var AMessage: TMessage);
begin
ProcessPaintMessages;
AfterShow;
end;
procedure ProcessPaintMessages; // << not tested, pulled out of code
var
msg: TMsg;
begin
while PeekMessage(msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE) do
DispatchMessage(msg);
end;
My final test was to add a Sleep to the AfterShow event and see the Form fully painted with empty db containers since the AfterShow events had not yet completed.
procedure TMainForm.AfterShow;
begin
inherited;
Sleep(8*1000);
......

Why doesn't my form receive WM_DropFiles when files are dropped on it?

I am using Embarcadero RAD Studio XE to develop an application. I am trying catch the file(s) drag and drop to the application with the following code
TMainForm = class(TForm)
public:
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Self.Handle, True);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
DragAcceptFiles(Self.Handle, False);
end;
procedure TMainForm.WMDropFiles(var Msg: TWMDropFiles);
begin
inherited;
showmessage('catch here');
// some code to handle the drop files here
Msg.Result := 0;
end;
This code complied without problem. Also, when I drag and drop files, the cursor show that the status changed to drag and drop but after things dropped, nothing happen (no message shown too). Is that anything wrong with that?
In a plain vanilla application, the code in the question results in WMDropFiles executing when an object is dropped on the form. So, clearly there's something else happening to stop it working. The most obvious potential causes are:
The main form's window handle is re-created after the initial call to DragAcceptFiles.
Your process is running at a higher integrity level than the process that is dropping files on it. For example, are you running your process as administrator. Note that running the Delphi IDE as administrator would lead to your process running as administrator when started from the IDE.
Something else in your process is interfering with drag/drop. Without knowing what's in your app, it's hard to guess what this could be. Start removing portions of your app until there's nothing left but the code in the question.
Option 2 seems quite plausible. To learn more see: Q: Why Doesn’t Drag-and-Drop work when my Application is Running Elevated? – A: Mandatory Integrity Control and UIPI
in TForm.Create use two lines
ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter (WM_COPYGLOBALDATA, MSGFLT_ADD);
that's all

PostMessage in service applications

There is a problem I am unable to solve. I created two service applications in Delphi and tried to post messages within them. Of course, there are no windows in such applications and PostMessage needs a window handle parameter to send a message.
Therefore, I created a window handle using the AllocateHWnd(MyMethod: TWndMethod) function and passed, as the 'MyMethod' parameter, a procedure I want to be called when a message is received. If it was a windowed application, PostMessage() called using the handle returned by the AllocateHWnd method would certainly send a message that would then be received by the 'MyMethod' procedure.
The situation, however, is different in my service applications. I do not understand why, but in one of them posting messages this way works fine, whereas in the second one it does not (the messages are not received at all). Only when the service is being stopped do I notice that two messages are received by 'MyMethod': WM_DESTROY and WM_NCDESTROY. The messages I send using PostMessage are never received by this procedure. On the other hand, the first service always receives all messages I send.
Could you please give me a clue that would help me find the reason of the second service not receiving my messages? I do not know in what way they can differ. I checked the settings of the services and they seem to be identical. Why then one of them works fine and the second one does not (as far as sending messages is concerned)?
Thanks for any advice.
Mariusz.
Without more information it will be difficult to help you debug this, especially why it works in one service but not in the other. However:
Instead of trying to fix the problem in your code you might want to remove the windows altogether, and use PostThreadMessage() instead of PostMessage(). For the posting of messages to work correctly you need a message loop, but not necessarily receiving windows.
Edit: I'm trying to reply to all your answers in one go.
First - if you want to make your life easy you should really check out OmniThreadLibrary by gabr. I don't know whether it does work in a Windows service application, I don't even know whether that has been tried yet. You could ask in the forum. It has however a lot of great features and is worth looking into, if only for the learning effect.
But of course you can also program this for yourself, and you will have to for Delphi versions prior to Delphi 2007. I will simply add some snippets from our internal library, which has evolved over the years and works in several dozen programs. I don't claim it to be bug-free though. You can compare it with your code, and if anything sticks out, feel free to ask and I'll try to clarify.
This is the simplified Execute() method of the worker thread base class:
procedure TCustomTestThread.Execute;
var
Msg: TMsg;
begin
try
while not Terminated do begin
if (integer(GetMessage(Msg, HWND(0), 0, 0)) = -1) or Terminated then
break;
TranslateMessage(Msg);
DispatchMessage(Msg);
if Msg.Message = WM_USER then begin
// handle differently according to wParam and lParam
// ...
end;
end;
except
on E: Exception do begin
...
end;
end;
end;
It is important to not let exceptions get unhandled, so there is a top-level exception handler around everything. What you do with the exception is your choice and depends on the application, but all exceptions have to be caught, otherwise the application will get terminated. In a service your only option is probably to log them.
There is a special method to initiate thread shutdown, because the thread needs to be woken up when it is inside of GetMessage():
procedure TCustomTestThread.Shutdown;
begin
Terminate;
Cancel; // internal method dealing with worker objects used in thread
DoSendMessage(WM_QUIT);
end;
procedure TCustomTestThread.DoSendMessage(AMessage: Cardinal;
AWParam: integer = 0; ALParam: integer = 0);
begin
PostThreadMessage(ThreadID, AMessage, AWParam, ALParam);
end;
Posting WM_QUIT will cause the message loop to exit. There is however the problem that code in descendant classes could rely on Windows messages being properly handled during shutdown of the thread, especially when COM interfaces are used. That's why instead of a simple WaitFor() the following code is used to free all running threads:
procedure TCustomTestController.BeforeDestruction;
var
i: integer;
ThreadHandle: THandle;
WaitRes: LongWord;
Msg: TMsg;
begin
inherited;
for i := Low(fPositionThreads) to High(fPositionThreads) do begin
if fPositionThreads[i] <> nil then try
ThreadHandle := fPositionThreads[i].Handle;
fPositionThreads[i].Shutdown;
while TRUE do begin
WaitRes := MsgWaitForMultipleObjects(1, ThreadHandle, FALSE, 30000,
QS_POSTMESSAGE or QS_SENDMESSAGE);
if WaitRes = WAIT_OBJECT_0 then begin
FreeAndNil(fPositionThreads[i]);
break;
end;
if WaitRes = WAIT_TIMEOUT then
break;
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
except
on E: Exception do
// ...
end;
fPositionThreads[i] := nil;
end;
end;
This is in the overridden BeforeDestruction() method because all threads need to be freed before the destructor of the descendant controller class begins to free any objects the threads might use.
I'd suggest you consider using named pipes for IPC. That is what they are designed to do:
Looking for an alternative to windows messages used in inter-process communication
As Mghie mentioned, you need a message processing loop. That's why PeekMessage returns the messages correctly. It's not that the messages aren't there, it's that you aren't processing them. In a standard application, Delphi creates a TApplication class and calls Application.Run. This IS the message processing loop for a normal app. It basically consists of:
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;
If you want your service application to handle messages, you'll need to perform the same kind of work.
There's an example of using a service and handling PostThreadMessage dispatches here. Keep in mind, as Mick mentioned, you cannot use message handling between applications of differing security contexts (particularly in Vista). You should use named pipes or similar. Microsoft discusses this here.
Edit:
Based on the code snippet that you posted, you may just be fighting a threading issue. AllocHWnd is not thread safe. See here for a really detailed explanation of the issue and a version that works correctly in threads.
Of course, this still leads us back to why you aren't using PostThreadMessage instead. The way your code sample is structured, it would be trivial to make the message handling a function of the thread and then pass it down into the class for disposition.
Thanks for all your answers. I think we can forget about the problem. I created a new service application and performed quick post message tests. The messages were delivered correctly, so I hope I can now state that normally everything works fine and something is wrong only with this one service I described. I know it is stupid, but I will just try to copy one fragment of code after another from the 'bad' service to a new one. Maybe this will help me find the reason of the problem.
I hope I can now consider the message-waiting loop unnecessary as long as everything works fine without it, can't I?
If it comes to the privileges, Microsoft says: "UAC uses WIM to block Windows messages from being sent between processes of different privilege levels.". My Vista's UAC is off and I did not set any privileges for those services I described. Apart from that I do not send messages between different processes. Messages are sent within one process.
To give you the idea of what I am doing, I'll show you a code snippet from a test service application.
uses ...;
type
TMyThread = class;
TMyClass = class
private
FThread: TMyThread;
procedure ReadMessage(var Msg: TMessage);
public
FHandle: HWND;
constructor Create;
destructor Destroy; override;
end;
TMyThread = class(TThread)
private
FMyClass: TMyClass;
protected
procedure Execute; override;
constructor Create(MyClass: TMyClass); reintroduce;
end;
implementation
{ TMyClass }
constructor TMyClass.Create;
begin
inherited Create;
FHandle := AllocateHWnd(ReadMessage);
FThread := TMyThread.Create(Self);
end;
destructor TMyClass.Destroy;
begin
FThread.Terminate;
FThread.WaitFor;
FThread.Free;
DeallocateHWnd(FHandle);
inherited Destroy;
end;
procedure TMyClass.ReadMessage(var Msg: TMessage);
begin
Log.Log('message read: ' + IntToStr(Msg.Msg));
end;
{ TMyThread }
constructor TMyThread.Create(MyClass: TMyClass);
begin
inherited Create(True);
FMyClass := MyClass;
Resume;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
//do some work and
//send a message when finished
if PostMessage(FMyClass.FHandle, WM_USER, 0, 0) then
Log.Log('message sent')
else
Log.Log('message not sent: ' + SysErrorMessage(GetLastError));
//do something else...
Sleep(1000);
end;
end;
This is only an example, but functioning of my real code bases on the same idea. When you create an object of this class, it will create a thread that will start sending messages to that class. Log.Log() saves data into a text file. When I use this code in a new service application, everything works fine. When i put it into the 'broken' service, it does not. Please note that I do not use any message-waiting loop to receive messages. I created a new service and just put the code above into it, then created an object of the class. That's all.
If I get to know why this does not work in the 'broken' service, I'll write about it.
Thanks for the time you devoted me.
Mariusz.
Here's what I would try:
Check the return value and GetLastError of PostMessage
Is this a Vista/2008 machine? If yes, check if the sending application have sufficient priviliges to do send the message.
I have to have more information to help you further.
I spent long hours trying to find the reason of the messages not being received. As I showed in my code snippet, the constructor of the class creates a window handle which I used to send messages to. As long as the class was constructed by the main thread, everything worked fine for the window handle (if I understand it correctly) existed in the context of the main thread which, by default, awaits messages. In the 'broken' service, as I called it by mistake, my class was created by another thread, so the handle must have existed in the context of that thread. Therefore, when I sent messages using this window handle, they were received by that thread, not by the main one. Because of the fact that this thread did not have any message-waiting loop, my messages were not received at all.
I just did not know it worked this way. To solve the problem in an easy way, I create and destroy the class in the main thread even though I use it in the second one.
Thanks for your time and all the information you gave me.
Mghie, I think you are absolutely right. I implemented a message waiting loop this way:
procedure TAsyncSerialPort.Execute;
var
Msg: tagMSG;
begin
while GetMessage(Msg, 0, 0, 0) do
begin
{thread message}
if Msg.hwnd = 0 then
begin
case Msg.message of
WM_DATA_READ: Log.Log('data read');
WM_READ_TIMEOUT: Log.Log('read timeout');
WM_DATA_WRITTEN: Log.Log('data written');
WM_COMM_ERROR: Log.Log('comm error');
else
DispatchMessage(Msg);
end;
end
else
DispatchMessage(Msg);
end;
end;
I'm doing it for the first time, so please, could you check the code whether it is correct? In fact, this is my real class code snippet (the logs will be substituted with a real code). It handles overlapped comm port. There are two threads that send thread messages to the thread above, informing it that they wrote or received some data from comm port, etc. When the thread gets such a message, it takes an action - it gets the received data from a queue, where the threads first put it and then calls an external method that, lets say, analyses the received data. I don't want to go into details for it is unimportant :). I send thread messages like this: PostThreadMessage(MyThreadId, WM_DATA_READ, 0, 0).
This code works properly as I checked, but I would like to be sure everything is correct, so I'm asking you about that. I would be grateful if you answered.
To free the thread I do the following:
destructor TAsyncSerialPort.Destroy;
begin
{send a quit message to the thread so that GetMessage returns false and the loop ends}
PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
{terminate the thread but wait until it finishes before the following objects
(critical sections) are destroyed for the thread might use them before it quits}
Terminate;
if Suspended then
Resume;
WaitFor;
FreeAndNil(FLock);
FreeAndNil(FCallMethodsLock);
inherited Destroy;
end;
I hope this is the proper way to end the message loop.
Thank you very much for your help.
BTW, I hope my English language is understandable, isn't it? :) Sorry if you have difficulties understanding me.
There's one trick in message loops in threads. Windows won't create a message queue for a thread immediately so there will be some time when posting messages to a thread will fail. Details are here. In my msg loop thread I use the technique MS proposes:
constructor TMsgLoopThread.Create;
begin
inherited Create(True);
FEvMsgQueueReady := CreateEvent(nil, True, False, nil);
if FEvMsgQueueReady = 0 then
Error('CreateEvent: '+LastErrMsg);
end;
procedure TMsgLoopThread.Execute;
var
MsgRec: TMsg;
begin
// Call fake PeekMessage for OS to create message queue for the thread.
// When it finishes, signal the event. In the main app execution will wait
// for this event.
PeekMessage(MsgRec, 0, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(FEvMsgQueueReady);
...
end;
// Start the thread with waitinig for it to get ready
function TMsgLoopThread.Start(WaitInterval: DWORD): DWORD;
begin
inherited Start;
Result := WaitForSingleObject(FEvMsgQueueReady, WaitInterval);
end;
But in your case I'd strongly recommend using other means of IPC.

Resources