filesystemuis/memscaneng/serversrc/memscanserv.cpp
branchRCL_3
changeset 39 65326cf895ed
parent 0 6a9f87576119
equal deleted inserted replaced
38:491b3ed49290 39:65326cf895ed
       
     1 /*
       
     2 * Copyright (c) 2006-2006 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0""
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Memory Scan Server
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 // SYSTEM INCLUDES
       
    21 #include <e32svr.h>
       
    22 #include <s32mem.h> // RBufWriteStream
       
    23 
       
    24 // USER INCLUDES
       
    25 #include "memscanserv.h"
       
    26 #include "memscanutils.h" // traces
       
    27 
       
    28     
       
    29 // ---------------------------------------------------------------------------
       
    30 // Server startup code
       
    31 // ---------------------------------------------------------------------------
       
    32 
       
    33 // Perform all server initialisation, in particular creation of the
       
    34 // scheduler and server and then run the scheduler
       
    35 // 
       
    36 static void RunServerL()
       
    37     {
       
    38     // naming the server thread after the server helps to debug panics
       
    39     User::LeaveIfError(User::RenameThread(KMemScanServName));
       
    40 
       
    41     // create and install the active scheduler we need
       
    42     CActiveScheduler* scheduler=new(ELeave) CActiveScheduler;
       
    43     CleanupStack::PushL(scheduler);
       
    44     CActiveScheduler::Install(scheduler);
       
    45     // create the server (leave it on the cleanup stack)
       
    46     CMemScanServ::NewLC();
       
    47     // Initialisation complete, now signal the client
       
    48 
       
    49     RProcess::Rendezvous(KErrNone);
       
    50 
       
    51     // Ready to run
       
    52     TRACES( RDebug::Print(_L("MemScanServ: server fully running")) );
       
    53     CActiveScheduler::Start();
       
    54     // Cleanup the server and scheduler
       
    55     CleanupStack::PopAndDestroy(2, scheduler);
       
    56     }
       
    57 
       
    58 // Server process entry-point
       
    59 TInt E32Main()
       
    60     {
       
    61     __UHEAP_MARK;
       
    62     TRACES( RDebug::Print(_L("MemScanServ: E32Main")) ); 
       
    63     CTrapCleanup* cleanup=CTrapCleanup::New();
       
    64     TInt r=KErrNoMemory;
       
    65     if (cleanup)
       
    66         {
       
    67         TRAP(r,RunServerL());
       
    68         delete cleanup;
       
    69         }
       
    70     __UHEAP_MARKEND;
       
    71     return r;
       
    72     }
       
    73     
       
    74 // RMessagePtr2::Panic() also completes the message. This is:
       
    75 // (a) important for efficient cleanup within the kernel
       
    76 // (b) a problem if the message is completed a second time
       
    77 void PanicClient(const RMessagePtr2& aMessage,TMemScanServPanic aPanic)
       
    78     {
       
    79     _LIT(KPanic,"MemScanServ");
       
    80     aMessage.Panic(KPanic,aPanic);
       
    81     }
       
    82 
       
    83     
       
    84 // ---------------------------------------------------------------------------
       
    85 // CShutDown
       
    86 // ---------------------------------------------------------------------------
       
    87 inline CShutdown::CShutdown()
       
    88     :CTimer(-1)
       
    89     {
       
    90     CActiveScheduler::Add(this);
       
    91     }
       
    92     
       
    93 inline void CShutdown::ConstructL()
       
    94     {
       
    95     CTimer::ConstructL();
       
    96     }
       
    97 
       
    98 inline void CShutdown::Start()
       
    99     {
       
   100     TRACES( RDebug::Print(_L("MemScanServ: starting shutdown timeout")) );
       
   101     After(EMemScanServShutdownDelay);
       
   102     }
       
   103 
       
   104 void CShutdown::RunL()
       
   105     {
       
   106     TRACES( RDebug::Print(_L("MemScanServ: server timeout ... closing")) );
       
   107     CActiveScheduler::Stop();
       
   108     }
       
   109 
       
   110 // ---------------------------------------------------------------------------
       
   111 // CMemScanServ
       
   112 // ---------------------------------------------------------------------------
       
   113 inline CMemScanServ::CMemScanServ()
       
   114     :CPolicyServer(0, KMemScanServPolicy, ESharableSessions)
       
   115     {
       
   116     }
       
   117     
       
   118 CServer2* CMemScanServ::NewLC()
       
   119     {
       
   120     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServ::NewLC")) );
       
   121     CMemScanServ* self=new(ELeave) CMemScanServ;
       
   122     CleanupStack::PushL(self);
       
   123     self->ConstructL();
       
   124     return self;
       
   125     }
       
   126 
       
   127 // 2nd phase construction - ensure the timer and server objects are running
       
   128 void CMemScanServ::ConstructL()
       
   129     {
       
   130     StartL(KMemScanServName);
       
   131     iShutdown.ConstructL();
       
   132     // ensure the server still exits even if the 1st client fails to connect
       
   133     if( !iShutdown.IsActive() )
       
   134         {
       
   135         iShutdown.Start();
       
   136         }
       
   137     }
       
   138 
       
   139 
       
   140 // Create a new client session.
       
   141 CSession2* CMemScanServ::NewSessionL(const TVersion& aVersion, const RMessage2&) const
       
   142     {
       
   143     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServ::NewSessionL")) );
       
   144 
       
   145     // Client-Server version check
       
   146 	TVersion version(KMemScanServMajor, KMemScanServMinor, KMemScanServBuild);
       
   147 	if( !User::QueryVersionSupported( version, aVersion ) )
       
   148 	    {
       
   149 	    User::Leave( KErrNotSupported );
       
   150 	    }
       
   151 
       
   152     return new (ELeave) CMemScanServSession();
       
   153     }
       
   154 
       
   155 // A new session is being created
       
   156 // Cancel the shutdown timer if it was running
       
   157 void CMemScanServ::AddSession()
       
   158     {
       
   159     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServ::AddSession")) );
       
   160     ++iSessionCount;
       
   161     iShutdown.Cancel();
       
   162     }
       
   163 
       
   164 // A session is being destroyed
       
   165 // Start the shutdown timer if it is the last session.
       
   166 void CMemScanServ::DropSession()
       
   167     {
       
   168     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServ::DropSession")) );
       
   169     if (--iSessionCount==0)
       
   170         {
       
   171         if( !iShutdown.IsActive() )
       
   172             {
       
   173             iShutdown.Start();
       
   174             }
       
   175         }
       
   176     }
       
   177 
       
   178 
       
   179 // ---------------------------------------------------------------------------
       
   180 // CMemScanServSession
       
   181 // ---------------------------------------------------------------------------
       
   182 inline CMemScanServSession::CMemScanServSession()
       
   183     {
       
   184     TRACES( RDebug::Print(_L("MemScanServer: CMemScanServSession::CMemScanServSession")); )
       
   185     }
       
   186     
       
   187 inline CMemScanServ& CMemScanServSession::Server()
       
   188     {
       
   189     return *static_cast<CMemScanServ*>(const_cast<CServer2*>(CSession2::Server()));
       
   190     }
       
   191 
       
   192 // 2nd phase construct for sessions - called by the CServer framework
       
   193 void CMemScanServSession::CreateL()
       
   194     {
       
   195     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::CreateL")); )
       
   196     Server().AddSession();
       
   197         
       
   198     // Create a transfer buffer
       
   199     iTransferBuffer = CBufFlat::NewL(KMemScanServTransferBufferExpandSize);
       
   200     }
       
   201 
       
   202 CMemScanServSession::~CMemScanServSession()
       
   203     {
       
   204     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::~CMemScanServSession")); )
       
   205     
       
   206     
       
   207     delete iTransferBuffer;
       
   208     delete iMseng;
       
   209     
       
   210     
       
   211     iEventBuffer.Close();
       
   212     Server().DropSession();
       
   213     }
       
   214 
       
   215 
       
   216 // Handle a client request.
       
   217 // Leaving is handled by CMemScanServSession::ServiceError() which reports
       
   218 // the error code to the client
       
   219 void CMemScanServSession::ServiceL(const RMessage2& aMessage)
       
   220     {
       
   221     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::ServiceL; %d"),aMessage.Function()); )
       
   222     switch (aMessage.Function())
       
   223         {
       
   224         case EMemScanPrepareDataGroups:
       
   225             {
       
   226             PrepareDataGroupsL( aMessage );
       
   227             break;
       
   228             }
       
   229         case EMemScanGetDataGroups:
       
   230             {
       
   231             GetDataGroupsL( aMessage );
       
   232             break;
       
   233             }            
       
   234         case EMemScanStartScan:
       
   235             {
       
   236             MemScanL( aMessage );
       
   237             break;
       
   238             }
       
   239         case EMemScanPrepareScanResults:
       
   240             {
       
   241             PrepareScanResultsL( aMessage );
       
   242             break;
       
   243             }
       
   244         case EMemScanGetScanResults:
       
   245             {
       
   246             GetScanResultsL( aMessage );
       
   247             break;
       
   248             }                       
       
   249         case EMemScanRequestScanEvents:
       
   250             {
       
   251             RequestScanEventsL( aMessage );
       
   252             break;
       
   253             }
       
   254         case EMemScanRequestScanEventsCancel:
       
   255             {
       
   256             RequestScanEventsCancel( aMessage );
       
   257             break;
       
   258             }
       
   259         case EMemScanInProgress:
       
   260             {
       
   261             ScanInProgress( aMessage );
       
   262             break;
       
   263             }
       
   264 
       
   265         default:
       
   266             {
       
   267             TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::ServiceL; %d"),aMessage.Function()); )
       
   268             PanicClient(aMessage,EPanicIllegalFunction);
       
   269             break;
       
   270             }
       
   271             
       
   272         }
       
   273     }
       
   274 
       
   275 // Handle an error from CMemScanServSession::ServiceL()
       
   276 void CMemScanServSession::ServiceError(const RMessage2& aMessage,TInt aError)
       
   277     {
       
   278     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::ServiceError %d"),aError); )
       
   279     CSession2::ServiceError(aMessage,aError);
       
   280     }
       
   281 
       
   282 
       
   283 // ***************************************************************************
       
   284 // Internal utility functions
       
   285 // ***************************************************************************
       
   286 
       
   287 
       
   288 // ---------------------------------------------------------------------------
       
   289 // CMemScanServSession::PrepareDataGroupsL()
       
   290 //
       
   291 // ---------------------------------------------------------------------------
       
   292 void CMemScanServSession::PrepareDataGroupsL(const RMessage2& aMessage)
       
   293     {
       
   294     // Create scan engine if it does not exist
       
   295     if(!iMseng)
       
   296         {
       
   297         iMseng = CMseng::NewL(*this);
       
   298         }
       
   299     
       
   300     // Get data group name array
       
   301     CDesCArray* dataGroupArray = iMseng->DataGroupsL();
       
   302     CleanupStack::PushL(dataGroupArray);
       
   303    
       
   304    
       
   305     // *** Start externalizing the data group array to transfer buffer
       
   306 
       
   307     // Clear the buffer
       
   308     iTransferBuffer->Reset();
       
   309 
       
   310     // Set buffer for the stream
       
   311     RBufWriteStream stream(*iTransferBuffer);
       
   312     CleanupClosePushL(stream);
       
   313     
       
   314     // Write number of fields in array to stream 
       
   315     TInt count = dataGroupArray->MdcaCount();
       
   316     stream.WriteInt32L(count);
       
   317     
       
   318     // Write each field in array to stream
       
   319     for(TInt i=0; i<count; i++)
       
   320         {
       
   321         TInt length = dataGroupArray->MdcaPoint(i).Length();
       
   322         stream.WriteInt32L(length); // writes datagroup name length to stream
       
   323         const TPtrC group = dataGroupArray->MdcaPoint(i);
       
   324         stream << group; // writes one datagroup to stream
       
   325         }
       
   326     
       
   327     stream.CommitL();
       
   328     CleanupStack::PopAndDestroy(&stream);
       
   329     CleanupStack::PopAndDestroy(dataGroupArray);
       
   330     
       
   331     // *** externalizing done
       
   332     
       
   333     
       
   334     // Write the size of transfer buffer back to client
       
   335     TPckgBuf<TInt> size(iTransferBuffer->Size());
       
   336     aMessage.WriteL(0, size);
       
   337     
       
   338     // complete the message
       
   339     aMessage.Complete( KErrNone );
       
   340     }
       
   341 
       
   342 
       
   343 // ---------------------------------------------------------------------------
       
   344 // CMemScanServSession::GetDataGroupsL()
       
   345 //
       
   346 // ---------------------------------------------------------------------------
       
   347 void CMemScanServSession::GetDataGroupsL(const RMessage2& aMessage)
       
   348     {
       
   349     // Get the prepared data groups
       
   350     aMessage.WriteL( KMesArg0, iTransferBuffer->Ptr(0)); 
       
   351         
       
   352     aMessage.Complete( KErrNone );
       
   353     }
       
   354     
       
   355 // ---------------------------------------------------------------------------
       
   356 // CMemScanServSession::PrepareScanResultsL()
       
   357 //
       
   358 // ---------------------------------------------------------------------------
       
   359 void CMemScanServSession::PrepareScanResultsL(const RMessage2& aMessage)
       
   360     {
       
   361     // Get scan results from server
       
   362     CArrayFix<TInt64>* resultArray = iMseng->ScanResultL();
       
   363     CleanupStack::PushL(resultArray);
       
   364 
       
   365     // *** Start externalizing the result array to transfer buffer
       
   366     
       
   367     // Clear the buffer
       
   368     iTransferBuffer->Reset();
       
   369         
       
   370     // Set buffer for the stream
       
   371     RBufWriteStream stream(*iTransferBuffer);
       
   372     CleanupClosePushL(stream);
       
   373     
       
   374     // Write number of fields in array to stream 
       
   375     TInt count = resultArray->Count();
       
   376     stream.WriteInt32L(count);    
       
   377     
       
   378     // Write each field in array to stream
       
   379     for(TInt i=0; i<count; i++)
       
   380         {
       
   381         const TInt64 result = resultArray->At(i);
       
   382         stream << result; // writes one data result to stream
       
   383         }
       
   384     
       
   385     stream.CommitL();
       
   386     CleanupStack::PopAndDestroy(&stream);
       
   387     CleanupStack::PopAndDestroy(resultArray);
       
   388     
       
   389     // *** externalizing done
       
   390     
       
   391     
       
   392     // Write the size of transfer buffer back to client
       
   393     TPckgBuf<TInt> size(iTransferBuffer->Size());
       
   394     aMessage.WriteL(0, size);    
       
   395     
       
   396     
       
   397     aMessage.Complete( KErrNone );    
       
   398     }
       
   399 
       
   400 
       
   401 // ---------------------------------------------------------------------------
       
   402 // CMemScanServSession::GetScanResultsL()
       
   403 //
       
   404 // ---------------------------------------------------------------------------
       
   405 void CMemScanServSession::GetScanResultsL(const RMessage2& aMessage)
       
   406     {
       
   407     // Get the prepared scan results
       
   408     aMessage.WriteL( KMesArg0, iTransferBuffer->Ptr(0));    
       
   409     
       
   410     aMessage.Complete( KErrNone ); 
       
   411     }
       
   412 
       
   413 
       
   414 // ---------------------------------------------------------------------------
       
   415 // CMemScanServSession::MemScanL()
       
   416 //
       
   417 // ---------------------------------------------------------------------------
       
   418 void CMemScanServSession::MemScanL(const RMessage2& aMessage)
       
   419     {
       
   420     TRACES( RDebug::Print(_L("MemScanServ: CMemScanServSession::ScanL")); )
       
   421     
       
   422      // Get the first integer parameter of message     
       
   423     TDriveNumber drive = TDriveNumber(aMessage.Int0()); 
       
   424         
       
   425     iMseng->ScanL( drive );
       
   426     aMessage.Complete( KErrNone );
       
   427     }
       
   428 
       
   429 
       
   430 // ---------------------------------------------------------------------------
       
   431 // CMemScanServSession::RequestScanEventsL()
       
   432 //
       
   433 // ---------------------------------------------------------------------------
       
   434 void CMemScanServSession::RequestScanEventsL(const RMessage2& aMessage)
       
   435     {
       
   436     if  ( iScanEventMessage.IsNull() ) 
       
   437         {
       
   438         // We want check that the client hasn't requested scan events
       
   439         // twice in a row. The client is only allowed to have one 
       
   440         // scan event request outstanding at any given time.
       
   441         //
       
   442         // Since the iScanEventMessage was null (i.e. its not been
       
   443         // initialised) then its safe to store the client's message
       
   444         // for completion later on when the scan engine has a real event.
       
   445         
       
   446         // Save the clients message for later until we receive an
       
   447         // event callback from the scan engine.
       
   448         iScanEventMessage = aMessage;
       
   449 
       
   450         // If we have at least one event ready to send to the client, then
       
   451         // we deliver it to the client immediately. This could be possible
       
   452         // if the client is slow to process an earlier event.
       
   453         const TBool haveAtLeastOneEventPending = IsEventReady();
       
   454         if  ( haveAtLeastOneEventPending )
       
   455             {
       
   456             // We must deliver the oldest event to the client.
       
   457             DeliverOldestEventToClientL(); // this will complete aMessage immediately.
       
   458             }
       
   459         }
       
   460     else
       
   461         {
       
   462         // The client has already asked for scan events as we still
       
   463         // have an existing (valid) iScanEventMessage object.
       
   464         //
       
   465         // This would imply a programming error in the client code
       
   466         // so we punish the client by panicking it.
       
   467         aMessage.Panic( KMemScanServerPanicCategory, EMemScanServerPanicRequestedScanEventsTwice );
       
   468         }
       
   469     }
       
   470 
       
   471 
       
   472 // ---------------------------------------------------------------------------
       
   473 // CMemScanServSession::RequestScanEventsCancel()
       
   474 //
       
   475 // ---------------------------------------------------------------------------
       
   476 void CMemScanServSession::RequestScanEventsCancel(const RMessage2& aMessage)
       
   477     {
       
   478     // We only are able to cancel a client request if the client actually
       
   479     // requested something.
       
   480     // We can check whether a request is pending by using the IsNull method
       
   481     // on our outstanding request object ("iScanEventMessage").
       
   482     if  ( iScanEventMessage.IsNull() == EFalse ) 
       
   483         {
       
   484         // The client has made a request, and we need to cancel it.
       
   485         iScanEventMessage.Complete( KErrCancel );
       
   486         }
       
   487         
       
   488         
       
   489     // If the client wants to cancel events, we should also empty
       
   490     // the event buffer.
       
   491     iEventBuffer.Reset();
       
   492     
       
   493     aMessage.Complete( KErrNone );
       
   494     }
       
   495 
       
   496 
       
   497 // ---------------------------------------------------------------------------
       
   498 // CMemScanServSession::ScanInProgress()
       
   499 //
       
   500 // ---------------------------------------------------------------------------
       
   501 void CMemScanServSession::ScanInProgress(const RMessage2& aMessage)
       
   502     {
       
   503     TBool scanInProgress = iMseng->ScanInProgress();
       
   504     aMessage.Complete(static_cast<TInt> (scanInProgress));
       
   505     }
       
   506 
       
   507 
       
   508 
       
   509 // From MMsengUIHandler:
       
   510 // ===========================================================================
       
   511 
       
   512 // ---------------------------------------------------------------------------
       
   513 // CMemScanServSession::StartL()
       
   514 // ---------------------------------------------------------------------------
       
   515 void CMemScanServSession::StartL()
       
   516     {
       
   517     SendEventToClientL( EMemScanEventScanningStarted );
       
   518     }
       
   519 
       
   520 // ---------------------------------------------------------------------------
       
   521 // CMemScanServSession::QuitL()
       
   522 // ---------------------------------------------------------------------------
       
   523 void CMemScanServSession::QuitL(TInt aReason)
       
   524     {
       
   525     SendEventToClientL( EMemScanEventScanningFinished, aReason );
       
   526     }
       
   527 
       
   528 // ---------------------------------------------------------------------------
       
   529 // CMemScanServSession::Error()
       
   530 // ---------------------------------------------------------------------------
       
   531 void CMemScanServSession::ErrorL(TInt aError)
       
   532     {
       
   533     SendEventToClientL( EMemScanEventScanningError, aError );
       
   534     }
       
   535 
       
   536 // ===========================================================================
       
   537 
       
   538 
       
   539 
       
   540     
       
   541 // ---------------------------------------------------------------------------
       
   542 // CMemScanServSession::SendEventToClientL()
       
   543 //
       
   544 // ---------------------------------------------------------------------------
       
   545 void CMemScanServSession::SendEventToClientL( TMemScanEvent aEventType,
       
   546                                               TInt aError )
       
   547     {
       
   548     // We need to tell the client about the event that has taken place. 
       
   549     // The client event API expects to receive the event type, i.e. what 
       
   550     // kind of "thing" just happened, and also any associated error value 
       
   551     // (e.g. "Nothing went wrong" or, "we ran out of memory").
       
   552     
       
   553     AddNewEventToBufferL( aEventType, aError );
       
   554     DeliverOldestEventToClientL();
       
   555     }
       
   556 
       
   557 // ---------------------------------------------------------------------------
       
   558 // CMemScanServSession::AddNewEventToBufferL()
       
   559 //
       
   560 // ---------------------------------------------------------------------------
       
   561 void CMemScanServSession::AddNewEventToBufferL( TMemScanEvent aEventType,
       
   562                                                 TInt aError )
       
   563     {
       
   564     TMemScanEventPackage event;
       
   565     event.iEvent = aEventType;
       
   566     event.iError = aError;
       
   567     
       
   568     // Add the event to the event buffer. We will send this event to the 
       
   569     // client when the client is ready to accept it.
       
   570     iEventBuffer.AppendL( event );
       
   571     }
       
   572 
       
   573 // ---------------------------------------------------------------------------
       
   574 // CMemScanServSession::IsEventReady()
       
   575 //
       
   576 // ---------------------------------------------------------------------------
       
   577 TBool CMemScanServSession::IsEventReady() const
       
   578     {
       
   579     // Returns whether we have at least one event in the buffer ready to send
       
   580     // to the client.
       
   581     const TInt count = iEventBuffer.Count();
       
   582     return ( count > 0 );
       
   583     }
       
   584 
       
   585 
       
   586 // ---------------------------------------------------------------------------
       
   587 // CMemScanServSession::DeliverOldestEventToClientL()
       
   588 //
       
   589 // ---------------------------------------------------------------------------
       
   590 void CMemScanServSession::DeliverOldestEventToClientL()
       
   591     {
       
   592     // Fetch the oldest event from the buffer and deliver it
       
   593     // to the client.
       
   594     if  ( iScanEventMessage.IsNull() == EFalse && IsEventReady() )
       
   595         {
       
   596         // This next block of code converts the error number to look like
       
   597         // a descriptor, since this is the only way of writing to the
       
   598         // client's address space. 
       
   599         //
       
   600         // We check that the client actually requested scan events before
       
   601         // we try and write to its address space. If we don't do this
       
   602         // then the kernel will panic our code with KERN-SVR 0
       
   603         // ("you're trying to use a null message object")
       
   604         const TMemScanEventPackage& event = iEventBuffer[ 0 ];
       
   605         
       
   606         TPckgC<TInt> associatedErrorAsDescriptor( event.iError );
       
   607         iScanEventMessage.WriteL( 0, associatedErrorAsDescriptor );
       
   608         
       
   609         // Now that we have written the error value, its safe to complete
       
   610         // the clients asynchronous request which will end up calling
       
   611         // the client's RunL method.
       
   612         iScanEventMessage.Complete( event.iEvent );
       
   613 
       
   614         // We've delivered the oldest event to the client, so now 
       
   615         // its safe to discard it.
       
   616         iEventBuffer.Remove( 0 );
       
   617         }
       
   618     }
       
   619  
       
   620 
       
   621 // End of File