Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
|
View to a kill
Lets play Bond.
James Bond.
You know, double-oh-seven, licenced to kill.
But in this case, the victim has to cooperate.
Problem
I was preparing my speech for C++World 1999 in Miami happily creating nice CORBA servers that also used the VCL, just to make server executables that also actually show something.
One of the topics is to create a server that can be killed remotely.
I could shut down the CORBA BOA and ORB with CORBA::ORB::shutdown() allright.
Using VisiBrokers osfind command I could see that the object implementations were indeed gone.
However, I had difficulties shutting the VCL application remotely.
My first try was to just call Close() on the applications mainform.
Didn't work.
Tried PostQuitMessage(0).
Nope.
Tried Application->Terminate().
Nope again.
Tried creating an Idle handler that called Close.
Almost.
Solution
In the end, the solution was to use an idle handler that checks for a boolean value that acts as a semaphore.
If the semaphore is true, then the form that contains the handler must be closed.
We close it by calling Close, and subsequently invoke Application->BringToFront().
The semaphore acts as the communication medium between a CORBA thread and the application thread.
Explanation
CORBA servers are multithreaded applications.
When you create the services, they are provided to the clients that wish to use them in a separate thread.
If there is a VCL part, then there is an extra trhead that handles the user interface.
If you start such an executable and examine the number of threads, that number is indeed larger than one.
If you call CORBA::BOA::shutdown(), the number of threads will decrease to one.
If an application is sent to the background, it does not receive events.
Not many in any event.
When the application goes to idle, it receieves a single idle messages from the operating system.
If the application signals that it is not done idling, it receives another idle message.
However, your application will then consume all available CPU.
After that, the application thread (the one handling the interface in our case) will not receive any events from the operating system any more and will not be running anymore.
That is because when the application looks in its message queue, it will find nothing and the operating system will then put the main thread to sleep untill a message is put into the queue.
Implementation
Here's the code in the CORBA server that signals the main form tp close.
The ShutdownRequested property is a boolean that is set to true if the server has been shut down:
//------------------------------------------------------- // Implementation of server shutdown void ServerImpl::ShutDown() { // This will take care of the server objects CORBA::ORB::shutdown(); // Now try and close the form MainForm->ShutdownRequested = true; }And here is the code in the main form dealing with the ShutdownRequested property. Notice the Application->BringToFront() call in the Set routine:
//------------------------------------------------------- // Main form class class TMainForm : public TForm { __published: void __fastcall FormShow(TObject *Sender); private: bool FShutdownRequested; void __fastcall OnIdle(TObject* Sender, bool& Done); void __fastcall SetShutdownRequested(bool value); bool __fastcall GetShutdownRequested(); public: __fastcall TMainForm(TComponent* Owner); void __fastcall Log(const AnsiString& message); __property bool ShutdownRequested = { read = GetShutdownRequested, write = SetShutdownRequested, default = false}; }; //------------------------------------------------------- void __fastcall TMainForm::OnIdle(TObject* Sender, bool& Done) { if (ShutdownRequested == true) { Close(); } // Set Done to true otherwise, your process will // spin its wheels consuming all processing power // of the system. Done = true; } //------------------------------------------------------- void __fastcall TMainForm::FormShow(TObject *Sender) { Application->OnIdle = OnIdle; } //------------------------------------------------------- void __fastcall TMainForm::SetShutdownRequested(bool value) { FShutdownRequested = value; // If set to true, we might be in idle state hidden somewhere // in the background not doing any message handling. if (FShutdownRequested == true) { Application->BringToFront(); } } bool __fastcall TMainForm::GetShutdownRequested() { return FShutdownRequested; }