My server must print some reports about its work. How can I use visual objects such as labels, edit boxes in the OneEecute event?
The same rule, for not modifying VCL objects in any thread except main thread, is valid here too. You should not change any of VCL controls in OnExecute event-handler, because that code will be run in the context of a worker thread spawn by Indy for every connection.
If you need to change graphical user interface, do it using Synchronize or Queue methods, or use a custom notification mechanism for notifying the main thread to do the GUI modification for you.
If you want to call Synchronize or Queue methods, you have to type-cast TIdYarn to TIdYarnOfThread which derives from TIdYarn and implements it with threads:
// Calling MyMethod using Synchornize inside TIdTcpServer.OnExecute event-handler
TIdYarnOfThread(AContext.Yarn).Thread.Synchronize(MyMethod);
// Calling MyMethod using Queue inside TIdTcpServer.OnExecute event-handler
TIdYarnOfThread(AContext.Yarn).Thread.Queue(MyMethod);
Related
I have a main form where I set a handler for the Application.OnMessage event. (Code of this handler is placed in the main form). Then, while running the program, there can be calls to SysUtils.LoadPackage that loads some bpl-package. And after that is loaded, the handler of Application.OnMessage is changed.
I couldn't find what doing this. At least there is not right such code that goes Application.OnMessage := in the package.
One more thing: in the debugger, before LoadPackage, I see OnMessage handler described as Main.TMainForm.AppMessage. All other handlers (such as OnMinimize, OnModalBegin e.t.c.) are nil. And after LoadPackage all events have handlers, described as Vcl.AppEvnts.TMultiCaster.DoMessage.
The package in question uses an internal instance of TApplicationEvents, which is a multicaster that intercepts TApplication events and delegates them to every TApplicationEvents instance in the application, allowing multiple Forms, components, etc to receive the same app events without stepping on each other's toes trying to assign handlers to TApplication directly.
So, to coexist with the package, the solution is to add a TApplicationEvents to your MainForm and assign a handler to its OnMessage event, instead of assigning a handler to the TApplication.OnMessage event directly.
I'm using IOmniParallelJoin to compute the several tasks in parallel with NoWait function because I want the GUI to stay responsive. But I also need to know when the computation is finished. Is there any event which is triggered in such case?
You can either use the OnStop function to inject some code or use a Task Configuration via TaskConfig and assign the code via OnTerminated. The difference is that OnStop is called inside one of the worker threads while OnTerminated is called inside the main thread.
I have assigned an event handler to Application.OnHelp, but it is not getting called. I have read some other threads about similar problems, but many of them are for Delphi 7 or Delphi 2010.
I have tried using D6OnHelpFix, but that simply changes the problems, rather than resolving them.
At the end of TApplication.DoOnHelp (in Forms) the invocation of FOnHelp always fails inside TMultiCaster.DoHelp in AppEvnts. That is, the DoHelp routine loops but fails to actually call my event handler.
It sounds like you have used TApplicationEvents objects in your code. In which case you need to attach your event handler to the OnHelp event of a TApplicationEvents object rather then the OnHelp event of TApplication.
The reason for this is that TApplicationEvents replaces the events attached to TApplication with its own handlers to perform the multi-cast dispatch.
Do I need to call Coinitialize in the main/VCL thread in Delphi
before using ShellExecuteEx?
For a thread, yes but for the VCL thread ?
No need to call CoInitialize for Windows Forms Applications.
This is done for you in the main thread.
More specific TApplication.Create in Forms.Pas:
...
if not IsLibrary then
FNeedToUninitialize := Succeeded(OleInitialize(nil));
...
If in doubt, do it. In either case, CoInitialize() will return a hr : HRESULT which you should check, because you need to CoUninitialize() on SUCCEEDED(hr), but not when FAILED(hr). A failed result usually indicates that it already has been called.
Cited from your MSDN ref:
Nonetheless, it is good practice to always initalize COM before using
this function.
In the RTL/VCL source, COM is initialized in the following ways:
By a call to OleInitialize made from Forms.TApplication.Create. So this call will be made for all VCL forms applications, but not, for example, for service applications.
By a call to CoInitialize or CoInitializeEx in ComObj.InitComObj. This is registered as an InitProc in the initialization section of the ComObj unit. In turn, the call to Application.Initialize in your project .dpr file's code will invoke ComObj.InitComObj.
In many and various other locations around the RTL/VCL. Including, but not limited to, Datasnap, ComServ, Soap, System.Win.Sensors, Winapi.DirectShow9. Some of these areas of code are more recent than Delphi 7.
Now, of these various COM initializations, the ones that count are 1 and 2. In any standard VCL forms application, both of these will run at startup in the main thread. Item 1 runs first and so gets to initialize COM first. That's the initialization that counts. Item 2 runs after and returns S_FALSE meaning that COM was already initialized.
So, to your question:
Do I need to call Coinitialize in the main/VCL thread?
No you do not. You can be sure that COM has already been initialized in a VCL application's main thread.
The documentation of delphi says that the WaitFor function for TMutex and others sychronization objects wait until a handle of object is signaled.But this function also guarantee the ownership of the object for the caller?
Yes, the calling thread of a TMutex owns the mutex; the class is just a wrapper for the OS mutex object. See for yourself by inspecting SyncObjs.pas.
The same is not true for other synchronization objects, such as TCriticalSection. Any thread my call the Release method on such an object, not just the thread that called Acquire.
TMutex.Acquire is a wrapper around THandleObjects.WaitFor, which will call WaitForSingleObject OR CoWaitForMultipleHandles depending on the UseCOMWait contructor argument.
This may be very important, if you use STA COM objects in your application (you may do so without knowing, dbGO/ADO is COM, for instance) and you don't want to deadlock.
It's still a dangerous idea to enter a long/infinite wait in the main thread, 'cause the only method which correctly handles calls made via TThread.Synchronize is TThread.WaitFor and you may stall (or deadlock) your worker threads if you use the SyncObjs objects or WinAPI wait functions.
In commercial projects, I use a custom wait method, built upon the ideas from both THandleObjects.WaitFor AND TThread.WaitFor with optional alertable waiting (good for asynchronous IO but irreplaceable for the possibility to abort long waits).
Edit: further clarification regarding COM/OLE:
COM/OLE model (e.g. ADO) can use different threading models: STA (single-threaded) and MTA (multi or free-threaded).
By definition, the main GUI thread is initialized as STA, which means, the COM objects can use window messages for their asynchronous messaging (particulary when invoked from other threads, to safely synchronize). AFAIK, they may also use APC procedures.
There is a good reason for the CoWaitForMultipleHandles function to exist - see its' use in SyncObjs.pas THandleObject.WaitFor - depending on the threading model, it can process internal COM messages, while blocking on the wait handle.