Symbian
Symbian OS Library

FAQ-1241 How do CEikDialog-based dialogs 'block' when using the EEikDialogFlagWait flag?

[Index][spacer] [Previous] [Next]



 

Classification: C++ Category: Applications
Created: 03/18/2005 Modified: 03/24/2005
Number: FAQ-1241
Platform: Not Applicable

Question:
How do CEikDialog-based dialogs 'block' when using the EEikDialogFlagWait flag?
Can an application receive system events (commands, callbacks etc) when running a 'waiting' dialog?
How do I 'unblock' a waiting dialog if a certain condition has been satisfied and recommence execution?


Answer:
How do CEikDialog based dialogs 'block' when using the EEikDialogFlagWait flag?

The CEikDialog exploits the 'nested loop' feature of the CActiveScheduler class (see CActiveScheduler). The scheduler provides a means of nesting 'wait' loops to introduce some form of synchronicity (modal processing) along an execution path.

When the flag 'EEikDialogFlagWait' is set, a dialog executes a new active scheduler loop that effectively blocks the current execution path at the point of calling either RunLD() or ExecuteLD(). This means that any code after these methods is not run until after the dialog is dismissed; which stops the current scheduler loop. In contrast, RNotifier-based dialogs, such as CAknGlobalProgressDialog, pass callback control to the initiator via the TRequestStatus object. The user then has a choice to either block or handle the response via an active object.

Can an application receive system events (commands, callbacks etc) when running a 'waiting' dialog?

Yes. When a dialog is run, a new active scheduler loop is started and all active objects in the active scheduler can receive events as per normal; albeit at a different 'anchorage' place in the execution path. The application can handle all asynchronous events, including system events such as an exit command 'EEikCmdExit' and kernel events such as timers, service providers callback, etc.

As using nested scheduler loops 'anchors' application code at a different execution point, caution should be observed when using nested loops - no assumptions should be made about the state of the installed active objects and/or the anchorage point. See remarks below.

How do I 'unblock' a waiting dialog if a certain condition has been satisfied and recommence execution?

There are a few use cases that may necessitate the use of nested loops; especially when retrofitting code. One such case is when a dialog is launched to notify the user that a particular command cannot be executed until a certain process has terminated. Though a FSM (see remarks below) approach would work, it might complicate code for an otherwise simple task. To automate the execution of the command when the process terminates involves watching the application in question, and continuing execution when either the dialog is dismissed or the application terminates. The code snippet below facilitates such a mode of operation.

Make sure that the dialog is designated to wait:

RESOURCE DIALOG r_terminate_process_dialog
{
buttons = r_ok_button;
flags = EEikDialogFlagWait;
...
..
}

Implement a CActive watcher object that handles 'logon' to a given application before launching the dailog.

_LIT(KApplicationToWatch, "Running Application");
TApaTask task(iEikonEnv->WsSession())

while (ApplicationRunning(KApplicationToWatch, task))
{
CNotifyDialog* dialog = newCNotifyDialog();
dialog->PrepareLC(R_TERMINATE_PROCESS_DIALOG);
dialog->SetHeaderTextL(_L("Attention!"));
dialog->SetMessageTextL(_L("Waiting for application to terminate!"));

// Gets the application thread from the task. Keeps a handle to the dialog so
// can delete it to unblock.
//
CApplicationWatcher* watcher = CApplicationWatcher::NewLC(task, dialog);

// Does a RThread::Logon(...) on the application, installs itself with the active scheduler
// and sets itself active.
//
watcher->WatchL();

// Pops the dialog to match PrepareLC before running. This starts a new active scheduler loop which
// blocks execution.
TInt result = dialog->RunLD();

// Do something here. Either the dialog has been dismissed so the user wants to close the app
// by sending a system command or the watcher has completed because the app has terminated
//
if (watcher->IsApplicationRunning())
{
watcher->CloseApplicationL();
}
...
...

// Note the dialog deletes itself.
CleanupStack::PopAndDestroy(watcher); // watcher
}

The RunL() of the watcher is implemented as below. If the application terminates (the user closes it, for example) the RunL() of the watcher is executed, which action deletes the dialog.

    void CApplicationWatcher::RunL()
    {delete iDialog;}
    The dialog stops the current scheduler loop before it is deleted. Note that this does not interfere with the delete done by RunLD() when the dialog is acknowledged. In the event when the dialog is dismissed, the application is closed via the watcher (in this case) before the watcher object is destroyed. By using the above example, one could equally wait on multiple events when the dialog is displayed.
      Remarks
        1. The use of the nested loop feature of the active scheduler is discouraged (see CActiveSchedulerWait) and other approaches should be sought; for example handling all events via a Finite State machine (FSM). You are encouraged to avoid employing nested loops from the outset when designing your application. The use of nested CActiveScheduler loop should be restricted only to when retrofitting/fixing code. Note that the use of nested loops is not confined to dialogs; it can be used (if necessary) within any process that has an active scheduler installed.
        2. It should be borne in mind that using active scheduler nested loops increases stack usage. Though rare, stack overflow is a likely possibility; caused by an otherwise well behaving code.
        3. It has been observed that if a new scheduler loop is started (or a dialog is launched) from within the RunL()of an active object (for example a timer object), the active object's RunL()method is re-run immediately when the dialog is executed. This happens because the active object is still marked as active and the status is other then KRequestPending. When the new loop is started the scheduler checks if anything needs to run; hence, the RunL() is re-executed. The work-around is to call Cancel()on the active object in question.